/* Главная. Все отступы и кегли — макетные пиксели Frame 29 через --u.
   Вертикаль набрана по чернилам букв: для Cy верх капители равен
   верху строчного бокса + (line-height − font-size)/2 + 0.02em, для
   DM Mono метки — + 2.76 при кегле 14 и шаге 18. Поэтому отступы ниже
   не круглые: они выведены из позиций в макете, а не назначены. */

/* --- Шапка ---------------------------------------------------------------
   Набрана акцентом (лайм) и висит поверх первого экрана. Поле одно и то же
   со всех сторон — 24. */

.nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 20;
  padding: calc(24 * var(--u));
  color: var(--accent);
}
.nav__in {
  display: flex;
  align-items: center;
}
.nav__mark {
  margin-inline-end: calc(18 * var(--u));
  font-family: var(--display);
  font-size: var(--fs-nav);
  letter-spacing: -0.02em;
  line-height: 1;
}
/* Линейка упирается в первую пилюлю без зазора — как в макете. */
/* Линейка — тоже рамка, а не залитый блок высотой --line: высоту блока
   браузер округляет по положению на экране (на 125% выходило то 2, то 3
   пикселя), рамку — всегда одинаково с обводкой кнопок и сеткой. */
.nav__rule {
  flex: 1;
  border-block-start: var(--line) solid currentColor;
}
.pills {
  display: flex;
  gap: calc(5 * var(--u));
}
/* Кнопка. На ховере слева выезжает стрелка (js/main.js ставит её в каждую
   кнопку): кнопка становится шире и раздвигает соседей — это и есть
   отклик, заливки на ховере нет.

   Стрелка нарисована рамками, а не SVG: браузер округляет толщину рамок
   до целых пикселей устройства, линию SVG — нет, и на дробном масштабе
   экрана (125%, 150%) стрелка выходила толще обводки. Рамка к рамке
   совпадает при любом масштабе. */
.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: calc(38 * var(--u));
  padding-inline: calc(16 * var(--u));
  border: var(--line) solid currentColor;
  border-radius: 100px;
  font-family: var(--mono);
  font-size: var(--fs-pill);
  letter-spacing: 0.1em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  transition:
    background-color var(--dur-ui) var(--ease),
    color var(--dur-ui) var(--ease),
    border-color var(--dur-ui) var(--ease);
}
.pill__arrow {
  --head: calc(7 * var(--u));
  position: relative;
  flex: none;
  width: 0;
  height: calc(10 * var(--u));
  margin-inline-end: 0;
  opacity: 0;
  transform: translateX(calc(-6 * var(--u)));
  transition:
    width var(--dur-ui) var(--ease),
    margin var(--dur-ui) var(--ease),
    opacity var(--dur-ui) var(--ease),
    transform var(--dur-ui) var(--ease);
}
/* Стрелка сайта — одна на кнопки и ветки 02. Древко — верхняя рамка во всю
   ширину, наконечник — уголок из двух рамок, повёрнутый на 45°; --head —
   сторона уголка, размер и положение задаёт хозяин. */
.arrow::before,
.arrow::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  box-sizing: border-box;
}
.arrow::before {
  inset-inline: 0 calc(var(--head) * 0.15);
  border-block-start: var(--line) solid currentColor;
  transform: translateY(-50%);
}
/* Центр уголка стоит так, чтобы его вершина (полдиагонали от центра)
   упиралась ровно в конец древка. */
.arrow::after {
  inset-inline-end: calc(var(--head) * 0.36);
  width: var(--head);
  height: var(--head);
  border-block-start: var(--line) solid currentColor;
  border-inline-end: var(--line) solid currentColor;
  transform: translateY(-50%) rotate(45deg);
  transform-origin: 50% 50%;
}
.pill:hover .pill__arrow,
.pill:focus-visible .pill__arrow {
  width: calc(16 * var(--u));
  margin-inline-end: calc(10 * var(--u));
  opacity: 1;
  transform: none;
}
/* Пилюля-кнопка (<button>) выглядит так же, как пилюля-ссылка: без этого
   браузер красит кнопку своим светлым фоном и системным шрифтом. */
button.pill {
  background: transparent;
  color: var(--accent);
  cursor: pointer;
}
/* Кнопка без обводки в покое (All work): обводка появляется на ховере —
   та же, что у кнопок шапки. */
.pill--ghost {
  border-color: transparent;
  color: var(--accent);
}
.pill--ghost:hover,
.pill--ghost:focus-visible { border-color: currentColor; }
/* Текущий раздел залит акцентом. */
.pill.is-current {
  background: var(--accent);
  color: var(--bg);
}
/* «get in touch» на 357 не помещается рядом с двумя другими пилюлями,
   поэтому у неё две подписи и переключаются они шириной экрана. */
.pill__short { display: none; }

/* --- Первый экран --------------------------------------------------------
   Единственная осевая композиция на странице. Высота не 100vh: в макете
   имя начинается на 160, а метка 01 — на 541. Отступ до метки 01 (223)
   стоит здесь, у #about он нулевой. */

.hero {
  position: relative;
  /* Сверху +24 к экспорту: над рамкой имени-ноды — шапка и ряд входов. */
  padding-block: calc(209 * var(--u)) calc(223 * var(--u));
  text-align: center;
  isolation: isolate;
}

.hero__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-hero);
  line-height: calc(100 * var(--u));
  letter-spacing: -0.02em;
  white-space: nowrap;
  /* Подпись стоит ниже имени, чем в экспорте: строчный бокс имени короче
     кегля, поэтому без этого отступа она подлезает под выносные элементы. */
  margin-block-end: 0;
}
.hero__sub {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-sub);
  line-height: calc(30 * var(--u));
  letter-spacing: 0;
  color: var(--accent);
}

/* --- Общие секции ---------------------------------------------------------
   Каждая полоса задаёт только свой верхний отступ — от низа предыдущего
   блока до верха своей метки. Так вертикаль собирается один раз и не
   расползается от накладывающихся margin. */

.band { padding-block: var(--gap-top) var(--gap-bottom, 0); }

#about     { --gap-top: 0; }   /* 223 переехали в padding-bottom первого экрана */
#expertise { --gap-top: calc(74  * var(--u)); --gap-bottom: calc(72 * var(--u)); }
#work      { --gap-top: calc(98  * var(--u)); }
#clients   { --gap-top: calc(129 * var(--u)); }
#talk      { --gap-top: calc(121 * var(--u)); --gap-bottom: calc(49 * var(--u)); }
#pipeline  { --gap-top: calc(120 * var(--u)); }

/* Переносы строк в дисплейных блоках расставлены руками под 1200. */
@media (max-width: 760px) { .display br { display: none; } }

/* --- 01 / About -----------------------------------------------------------
   В макете это дисплейный набор Cy 48/43, а не моноширинный абзац. */

.lead {
  margin-block-start: calc(33 * var(--u));
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: var(--track);
}

/* --- 02 / Expertise --------------------------------------------------------
   Три пункта стоят на общей сетке 3×304 — тех же колонках, что и проекты
   ниже: 144, 448, 752. Стрелка появляется только на ховере и вынесена из
   потока (position: absolute), поэтому в покое пункты стоят флашем по
   колонке, а на ховере уезжает вправо сам текст — место под стрелку не
   зарезервировано и композиция в покое не расшатана. */

.branches {
  margin-block-start: calc(35 * var(--u));
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: var(--track);
}
/* Ячейка сжата до слова: каждая ветка наклоняется своей плоскостью
   вокруг центра слова, а не пустой части колонки (js/tilt.js). */
.branch { position: relative; justify-self: start; }
.branch a {
  display: inline-block;
  transition:
    transform var(--dur-ui) var(--ease),
    color var(--dur-ui) var(--ease);
}
/* Сдвиг равен ширине стрелки плюс зазор — текст встаёт ровно туда, где он
   стоял бы, будь стрелка в потоке. */
.branch:hover a,
.branch:focus-within a {
  transform: translateX(calc(39 * var(--u)));
  color: var(--accent);
}
/* Стрелка — та же, что в кнопках (.arrow), только крупнее. Раньше здесь
   был SVG, и на масштабе экрана 125% его линия выходила толще рамок:
   браузер округляет рамку до целых пикселей устройства, а stroke — нет. */
.branch__arrow {
  --head: calc(10 * var(--u));
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 50%;
  width: calc(29 * var(--u));
  height: calc(18 * var(--u));
  color: var(--accent);
  opacity: 0;
  transform: translateY(-50%) translateX(calc(-10 * var(--u)));
  transition:
    opacity var(--dur-ui) var(--ease),
    transform var(--dur-ui) var(--ease);
}
.branch:hover .branch__arrow,
.branch:focus-within .branch__arrow {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

/* --- 03 / Projects ---------------------------------------------------------
   Сетка 3×304 с волосяными линиями в 1px. Линейки рисуются правым и
   нижним краем ячейки, а верхний и левый край — у контейнера: иначе на
   стыках получалась бы двойная толщина. */

.work {
  margin-block-start: calc(21 * var(--u));
  display: grid;
  grid-template-columns: repeat(3, var(--col));
  grid-auto-rows: var(--col);
  border-block-start: var(--line) solid var(--hair);
  border-inline-start: var(--line) solid var(--hair);
  width: calc(var(--col) * 3 + var(--line));
}
.card {
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding: calc(51 * var(--u)) calc(24 * var(--u)) calc(16 * var(--u));
  border-inline-end: var(--line) solid var(--hair);
  border-block-end: var(--line) solid var(--hair);
  background: var(--bg);
  overflow: hidden;
}
/* Ссылка на кейс — прозрачный слой поверх всей ячейки. */
.card__link {
  position: absolute;
  inset: 0;
  z-index: 2;
}
.card__link:focus-visible {
  outline: var(--line) solid var(--accent);
  outline-offset: calc(-4 * var(--u));
}
.card__cap { z-index: 1; }

/* Карточка с цветом бренда: знак по центру ячейки в четверть её ширины.
   Центрируется полями, а не translate — transform у знака занят
   наклоном за курсором (js/tilt.js). */
.card__logo {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 25%;
  height: 25%;
  margin: auto;
  background: var(--fg);
  -webkit-mask: var(--src) center / contain no-repeat;
  mask: var(--src) center / contain no-repeat;
  pointer-events: none;
}
/* Чернила (js/spot.js) — один прозрачный WebGL-холст на всю сетку. Лежит
   первым в сетке, поэтому рамки ячеек, знаки и подписи рисуются поверх.
   Фон у ячеек снимается: иначе он закрыл бы холст. */
.work {
  position: relative;
  isolation: isolate;
}
.work__ink {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
.work.has-ink .card { background: transparent; }
/* Чернила лежат под знаком и подписью и их не затемняют: набор остаётся
   своего цвета поверх любого цвета ячейки. */
/* Подпись — одна строка. Заголовок и вторая строка лежат в одном месте:
   на ховере заголовок уезжает на строку вверх, а вторая строка
   проявляется снизу на его месте. Высота подписи — одна строка, поэтому
   сетка от ховера не двигается. */
.card__cap {
  position: relative;
  margin-block-start: auto;
  height: calc(18 * var(--u));
  font-family: var(--mono);
  font-size: var(--fs-caption);
  line-height: calc(18 * var(--u));
}
.card__title,
.card__kicker {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition:
    transform var(--dur-ui) var(--ease),
    opacity var(--dur-ui) var(--ease);
}
.card__title { overflow: visible; text-overflow: clip; }
.card__title sup {
  font-size: 0.65em;
  vertical-align: super;
  line-height: 0;
  margin-inline-start: calc(3 * var(--u));
  color: color-mix(in srgb, var(--fg) 45%, var(--bg));
}
.card__kicker {
  color: color-mix(in srgb, var(--fg) 60%, var(--bg));
  opacity: 0;
  transform: translateY(100%);
}
.card:hover .card__title,
.card:focus-within .card__title { transform: translateY(-100%); }
.card:hover .card__kicker,
.card:focus-within .card__kicker {
  opacity: 1;
  transform: none;
}

/* Ссылка на полный список. Стоит под сеткой в её левом поле — там же,
   где начинаются метки секций. */
.work__more {
  margin-block-start: calc(24 * var(--u));
  font-family: var(--mono);
  font-size: var(--fs-caption);
  line-height: calc(18 * var(--u));
  color: color-mix(in srgb, var(--fg) 62%, var(--bg));
}
/* --- 04 / Happy clients ----------------------------------------------------
   Лента бесконечная и обрезается краями экрана — выходит из оболочки
   на всю ширину. Дорожка продублирована, сдвиг замыкает цикл без шва. */

.ticker {
  margin-block-start: calc(86 * var(--u));
  display: grid;
  gap: calc(57 * var(--u));
}
.ticker__row {
  overflow: hidden;
  height: calc(112 * var(--u));
  /* Знаки не обрезаются краем экрана, а растворяются к нему. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 32%, #000 68%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 32%, #000 68%, transparent);
}
/* Движение управляется скроллом (js/main.js), а не keyframes. */
.ticker__track {
  display: flex;
  align-items: center;
  height: 100%;
  width: max-content;
  gap: calc(96 * var(--u));
  padding-inline-end: calc(96 * var(--u));
  transform: translateX(0);
  will-change: transform;
}
@keyframes ticker { to { transform: translateX(-50%); } }

/* Логотипы одноцветные: mask + background-color. Высоты сняты с макета
   поштучно — знаки выровнены на глаз по оптике, а не по одной высоте. */
/* Логотипы клиентов одного приглушённого тона (#B5B3C8) и проявляются
   вторым акцентом под пятном курсора: --p (0..1) считает js/logo-spot.js
   по близости к указателю, сглаживание тоже там — без transition. */
.logo {
  flex: none;
  height: var(--h, calc(48 * var(--u)));
  aspect-ratio: var(--ar, 4);
  background: color-mix(in srgb, var(--accent-2) calc(var(--p, 0) * 100%), #B5B3C8);
  -webkit-mask: var(--src) center / contain no-repeat;
  mask: var(--src) center / contain no-repeat;
}

.mark-gazprombank { --src: url("../assets/logos/gazprombank.svg"); --ar: 4.74; --h: calc(52 * var(--u)); }
.mark-sber        { --src: url("../assets/logos/sber.svg");        --ar: 6.04; --h: calc(30 * var(--u)); }
.mark-mts         { --src: url("../assets/logos/mts.svg");         --ar: 1;    --h: calc(112 * var(--u)); }
.mark-honor       { --src: url("../assets/logos/honor.svg");       --ar: 5.2;  --h: calc(34 * var(--u)); }
.mark-avito       { --src: url("../assets/logos/avito.svg");       --ar: 3.93; --h: calc(34 * var(--u)); }
.mark-vk          { --src: url("../assets/logos/vk.svg");          --ar: 1;    --h: calc(106 * var(--u)); }
.mark-zen         { --src: url("../assets/logos/zen.svg");         --ar: 3.24; --h: calc(54 * var(--u)); }
.mark-2x2         { --src: url("../assets/logos/2x2.svg");         --ar: 2.33; --h: calc(74 * var(--u)); }
.mark-yandex      { --src: url("../assets/logos/yandex.svg");      --ar: 3.46; --h: calc(45 * var(--u)); }

/* --- 05 / Lets talk ---------------------------------------------------------
   Миллиметровка 912×494 — ровно 24×13 клеток по 38. Метка стоит вплотную
   над полем, зазор в макете 1px. */

.graph {
  background-image:
    repeating-linear-gradient(to right, var(--graph) 0 1px, transparent 1px var(--graph-cell)),
    repeating-linear-gradient(to bottom, var(--graph) 0 1px, transparent 1px var(--graph-cell));
  background-position: 0 0;
}
.talk {
  margin-block-start: calc(72 * var(--u));
  display: grid;
  place-items: center;
  align-content: center;
  gap: calc(32 * var(--u));
  color: inherit;
  height: calc(494 * var(--u));
  padding-block-start: calc(12 * var(--u));
  /* Поле — ровно 24×13 клеток по 38, поэтому градиент рисует линии только
     по левому и верхнему краю. Замыкающие правую и нижнюю добавляет тень:
     она не входит в поток и не смещает содержимое. */
  box-shadow: inset -1px -1px 0 var(--graph);
  text-align: center;
}
/* Заливки по скроллу здесь нет: вопрос набран сразу в полный цвет.
   Два акцента — два ключевых слова. */
.talk em { font-style: normal; color: var(--accent); }
.talk em.alt { color: var(--accent-2); }
/* Весь блок — кнопка «написать»: пилюля под вопросом отзывается на
   наведение по всему блоку, а не только по себе. */
.talk .display { display: block; }
.talk__cta { color: var(--accent); }
.talk:hover .talk__cta,
.talk:focus-visible .talk__cta {
  background: var(--accent);
  color: var(--bg);
}
.talk:hover .pill__arrow,
.talk:focus-visible .pill__arrow {
  width: calc(16 * var(--u));
  margin-inline-end: calc(10 * var(--u));
  opacity: 1;
  transform: none;
}

/* --- 05 / Обо мне -----------------------------------------------------------
   Сетка 3×304, как у проектов: фото в первой колонке, текст во второй и
   третьей. Только имя, абзац и кнопки контактов — без подписей и рамок.
   Фото чёрно-белое, в круге с пунктирной орбитой; клик меняет кадр
   цифровым переходом (js/avatar.js). */

.me {
  margin-block-start: calc(40 * var(--u));
  display: grid;
  grid-template-columns: var(--col) 1fr;
  gap: calc(40 * var(--u));
  align-items: end;
}
.me__photo {
  position: relative;
  margin: calc(18 * var(--u));
  cursor: pointer;
}
/* Кадр — круг. Снаружи пунктирная орбита, по ней ползёт точка акцента. */
.me__photo img,
.me__fx {
  border-radius: 50%;
}
.me__photo::before {
  content: "";
  position: absolute;
  inset: calc(-14 * var(--u));
  border: var(--line) dashed var(--hair);
  border-radius: 50%;
  animation: me-orbit 60s linear infinite;
  pointer-events: none;
}
.me__photo::after {
  content: "";
  position: absolute;
  inset: calc(-14 * var(--u));
  border-radius: 50%;
  background: radial-gradient(circle at 50% 0, var(--accent) 0 calc(3 * var(--u)), transparent calc(3.5 * var(--u)));
  animation: me-orbit 14s linear infinite;
  pointer-events: none;
}
@keyframes me-orbit { to { rotate: 1turn; } }
/* Холст цифрового перехода (js/avatar.js) — поверх фото, только на время
   перехода. */
.me__fx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
}
.me__photo.is-fx .me__fx { opacity: 1; }
/* На время перехода фото под холстом прячется: между линиями — фон. */
.me__photo.is-fx img { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .me__photo::before, .me__photo::after { animation: none; }
}
.me__count {
  position: absolute;
  inset-block-end: calc(-6 * var(--u));
  inset-inline-start: 50%;
  translate: -50% 0;
  padding: calc(2 * var(--u)) calc(8 * var(--u));
  border-radius: 100px;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  font-family: var(--mono);
  font-size: calc(10 * var(--u));
  line-height: calc(14 * var(--u));
  letter-spacing: 0.1em;
  color: var(--fg);
  pointer-events: none;
}
.me__photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}
.me__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: var(--track);
}
.me__bio {
  max-width: calc(var(--col) * 1.9);
  margin-block-start: calc(20 * var(--u));
  font-family: var(--mono);
  font-size: var(--fs-caption);
  line-height: calc(22 * var(--u));
  color: color-mix(in srgb, var(--fg) 62%, var(--bg));
}
.me__socials { margin-block-start: calc(32 * var(--u)); }

/* --- Кнопки-иконки контактов ---------------------------------------------------
   Круг той же обводки, что пилюли, иконка внутри той же линией. На ховере
   заливается акцентом, как текущая пилюля. */

.socials {
  display: flex;
  flex-wrap: wrap;
  gap: calc(8 * var(--u));
}
.social {
  display: grid;
  place-items: center;
  width: calc(44 * var(--u));
  height: calc(44 * var(--u));
  min-width: 36px;
  min-height: 36px;
  padding: 0;
  border: var(--line) solid currentColor;
  border-radius: 50%;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  transition:
    background-color var(--dur-ui) var(--ease),
    color var(--dur-ui) var(--ease);
}
.social:hover,
.social:focus-visible {
  background: var(--accent);
  color: var(--bg);
}
.social__icon {
  width: 46%;
  height: 46%;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--line-px);
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}
.social__icon * { vector-effect: non-scaling-stroke; }

/* --- Окно «Get in touch» (js/contact.js) --------------------------------------
   Компактная панель: ширина — две колонки сетки, высота — по содержимому.
   Чистый фон, одна рамка. Появляется снизу вверх, подложка затемняется. */

.contact {
  width: min(calc(var(--col) * 2), calc(100vw - 32px));
  max-width: none;
  max-height: calc(100dvh - 32px);
  padding: calc(24 * var(--u)) calc(28 * var(--u)) calc(28 * var(--u));
  border: var(--line) solid var(--hair);
  border-radius: calc(20 * var(--u));
  background: var(--bg);
  color: var(--fg);
  overflow: auto;
}
.contact::backdrop {
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(6px);
}
.contact[open] { animation: contact-in var(--dur) var(--ease-out); }
.contact[open]::backdrop { animation: contact-fade var(--dur) var(--ease-out); }
@keyframes contact-in { from { opacity: 0; translate: 0 calc(24 * var(--u)); } }
@keyframes contact-fade { from { opacity: 0; } }

.contact__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.contact__close {
  width: calc(36 * var(--u));
  height: calc(36 * var(--u));
  color: var(--fg);
  border-color: var(--hair);
}
.contact__title {
  margin-block-start: calc(28 * var(--u));
  font-family: var(--display);
  font-weight: 700;
  font-size: calc(32 * var(--u));
  line-height: calc(32 * var(--u));
  letter-spacing: var(--track);
}
.contact__note {
  margin-block-start: calc(12 * var(--u));
  font-family: var(--mono);
  font-size: var(--fs-caption);
  line-height: calc(22 * var(--u));
  color: color-mix(in srgb, var(--fg) 62%, var(--bg));
}
.contact__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(8 * var(--u));
  margin-block-start: calc(28 * var(--u));
}
.contact__mail {
  height: calc(44 * var(--u));
  min-height: 36px;
  color: var(--bg);
  background: var(--accent);
  border-color: var(--accent);
}
.contact__status {
  margin-block-start: calc(24 * var(--u));
  font-family: var(--mono);
  font-size: var(--fs-label);
  line-height: calc(18 * var(--u));
  color: color-mix(in srgb, var(--fg) 45%, var(--bg));
}
@media (prefers-reduced-motion: reduce) {
  .contact[open], .contact[open]::backdrop { animation: none; }
}

/* --- Лента слов -------------------------------------------------------------
   Две строки навстречу, вторая со сдвигом фазы — в макете слова
   не стоят друг под другом. Кегль 96, шаг строк 119. */

.marquee {
  padding-block-start: calc(58 * var(--u));
  overflow: hidden;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-big);
  line-height: calc(119 * var(--u));
  letter-spacing: var(--track);
  white-space: nowrap;
}
.marquee__row { overflow: hidden; }
/* Четыре копии подряд: одна копия шире экрана на любой ширине (кегль
   тянется тем же --u), поэтому ни шва, ни дырки в конце цикла. Движение
   ведёт js/main.js вместе с лентой логотипов — и разворот по скроллу
   у них общий. */
.marquee__track {
  display: inline-flex;
  will-change: transform;
}
/* Слова и пиктограммы белые, подсвечивается то, что под курсором.
   «creative.» набран акцентом, и акценты чередуются шахматкой: по строке
   лайм и фиолет идут через копию, нижняя строка начинает со второго.
   Лента оборачивается на половине дорожки — это ровно две копии, поэтому
   чередование замыкается без шва. */
.marquee b,
.picto { transition: color var(--dur-ui) var(--ease); }
.marquee b { font-weight: inherit; }
.marquee b:hover,
.picto:hover { color: var(--accent); }
.marquee b[data-tone="1"] { color: var(--accent); }
.marquee b[data-tone="2"] { color: var(--accent-2); }

/* Пиктограмма стоит между словами на средней линии строчных, с полями
   в ширину пробела. */
/* Толщина линии — общая для сайта (--line-px), а не доля знака: иначе она
   тянулась вместе с кеглем и на 1200 выходила вдвое тоньше рамок.
   non-scaling-stroke считает её в пикселях экрана, а не в единицах
   viewBox; свойство не наследуется, поэтому стоит на самих фигурах. */
.picto {
  flex: none;
  width: 0.82em;
  height: 0.82em;
  margin-inline: 0.32em;
  vertical-align: -0.06em;
  overflow: visible;
  stroke-width: var(--line-px);
}
.picto-sprite g > * { vector-effect: non-scaling-stroke; }
.picto-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* --- Lab: тизер на главной --------------------------------------------------
   Ведёт на lab.html. Скобки — отдельные элементы: на hover расходятся, слово остаётся на месте.
   Зазор зарезервирован сразу, иначе расхождение двигало бы соседние блоки. */

.lab-teaser {
  padding-block: calc(137 * var(--u)) calc(129 * var(--u));
  text-align: center;
}
.lab__link {
  display: inline-flex;
  align-items: baseline;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-big);
  line-height: calc(96 * var(--u));
  letter-spacing: var(--track);
  color: var(--accent);
}
.lab__bracket { transition: transform var(--dur-ui) var(--ease); }
.lab__bracket--open { transform-origin: right center; }
.lab__bracket--close { transform-origin: left center; }
.lab__link:hover .lab__bracket--open { transform: translateX(-0.2em); }
.lab__link:hover .lab__bracket--close { transform: translateX(0.2em); }
.lab__word { padding-inline: 0.11em; }

/* --- Футер ---------------------------------------------------------------------
   Светлая плита со скруглением 24 сверху. Внутри та же сетка 3×304, что
   на странице: в первой колонке роза векторов в угловых метках, во
   второй и третьей — строка,
   контакты и разделы; внизу моно-строка с координатами курсора.
   Круги здесь цвета текста, стрелки — второй акцент: лайм на светлой
   плите не читается. */

.footer {
  --vg-line: color-mix(in srgb, var(--bg) 70%, var(--fg));
  --vg-dot: var(--accent-2);
  --hair: color-mix(in srgb, var(--bg) 22%, var(--fg));
  border-start-start-radius: calc(24 * var(--u));
  border-start-end-radius: calc(24 * var(--u));
  padding-block: calc(96 * var(--u)) calc(40 * var(--u));
}

.footer__grid {
  display: grid;
  grid-template-columns: var(--col) 1fr;
  gap: calc(40 * var(--u));
  align-items: center;
}
.footer__object {
  aspect-ratio: 1;
  padding: calc(18 * var(--u));
}
.footer .vg-corners i {
  border-color: var(--bg);
  opacity: 1;
}
.footer__compass { width: 100%; height: 100%; }

.footer__line {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: var(--track);
}
.footer__socials { margin-block-start: calc(32 * var(--u)); }
.footer .social { color: var(--bg); }
.footer .social:hover,
.footer .social:focus-visible {
  background: var(--bg);
  color: var(--fg);
}
.footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: calc(5 * var(--u));
  margin-block-start: calc(12 * var(--u));
  color: var(--bg);
}

.footer__base {
  display: grid;
  grid-template-columns: repeat(4, auto);
  justify-content: space-between;
  align-items: center;
  gap: calc(16 * var(--u));
  margin-block-start: calc(64 * var(--u));
  padding-block-start: calc(16 * var(--u));
  border-block-start: var(--line) solid var(--hair);
  font-family: var(--mono);
  font-size: var(--fs-label);
  line-height: calc(18 * var(--u));
  color: color-mix(in srgb, var(--bg) 60%, var(--fg));
}
.footer__coords {
  font-variant-numeric: tabular-nums;
  color: var(--accent-2);
}
/* «Наверх» — стрелка сайта (.arrow), повёрнутая вверх. */
.footer__top {
  display: inline-flex;
  align-items: center;
  gap: calc(10 * var(--u));
  color: var(--bg);
  transition: opacity var(--dur-ui) var(--ease);
}
.footer__up {
  --head: calc(7 * var(--u));
  position: relative;
  width: calc(16 * var(--u));
  height: calc(10 * var(--u));
  rotate: -90deg;
  transition: translate var(--dur-ui) var(--ease);
}
.footer__top:hover .footer__up { translate: 0 calc(-4 * var(--u)); }

/* --- Мобильная раскладка ---------------------------------------------------
   Не сжатый десктоп: --u уже ужимает композицию пропорционально, но ниже
   760 сетки схлопываются, иначе текст уходит в нечитаемый кегль. */

@media (max-width: 760px) {
  :root { --u: calc(100vw / 357); }

  /* Дисплейный кегль на телефоне отдельный, а не пересчитанный.
     --u уже ужимает композицию пропорционально, но 48 макетных единиц на
     ширине 357 — это те же 13% экрана, и строка из тринадцати знаков
     уходит за край. Мобильная версия — отдельное решение, а не сжатый
     десктоп (website.md), поэтому здесь свой набор кеглей. */
  :root {
    --fs-display: calc(24 * var(--u));
    --lh-display: calc(27 * var(--u));
    --fs-big:     calc(52 * var(--u));
    --fs-footer:  calc(18 * var(--u));
    --fs-note:    calc(13 * var(--u));
    --fs-nav:     calc(20 * var(--u));
  }

  .nav { padding-block-start: calc(24 * var(--u)); padding-inline: calc(20 * var(--u)); }
  .nav__rule { display: none; }
  .nav__mark { margin-inline-end: auto; }
  .pills { gap: calc(4 * var(--u)); }
  .pill { padding-inline: calc(12 * var(--u)); }
  .pill__long { display: none; }
  .pill__short { display: inline; }

  /* Снизу место под выход графа, шоурил рядом с ним и ряд WORK · ABOUT
     под ними (js/hero-nodes.js). */
  .hero { padding-block: calc(240 * var(--u)) calc(310 * var(--u)); }
  /* Имя набирается в одну строку и на телефоне: холст первого экрана
     рисует строку целиком через fillText и переносов не знает. Поэтому
     кегль подбирается под ширину, а не наоборот — 46 единиц дают около
     80% колонки на любой ширине ниже 760. */
  .hero__name { font-size: calc(46 * var(--u)); line-height: calc(52 * var(--u)); }
  .hero__sub { font-size: calc(13 * var(--u)); line-height: calc(20 * var(--u)); }

  .branches { grid-template-columns: 1fr; gap: calc(8 * var(--u)); }
  /* Орбиту крутит rotate на квадратном блоке: углы повёрнутого квадрата
     выходят за экран и раздувают страницу вбок. Сам круг внутри .me. */
  .me { grid-template-columns: 1fr; gap: calc(24 * var(--u)); overflow: clip; }
  .footer__grid { grid-template-columns: 1fr; }
  .footer__object { width: 60%; justify-self: center; }
  .footer__base { grid-template-columns: 1fr 1fr; }
  .footer__coords { display: none; }
  .me__bio { max-width: none; }
  .contact { padding: calc(20 * var(--u)); }
  .contact__title { font-size: calc(24 * var(--u)); line-height: calc(26 * var(--u)); }
  .work { grid-template-columns: repeat(2, 1fr); grid-auto-rows: auto; width: 100%; }
  .card { aspect-ratio: 1; padding-inline: calc(14 * var(--u)); }
  /* Ховера на телефоне нет — вторая строка не нужна, а длинное название
     переносится, а не режется. */
  .card__cap {
    height: auto;
    font-size: calc(12 * var(--u));
    line-height: calc(16 * var(--u));
  }
  .card__title,
  .card:hover .card__title,
  .card:focus-within .card__title { position: static; white-space: normal; transform: none; }
  .card__kicker { display: none; }

  /* Лента клиентов вполовину: иначе на экран влезает один знак. Все
     размеры ленты — от --u, поэтому достаточно ужать его локально. */
  .ticker { --u: calc(100vw / 357 * 0.5); margin-block-start: calc(60 * 100vw / 357); }
}

/* --- Переключатель сетки ----------------------------------------------------
   QA-инструмент: колонки 912/144/12 поверх страницы, чтобы сверять вёрстку
   с Figma. По умолчанию скрыт, состояние держит js/main.js классом
   .show-grid на <html>. */

.grid-overlay {
  position: fixed;
  inset: 0;
  z-index: 15;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms var(--ease-out);
}
html.show-grid .grid-overlay { opacity: 1; }
/* Колоночная сетка: 12 колонок линиями, поля 144 (ширина .wrap — 912). */
.grid-overlay__wrap {
  height: 100%;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  border-inline-end: 1px solid var(--grid-line);
}
.grid-overlay__col { border-inline-start: 1px solid var(--grid-line); }

/* Панель разработчика: переключатели сетки и кривых стоят в один ряд.
   Видна только в режиме разработчика (html.is-dev, js/main.js) —
   включается из админки. */
.devbar { display: none !important; }
html.is-dev .devbar { display: flex !important; }
.devbar {
  position: fixed;
  inset-block-end: 1.25rem;
  inset-inline-end: 1.25rem;
  z-index: 30;
  display: flex;
  gap: 0.5rem;
}

.grid-switch {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.7rem 0.4rem 0.5rem;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--hair);
  border-radius: 100px;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}
.grid-switch__track {
  position: relative;
  width: 1.9rem;
  height: 1.1rem;
  border-radius: 100px;
  background: var(--hair);
  transition: background-color 200ms var(--ease-out);
}
.grid-switch__knob {
  position: absolute;
  inset-block-start: 2px;
  inset-inline-start: 2px;
  width: calc(1.1rem - 4px);
  height: calc(1.1rem - 4px);
  border-radius: 50%;
  background: var(--fg);
  transition: transform 200ms var(--ease-out);
}
.grid-switch[aria-pressed="true"] .grid-switch__track { background: var(--accent); }
.grid-switch[aria-pressed="true"] .grid-switch__knob {
  transform: translateX(calc(1.9rem - 1.1rem));
  background: var(--ink);
}

@media (max-width: 760px) {
  .grid-switch__label { display: none; }
  .grid-switch { padding: 0.4rem; }
}


/* --- Панель кривых -----------------------------------------------------------
   Настройка моторики вживую: кривая и длительность пишутся в --ease,
   --dur-ui и --dur на <html>, поэтому меняется вся интерактивная анимация
   разом. Значения держатся в localStorage — это настройка разработчика,
   а не состояние страницы, поэтому в разметку они не попадают. */

.ease-switch {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.7rem;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--hair);
  border-radius: 100px;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}
.ease-switch[aria-expanded="true"] { background: var(--accent); color: var(--ink); }

.easepanel {
  position: fixed;
  inset-block-end: 4.25rem;
  inset-inline-end: 1.25rem;
  z-index: 31;
  width: 19rem;
  padding: 1rem;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--hair);
  border-radius: 14px;
  font-family: var(--mono);
  font-size: 0.6875rem;
  line-height: 1.5;
}
.easepanel[hidden] { display: none; }
.easepanel__title {
  margin-block-end: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.6;
}
.easepanel__presets {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-block-end: 0.75rem;
}
.easepanel__preset {
  padding: 0.25rem 0.5rem;
  background: none;
  color: inherit;
  border: 1px solid var(--hair);
  border-radius: 100px;
  font: inherit;
  cursor: pointer;
}
.easepanel__preset[aria-pressed="true"] { background: var(--accent); color: var(--ink); border-color: var(--accent); }

/* Кривая и дорожка предпросмотра: точка проезжает по той же --ease,
   что уходит в страницу, — видно характер, а не только цифры. */
.easepanel__stage { display: flex; gap: 0.75rem; align-items: stretch; margin-block-end: 0.75rem; }
.easepanel__curve { flex: none; width: 92px; height: 92px; border: 1px solid var(--hair); border-radius: 6px; }
.easepanel__curve path { fill: none; stroke: var(--accent); stroke-width: 2; }
.easepanel__curve line { stroke: var(--hair); stroke-width: 1; }
.easepanel__demo { flex: 1; position: relative; border: 1px solid var(--hair); border-radius: 6px; overflow: hidden; }
.easepanel__dot {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 6px;
  width: 14px;
  height: 14px;
  margin-block-start: -7px;
  border-radius: 50%;
  background: var(--accent);
}
.easepanel__dot.is-away { transform: translateX(calc(100% + 0px)); }

.easepanel__row { display: grid; grid-template-columns: 2.6rem 1fr 2.6rem; align-items: center; gap: 0.4rem; }
.easepanel__row input[type="range"] { width: 100%; accent-color: var(--accent); }
.easepanel__row output { text-align: end; opacity: 0.7; }
.easepanel__code {
  margin-block-start: 0.75rem;
  padding: 0.4rem 0.5rem;
  background: color-mix(in srgb, var(--fg) 7%, transparent);
  border-radius: 6px;
  word-break: break-all;
  cursor: pointer;
}

/* Панель наклона (js/tilt-panel.js) — тот же материал, стоит левее панели
   кривых, чтобы обе можно было держать открытыми. */
.tiltpanel { inset-inline-end: 21.5rem; width: 20rem; }
.tiltpanel .easepanel__row { grid-template-columns: 6.4rem 1fr 2.8rem; }
.tiltpanel__head { display: flex; gap: 0.4rem; margin-block-end: 0.6rem; }
.tiltpanel select {
  flex: 1;
  min-width: 0;
  padding: 0.25rem 0.4rem;
  background: var(--bg);
  color: inherit;
  border: 1px solid var(--hair);
  border-radius: 6px;
  font: inherit;
}
/* Панель чернил (js/ink-panel.js) — ещё левее, чтобы все три можно было
   держать открытыми. */
.inkpanel { inset-inline-end: 42.75rem; }
.inkpanel .easepanel__row { grid-template-columns: 8.2rem 1fr 2.8rem; }

.is-tilt-picked { outline: 1px dashed var(--accent); outline-offset: 4px; }
html.is-tilt-picking,
html.is-tilt-picking * { cursor: crosshair !important; }

@media (max-width: 760px) { .devbar, .easepanel { display: none; } }

/* =============================================================================
   Блоки, добавленные к макету
   =============================================================================
   Композиция Frame 29 не меняется: секции стоят на тех же местах, с теми же
   метками и тем же кеглем. Добавлено то, чего в макете не было, а на странице
   не хватало, — вторая строка у веток, цифры у About, контакт под вопросом
   и техническая графика. Ни один новый кегль не заведён: всё набрано
   существующими --fs-*.
   ============================================================================= */

/* --- 01 / About: абзац ------------------------------------------------------ */

.about {
  max-width: calc(var(--col) * 2 + var(--u) * 24);
}
/* Подход одним абзацем — тот же моно, что подзаголовки разделов. */
.about__note {
  max-width: calc(var(--col) * 2);
  margin-block-start: calc(28 * var(--u));
  font-family: var(--mono);
  font-size: var(--fs-caption);
  line-height: calc(22 * var(--u));
  color: color-mix(in srgb, var(--fg) 62%, var(--bg));
}

/* --- 04 / Clients: строка контекста ----------------------------------------- */

.clients__note {
  margin-block-start: calc(40 * var(--u));
  max-width: calc(var(--col) * 2);
  font-family: var(--mono);
  font-size: var(--fs-caption);
  line-height: calc(20 * var(--u));
  color: color-mix(in srgb, var(--fg) 55%, var(--bg));
}

/* --- Lab: тизер с перспективной сеткой -------------------------------------- */

.lab-teaser__note {
  margin-block-start: calc(18 * var(--u));
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--fg) 45%, var(--bg));
}

/* --- Мобильная раскладка добавленных блоков --------------------------------- */

@media (max-width: 760px) {
  .about { max-width: none; }
}

/* --- Курсор-кольцо (js/cursor.js) ------------------------------------------------
   Системный курсор прячется только когда кольцо есть (мышь, скрипт
   загрузился); в полях ввода остаётся текстовый. */

html.has-cursor,
html.has-cursor * { cursor: none !important; }
html.has-cursor :is(input, textarea, select, [contenteditable="true"]) { cursor: text !important; }

.cursor {
  position: fixed;
  inset: 0 auto auto 0; /* сброс стилей popover: он по умолчанию по центру */
  padding: 0;
  overflow: visible;
  background: transparent;
  color: inherit;
  z-index: 100;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border: 0;
  mix-blend-mode: difference;
  pointer-events: none;
  opacity: 0;
  transition:
    opacity 200ms var(--ease-out),
    scale 380ms var(--ease);
}
.cursor svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  /* Крутится всегда, но стоит на паузе: сплошной круг вращения не выдаёт,
     а с паузы пунктир трогается с того же места, без рывка. */
  animation: cursor-spin 7s linear infinite paused;
}
.cursor circle {
  fill: none;
  stroke: #fff;
  stroke-width: 1.5;
  /* Штрих 8 без зазора — сплошная линия; на ховере зазор раскрывается. */
  stroke-dasharray: 8 0;
  transition:
    stroke-dasharray 380ms var(--ease),
    stroke-width 380ms var(--ease);
}
@keyframes cursor-spin { to { rotate: 1turn; } }

.cursor.is-shown { opacity: 1; }
/* Ховер: кольцо вырастает, линия рассыпается в пунктир и крутится по
   часовой. Толщина делится на масштаб, чтобы на экране остались те же 1.5px. */
.cursor:is(.is-hot, .is-drag) { scale: 2; }
.cursor:is(.is-hot, .is-drag) svg { animation-play-state: running; }
.cursor:is(.is-hot, .is-drag) circle { stroke-dasharray: 4.5 3.5; stroke-width: 0.75; }
.cursor.is-down { scale: 0.7; }
.cursor:is(.is-hot, .is-drag).is-down { scale: 1.6; }
.cursor.is-text { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .cursor svg { animation: none; }
}

@media (hover: none), (pointer: coarse) { .cursor { display: none; } }

/* --- Окно шоурила --------------------------------------------------------------
   Общее для /work (кнопка в ветке Motion) и главной (нода SHOWREEL на
   первом экране). Шоурил вертикальный, поэтому окно — по высоте экрана. */

.reel-box {
  padding: 0;
  border: 0;
  background: transparent;
  max-width: none;
  max-height: none;
  overflow: visible;
}
.reel-box::backdrop { background: color-mix(in srgb, var(--bg) 92%, transparent); }
.reel-box__video {
  display: block;
  height: 88vh;
  max-width: 92vw;
  background: var(--bg);
}
.reel-box__close {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: calc(100% + 12px);
  color: var(--accent);
  background: var(--bg);
}
@media (max-width: 760px) {
  .reel-box__video { height: auto; width: 92vw; }
  .reel-box__close { inset-inline-start: auto; inset-inline-end: 0; inset-block-start: calc(100% + 12px); }
}
/* --- Точечный паттерн главной (js/dots.js) ----------------------------------------
   Та же сетка точек, что под нодами, на весь документ: почти не видна, а в
   зоне курсора проявляется. Два слоя: ровный фон и тот же узор ярче,
   открытый круглой маской вокруг курсора (--mx, --my — координаты
   страницы, пишет скрипт). Слой прокручивается вместе со страницей. */

body:has(> .dots) { position: relative; }
.dots {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  --dot: calc(19 * var(--u));
  background-image: radial-gradient(
    circle,
    color-mix(in srgb, var(--fg) 7%, transparent) 0 calc(1 * var(--u)),
    transparent calc(1.2 * var(--u))
  );
  background-size: var(--dot) var(--dot);
}
.dots::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(
    circle,
    color-mix(in srgb, var(--fg) 38%, transparent) 0 calc(1.1 * var(--u)),
    transparent calc(1.3 * var(--u))
  );
  background-size: var(--dot) var(--dot);
  -webkit-mask: radial-gradient(circle 240px at var(--mx, -999px) var(--my, -999px), #000, transparent);
  mask: radial-gradient(circle 240px at var(--mx, -999px) var(--my, -999px), #000, transparent);
  opacity: var(--on, 0);
  transition: opacity 400ms var(--ease-out);
}
/* Блоки с data-dots-off — без точек: маска-градиент по вертикали, границы
   (координаты на странице) пишет js/dots.js. */
.dots.is-cut {
  -webkit-mask: linear-gradient(#000 var(--cut-a0), transparent var(--cut-a), transparent var(--cut-b), #000 var(--cut-b1));
  mask: linear-gradient(#000 var(--cut-a0), transparent var(--cut-a), transparent var(--cut-b), #000 var(--cut-b1));
}
/* На главной у пайплайна своих точек нет — под ним общий узор. */
.dots ~ main .pipe .nodes__canvas { background-image: none; }
