/* ==========================================================================
   ИНЖЕНЕР ИНФОРМАЦИОННЫХ ТЕХНОЛОГИЙ — страница-визитка
   Стиль: «инженерный даташит», монохром, светлая тема
   Шрифт: Geist Mono (локально, без внешних запросов)
   ========================================================================== */

/* --------------------------------------------------------------------------
   01 · ШРИФТ
   Пять субсетов. symbols2 нужен для псевдографики ─ │ ├ └ ╎ в схеме.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Geist Mono';
  src: url('../fonts/GeistMono-cyrillic-ext.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('../fonts/GeistMono-cyrillic.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('../fonts/GeistMono-symbols2.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('../fonts/GeistMono-latin-ext.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('../fonts/GeistMono-latin.woff2') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   02 · ТОКЕНЫ
   -------------------------------------------------------------------------- */
:root {
  /* монохромная шкала */
  --c-bg:        #F5F5F5;
  --c-surface:   #FFFFFF;
  --c-surface-2: #EDEDED;
  --c-ink:       #0F0F0F;
  --c-ink-2:     #3D3D3D;
  /* Шкала сдвинута вниз: прежние #6E6E6E и #9B9B9B давали на фоне 4.7:1
     и 2.55:1, а мелким ярлыком в 11px набрано почти всё служебное — при
     2.55:1 на дешёвой матрице или на солнце он пропадал. Теперь самый
     светлый серый даёт 5.0:1, порог WCAG для обычного текста — 4.5:1.
     Чёрный и основной текст (17.6:1 и 10:1) не тронуты. */
  --c-ink-3:     #5A5A5A;   /* 6.4:1 на фоне */
  --c-ink-4:     #6A6A6A;   /* 5.0:1 — самый светлый, что проходит AA */
  --c-rule:      #DCDCDC;   /* декоративные линейки листа, 1.3:1 — намеренно */
  --c-rule-2:    #C4C4C4;
  /* Кромка того, что нажимают, и линии, несущие смысл: 3.2:1 — порог WCAG
     для элементов интерфейса (1.4.11). Отдельным токеном, чтобы не тянуть
     за собой внутренние разделители листа. */
  --c-edge:      #8A8A8A;
  /* Латинские дубли заголовков (Services, Networks…) — декор, не смысл:
     владелец просил их светлее, чтобы читались, но не лезли в глаза.
     2.6:1 к листу; экранному диктору они скрыты (aria-hidden). */
  --c-ink-5:     #9A9A9A;
  /* Клетка тетради. Пока она стояла прямо за текстом, её пришлось
     приглушить вдвое — лезла вперёд содержимого. Теперь сайт — лист,
     лежащий на тетради (см. .page): в полную силу клетка видна только на
     полях вокруг листа, а под текстом проступает втрое слабее. Поэтому
     ей вернули прежнюю плотность (.036 и .060), а 05.10.2026 владелец
     попросил клетку чуть прозрачнее — обе линии на четверть светлее.
     На полях линия клетки темнее тетради на 6 единиц, линия модуля — на
     16: модуль ложится поверх клетки, и его линия — обе разом (было 8
     и 22, замер на живом сайте). */
  --c-grid:      rgba(15, 15, 15, .027);
  --c-grid-mod:  rgba(15, 15, 15, .045);
  /* Тетрадь — фон вокруг листа, на три единицы темнее его. Тон выбрал
     владелец, перебрав #DADADA и #E0E0E0. */
  --c-desk:      #F2F2F2;
  /* Просвет тетради по бокам листа и край листа — линия 1 px и мягкая
     тень. Нужны и листу, и шторкам у шапки (раздел 11): шторки
     повторяют этот край один в один. */
  --desk-gap:    clamp(10px, 3vw, 40px);
  --sheet-edge:  0 0 0 1px var(--c-rule), 0 14px 50px rgba(15, 15, 15, .07);

  /* инверсия — только для блока «Обучение» */
  --c-inv-bg:    #0F0F0F;
  --c-inv-ink:   #F2F2F2;
  --c-inv-ink-2: #B4B4B4;
  --c-inv-ink-3: #8A8A8A;
  --c-inv-ink-5: #565656;   /* то же приглушение на чёрном: 2.6:1 */
  --c-inv-rule:  #333333;

  /* типографика */
  --ff: 'Geist Mono', ui-monospace, SFMono-Regular, 'Cascadia Mono', 'Segoe UI Mono', Menlo, Consolas, monospace;
  --fs-display: clamp(1.9rem, 5.1vw, 3.625rem);
  --fs-h2:      clamp(1.5rem, 3.4vw, 2.25rem);
  --fs-h3:      clamp(1.0625rem, 1.6vw, 1.3125rem);
  --fs-body:    clamp(0.9375rem, 1vw, 1.0313rem);
  --fs-small:   0.875rem;    /* 14 px; было 13 — мелко для моноширинного */
  --fs-micro:   0.75rem;     /* 12 px; было 11 — меню, кнопки, ярлыки */

  /* ритм */
  --maxw:      1180px;
  --pad-x:     clamp(1.25rem, 5vw, 4rem);
  /* Поле раздела сверху и снизу. Было clamp(3.5rem, 8vw, 7rem): между
     концом одного раздела и заголовком следующего на 1280 px выходило
     ~205 px пустой бумаги, на телефоне 112 — владелец счёл это лишним
     (04.10.2026). Теперь ~115 и 80. Заголовок с чертой под ним отделяет
     раздел и так: пустота над ним всё ещё вдвое больше, чем под ним. */
  --section-y: clamp(2.5rem, 4.5vw, 4rem);
  --header-h:  58px;
}

/* --------------------------------------------------------------------------
   03 · СБРОС И БАЗА
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  color-scheme: only light;             /* без перекраски в тёмное — см. <head> */
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;              /* плавно — переходы по якорям; колесо сглаживает main.js, раздел 7 */
  scroll-padding-top: calc(var(--header-h) + 1.5rem);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  min-height: 100%;
  background: var(--c-desk);
  color: var(--c-ink-2);
  font-family: var(--ff);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.62;
  font-feature-settings: 'ss01' on;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Без одного слова на последней строке: владелец не любит переносов,
     после которых на строке остаётся одно слово (05.10.2026). Браузер
     сам переставит переносы в конце абзаца, пункта, заголовка — так,
     чтобы вниз ушло хотя бы два слова. Свойство наследуется, хватает
     одного объявления. Где его не знают (Firefox, старые браузеры),
     переносы обычные. Полосе характеристик этого мало — у неё свои
     правила, раздел 08. */
  text-wrap: pretty;
}
/* pretty в Chrome считает висячим только короткое слово: «восстановления»
   или «аутентификация» одни на строке он оставляет. Поэтому в конце
   каждого абзаца и пункта списка между двумя последними словами стоит
   неразрывный пробел (&nbsp; в разметке) — вниз уходят минимум два.
   Заголовки и названия заказчиков короткие, их выравниваем по длине
   строк (balance знают и Firefox, и Safari): строки выходят ровнее, и
   за край ничего не вылезет. Пунктам списка balance не подошёл — Chrome
   с ним оставлял одно слово в средней строке. */
h1, h2, h3, figcaption, .client__name { text-wrap: balance; }

h1, h2, h3, h4, p, ul, ol, dl, dd, figure, pre { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

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

/* Инженерная сетка на фоне — «тетрадь». Отдельный пустой элемент, а не
   body::before: сдвиг параллакса скрипт пишет ему самому, и браузер каждый
   кадр пересчитывает стиль одного этого элемента. Пока число стояло на
   <html>, его наследовали все элементы, и на каждом кадре прокрутки
   пересчитывалась вся страница: на ПК ~2 мс, на телефоне впятеро-вдесятеро
   дольше — прокрутка тормозила (замер 04.10.2026). */
.desk {
  position: fixed;
  /* Запас сверху и снизу — под сдвиг параллакса. Лист смещается не больше
     чем на период рисунка (240px), так что кромка не покажется. */
  inset: -240px 0;
  z-index: 0;
  pointer-events: none;
  /* Цвет тетради — на самой тетради, а не только на body. Фон body браузер
     рисует как холст всей страницы, и Mi Browser (Xiaomi) в тёмном режиме
     этот холст затемнял, несмотря на «only light»: поля вокруг листа
     выходили чёрными, полупрозрачный лист — серым с пятнами фактуры
     (05.10.2026). Тетрадь закрывает весь экран, холст под ней не виден. */
  background-color: var(--c-desk);
}
/* Клетка — у тетради и у её копий в шторках у шапки (раздел 11). */
.desk,
.flap__desk {
  background-image:
    linear-gradient(to right,  var(--c-grid-mod) 1px, transparent 1px),
    linear-gradient(to bottom, var(--c-grid-mod) 1px, transparent 1px),
    linear-gradient(to right,  var(--c-grid)     1px, transparent 1px),
    linear-gradient(to bottom, var(--c-grid)     1px, transparent 1px);
  background-size: 240px 240px, 240px 240px, 40px 40px, 40px 40px;
  /* Сдвиг ставит скрипт — переменной --grid-y. Не отработал, или человек
     просил меньше движения: подставляется 0px, и лист просто стоит
     на месте, как было до параллакса. */
  transform: translate3d(0, var(--grid-y, 0px), 0);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .desk, .flap__desk { transform: none; }
}

/* Лист. Сайт — лист без клетки, лежащий на тетради в клетку; тетрадь
   едет медленнее листа (параллакс в main.js), отсюда глубина. Идея
   владельца, обкатана на пробной странице.

   Поле тетради по бокам на широком экране получается само — макет уже
   окна. Ниже ~1240 px макет занял бы окно целиком, и клетка пропала бы,
   а на телефоне — с ней и весь параллакс. Поэтому поле не меньше
   --desk-gap: лист всегда чуть уже окна, на телефоне — полоски по ~11 px.

   Лист на 70 % плотный: клетка проступает сквозь него втрое слабее, чем
   на полях, но видно, что она едет и под текстом. Прозрачен только фон:
   opacity у всего листа сделала бы прозрачными и текст с картинками.
   Видимый тон = 0.7 × лист + 0.3 × тетрадь; лист = (245 − 0.3 × тетрадь)
   / 0.7 = 246 — на просвет ровно 245, тон, с которым сведены все контрасты
   текста. Меняете --c-desk — пересчитайте. Под тетрадью темнее ~225 так
   уже не выйдет: нужен будет лист белее белого.

   Кромка — волосяная линия, как у всех линеек листа, и едва заметная тень:
   лист приподнят над тетрадью. Линия обязательна — тон листа и тетради
   почти один (245 и 242), на одной тени край бы растворился. */
.page {
  position: relative;
  z-index: 1;
  width: min(var(--maxw), 100% - 2 * var(--desk-gap));
  margin-inline: auto;
  background: rgba(246, 246, 246, .7);
  box-shadow: var(--sheet-edge);
}
/* Фактура листа — «промокашка» (выбрал владелец 04.10.2026 на пробе /try/,
   сила 3 из 3): однотонная белая бумага с мягким матовым ворсом, без
   волокон, полос и пятен. Слой лежит над полупрозрачной заливкой листа
   и под всем содержимым: у .page свой слой (position + z-index), поэтому
   z-index:-1 не проваливается под тетрадь. Клетка под листом едет
   параллаксом, а фактура стоит на месте — она часть бумаги. Шум рисует
   сам браузер (feTurbulence в paper.svg, 3 КБ), плитка 512 px сшита без
   швов; средний тон листа она почти не меняет — тёмное в ней
   уравновешено светлым.
   Крупную «облачность» сняли 05.10.2026: она читалась лёгкими тёмными
   пятнами, а тон листа должен быть ровным (подробно — в paper.svg).
   Шапка, полоса характеристик и плашка схемы непрозрачны и остаются
   гладкими, чёрный блок «Обучение» — тоже. */
.page::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: url('../img/paper.svg?v=2') 0 0 / 512px 512px;
}

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--c-ink);
  color: var(--c-bg);
  padding: .75rem 1.25rem;
  font-size: var(--fs-small);
}
.skip:focus { left: 0; }

/* спрятать от глаза, но оставить экранному диктору */
.u-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Атрибут hidden обязан побеждать раскладку: авторское правило с display
   (у .btn — inline-flex) перебило бы правило браузера, и спрятанное
   мелькнуло бы на месте. Так пряталась кнопка «Позвонить», пока скрипт не
   соберёт номер; кнопку убрали 07.10.2026, правило оставлено на будущее. */
[hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   04 · ЗАЩИТА КОНТЕНТА
   С 03.10.2026 снята. Запреты остались здесь под классами no-select и
   no-print — их ставит main.js по флагам PROTECTION. Без классов (и без
   скрипта) страница открыта. См. README, п. «Защита».
   -------------------------------------------------------------------------- */
html.no-select body {
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
img, svg {
  -webkit-user-drag: none;
  user-drag: none;
  pointer-events: none;
}
a img, a svg { pointer-events: auto; }

/* печать и «сохранить в PDF» */
@media print {
  html.no-print .page { display: none !important; }
  html.no-print .desk { display: none !important; }
  .flap { display: none !important; }
  html.no-print body::after {
    content: 'Страница защищена от печати и копирования.';
    display: block;
    padding: 4rem;
    font-family: var(--ff);
    font-size: 12pt;
    color: #000;
  }
}

/* --------------------------------------------------------------------------
   05 · ТИПОГРАФСКИЕ ПРИМИТИВЫ
   -------------------------------------------------------------------------- */
.label {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--c-ink-4);
}

.idx {
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: .12em;
  color: var(--c-ink-4);
  font-variant-numeric: tabular-nums;
}

.rule { height: 1px; background: var(--c-rule); border: 0; }

/* шапка раздела: 01 / НАЗВАНИЕ ................ LATIN */
.sec-head {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--c-ink);
  margin-bottom: clamp(1.75rem, 3.5vw, 2.75rem);
}
.sec-head__title {
  font-size: var(--fs-h2);
  font-weight: 500;
  letter-spacing: -.02em;
  line-height: 1.1;
  color: var(--c-ink);
}
.sec-head__lat {
  margin-left: auto;
  flex: none;
  font-size: var(--fs-micro);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--c-ink-5);
}

/* Линии между разделами нет (снята 04.10.2026 по просьбе владельца):
   раздел и так отделяет крупный заголовок с чёрной чертой под ним,
   волосяная линия над ним была лишней. */
.section { padding-block: var(--section-y); }

/* --------------------------------------------------------------------------
   06 · ШАПКА
   -------------------------------------------------------------------------- */
/* Шапка белая — светлее листа — и плотная: под ней проезжает текст,
   и сквозь полупрозрачную он просвечивал бы. */
.header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-rule);
}
.header__bar {
  display: flex;
  align-items: center;
  gap: 1rem;
  height: var(--header-h);
}
.brand {
  display: flex;
  align-items: center;
  flex: none;
  padding-block: .625rem;   /* зона нажатия 56 px; шапка 58, влезает */
}
/* Знак «МИТ» — квадрат «МИ / Т●» (logo/concepts-3, направление 02). Буквы
   уже в нём, поэтому набранного «МИТ» рядом больше нет. 36 px — меньше
   буквы в клетках сливаются, больше — знак перевешивает меню. */
.brand__mark { display: block; width: 36px; height: 36px; }

.nav { margin-left: auto; }
.nav__list { display: flex; align-items: center; gap: clamp(1rem, 2.4vw, 2rem); }
.nav__link {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-ink-3);
  padding-block: .35rem;
  border-bottom: 1px solid transparent;
  transition: color .12s linear, border-color .12s linear;
}
.nav__link:hover { color: var(--c-ink); border-bottom-color: var(--c-ink); }

.burger {
  display: none;
  margin-left: auto;
  align-items: center;
  gap: .5rem;
  background: none;
  border: 1px solid var(--c-edge);
  color: var(--c-ink);
  font: inherit;
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  padding: .45rem .7rem;
  /* Подпись переключается МЕНЮ ↔ ЗАКРЫТЬ, и кнопка прыгала бы шире на
     нажатии. Ширину задаём по длинному слову — моноширинный шрифт
     считается точно: 7 знаков по .6em плюс трекинг .16em после каждого,
     затем поля .7rem с двух сторон и две рамки по 1px (box-sizing
     у нас border-box). Высота у обоих слов и так одна: кегль и поля
     те же, перенос запрещён. */
  min-width: calc(7 * (.6em + .16em) + 1.4rem + 2px);
  justify-content: center;
  white-space: nowrap;
  cursor: pointer;
}
.burger:hover { border-color: var(--c-ink); }
/* Рамка кнопки — 36 px в высоту, а палец просит 44. Не раздуваем рамку,
   а расширяем невидимую зону нажатия вокруг неё. */
.burger { position: relative; }
.burger::after { content: ''; position: absolute; inset: -6px -4px; }

@media (max-width: 820px) {
  .burger { display: inline-flex; }
  .nav {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    background: var(--c-surface);   /* часть шапки — её цвет */
    border-bottom: 1px solid var(--c-rule);
  }
  .nav.is-open { display: block; }
  .nav__list { flex-direction: column; align-items: stretch; gap: 0; padding: var(--pad-x); padding-block: .5rem 1.25rem; }
  /* Ссылка здесь — блок. Строчной она не раздвигалась полями, а нижняя
     кромка уезжала вниз на их величину и перечёркивала следующий пункт —
     те самые обрывки линеек поперёк слов. Разделителей между пунктами
     больше нет: их четыре, столбиком они читаются и без разлиновки. */
  .nav__link { display: block; padding-block: .85rem; border-bottom: 0; font-size: var(--fs-small); }
}

/* --------------------------------------------------------------------------
   07 · ПЕРВЫЙ ЭКРАН
   -------------------------------------------------------------------------- */
.hero { padding-block: clamp(3rem, 7vw, 6rem) var(--section-y); }
.hero__grid {
  display: grid;
  /* Колонка портрета шире прежнего: композиция в ней вертикальная (2:3),
     и при 1.35fr она оставляла пустой весь верх правой половины экрана. */
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}

.hero__eyebrow {
  display: flex;
  align-items: center;
  gap: .625rem;
  margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}
.hero__eyebrow::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--c-rule);
}
/* На телефоне надзаголовок обязан встать в одну строку (просьба владельца).
   35 знаков моноширинного набора при 12 px и трекинге .2em — 336 px, а
   у экрана в 375 px под текст остаётся 288. Кегль тянется от ширины
   экрана (3vw, от 10 до 12 px), трекинг ужат: до 480 px вдвое, до 380 —
   вчетверо. На 320: 35 знаков × 10 px × (.6 + .05) = 228 px при 235
   доступных; на 375 — 256 при 288; с 400 px — снова 12 px. */
@media (max-width: 480px) {
  .hero__eyebrow .label { font-size: clamp(10px, 3vw, 12px); letter-spacing: .1em; }
}
@media (max-width: 380px) {
  .hero__eyebrow .label { letter-spacing: .05em; }
}

/* Расшифровка МИТ в три строки. Аббревиатуру набирает сама вёрстка:
   строки начинаются от одного края, поэтому М, И и Т стоят колонкой.
   Выделять их не нужно — на плотном чёрном наборе колонка читается сама.

   Набрано прописными. Причина не только в стиле: у строчных «р» и «ф»
   есть выносные элементы, и на трёх строках подряд они лезли бы в
   верхние знаки следующей строки. У капители выносных нет.

   Трекинг отрицательный: набор сжат в плотный блок. Кернинга здесь нет
   и быть не может — шрифт моноширинный, каждый знак занимает клетку в
   0.6em, парные подгонки в таких гарнитурах отключены. Дальше -.03em
   уводить не стоит: у капители Geist Mono боковые просветы около .05em
   на пару, при -.06em знаки начнут слипаться.

   Шрифт моноширинный, ширина строки считается точно:
       знаков × (0.6 + трекинг) × кегль
   Длинная строка здесь — «ИНФОРМАЦИОННЫХ», 14 знаков против 7 у прежнего
   «ДМИТРИЙ». Вдвое длиннее, поэтому и кегль вдвое мельче прежнего
   --fs-display: иначе строка вылезала бы из колонки на любом экране.
   Меняете трекинг — пересчитайте и кегль, иначе заполнение колонки
   уплывёт: эти два значения связаны формулой выше.

   Отрицательный text-indent — оптическое выравнивание, а не сдвиг.
   Знак стоит в клетке не вплотную к её краю: у «М» есть левый просвет
   в .035em. При кегле лида это доли пикселя, а при кегле заголовка уже
   2 px — левые края формально совпадают, а глаз видит уступ. Отступ
   задан в em, поэтому поправка масштабируется вместе с кеглем сама;
   в пикселях она была бы верна ровно на одной ширине экрана. */
.hero__mit {
  font-size: clamp(1.5rem, 1.51rem + 1.55vw, 2.75rem);
  font-weight: 500;
  line-height: 1.18;
  letter-spacing: -.03em;
  text-indent: -.035em;
  text-transform: uppercase;
  color: var(--c-ink);
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--c-rule);
  margin-bottom: 1.5rem;
}
.hero__mit-line { display: block; }
.hero__lead {
  max-width: 54ch;
  color: var(--c-ink-2);
}
.hero__lead strong { font-weight: 500; color: var(--c-ink); }

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
}

.hero__status {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-top: 1.75rem;
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-ink-3);
}
.hero__status::before {
  content: '';
  width: 7px;
  height: 7px;
  background: var(--c-ink);
  animation: cursor-blink 1s step-end infinite;
}

/* Мигание курсором: step-end даёт жёсткое переключение вкл/выкл,
   без плавного затухания — как в терминале. Период 1 с: полсекунды горит,
   полсекунды нет. */
@keyframes cursor-blink { 50% { opacity: 0; } }

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

/* портрет
   Готовая композиция: рамка и человек, выходящий из кадра, — всё сделано
   владельцем в графическом редакторе и вставлено одной картинкой. Никакой
   сборки на стороне страницы здесь нет.

   Файл с прозрачностью, и это принципиально: фоновая клетка просвечивает
   вокруг рамки, а не обрывается по прямоугольнику, как было со снимком на
   непрозрачной подложке. Прозрачность почти ничего не стоит: WebP с альфой
   в 1200 px — 131 КБ, прежний непрозрачный JPEG был 113 КБ при вдвое
   меньшем разрешении. 1200 — это 520 px показа на экране с двойной
   плотностью пикселей (retina, телефоны) с небольшим запасом.

   Прозрачность в файле двухступенчатая: пиксель либо есть, либо его нет,
   промежуточных значений в нём не бывает. Поэтому мягкую тень в файле не
   сохранить — её рисует браузер, см. .portrait__img ниже. */
.portrait {
  position: relative;
  /* Ширина самой картинки: колонка плюс вылет на поле страницы. Вынесена
     в переменную, потому что от неё считаются и подпись, и её поля, —
     а в одну колонку вылета нет, и менять приходилось бы в трёх местах. */
  --img-w: calc(100% + var(--pad-x));
  /* Доли ширины файла, занятые пустым полем вокруг рамки. Промерено по
     файлу 1200×1200: рамка занимает x 123..1062, y 159..1113. По ним подпись
     равняется на кромки рамки, а не на край файла, и подтягивается под
     нижнюю кромку. Запасной PNG 500 px сходится с ними до 0.002 — меньше
     пикселя на экране. Меняете картинку — промерьте заново. */
  --mat-l: .1025;  /* 123 / 1200 */
  --mat-r: .1142;  /* (1200 − 1063) / 1200 */
  --mat-b: .0717;  /* (1200 − 1114) / 1200 */
  /* Просвет от нижней кромки рамки до подписи. Первые ~24 px его занимает
     тень, поэтому меньше делать нельзя — подпись сядет в неё. */
  --cap-gap: 2rem;
}

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

   Композиция квадратная — это и позволило свести оба требования. При
   вертикальной (2:3) они не сходились: высота жёстко следует за шириной, и
   чтобы низ встал к кнопкам, ширину пришлось бы ронять с 522 до 321, открывая
   просвет в 225 px. У квадрата при той же высоте ширина 483 — почти прежняя.

   Съезд вверх задан в долях ширины колонки (проценты у margin считаются от
   ширины, в том числе у margin-top), поэтому соразмерен макету на любом
   экране. */
.portrait__pic {
  display: block;
  width: var(--img-w);
  margin-right: calc(var(--pad-x) * -1);
  margin-top: -13%;
}
.portrait__img {
  display: block;
  width: 100%;
  height: auto;
  /* Тень рисует браузер. drop-shadow идёт по альфа-каналу, а не по коробке,
     поэтому ложится точно по контуру — вдоль рамки и вокруг головы и руки,
     вышедших за неё. box-shadow обвёл бы прямоугольник целиком. */
  filter: drop-shadow(0 var(--sh-y, 6px) var(--sh-blur, 18px) rgba(15, 15, 15, .22));
}

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

   Отрицательный отступ сверху — не опечатка: под нижней кромкой рамки
   в файле осталась полоса пустоты в 7 % высоты, и без поправки подпись
   отъезжала бы от композиции на её величину. Вычитаем полосу и возвращаем
   просвет явным слагаемым — им и задаётся расстояние до кромки рамки. */
.portrait__cap {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  width: var(--img-w);
  margin-right: calc(var(--pad-x) * -1);
  padding-left:  calc(var(--img-w) * var(--mat-l));
  padding-right: calc(var(--img-w) * var(--mat-r));
  margin-top: calc(var(--cap-gap) - var(--img-w) * var(--mat-b));
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-ink-4);
}

@media (max-width: 900px) {
  .hero__grid { grid-template-columns: 1fr; }
  /* В одну колонку выходить некуда: слева край страницы, сверху текст. */
  .portrait { max-width: 340px; --img-w: 100%; --sh-y: 4px; --sh-blur: 12px; --cap-gap: 1.25rem; }
  .portrait__pic { margin: 0; }
  .portrait__cap { margin-right: 0; }
}

/* --------------------------------------------------------------------------
   08 · ПОЛОСА ХАРАКТЕРИСТИК
   -------------------------------------------------------------------------- */
.spec {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border: 1px solid var(--c-rule);
  background: var(--c-surface);
}
.spec__cell { padding: clamp(1rem, 2vw, 1.5rem); border-left: 1px solid var(--c-rule); }
.spec__cell:first-child { border-left: 0; }
.spec__key {
  display: block;
  font-size: var(--fs-micro);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--c-ink-4);
  margin-bottom: .5rem;
}
.spec__val { font-size: var(--fs-small); color: var(--c-ink); line-height: 1.5; }
/* Значение — две половинки, и ни одна не рвётся внутри: оно либо стоит
   в строку, либо делится ровно посередине, по два слова и больше на
   строке (владелец не любит переносов, после которых на строке остаётся
   одно слово, 05.10.2026). Поэтому колонок четыре только с 1160 px, а
   две — до 600: так самая длинная половинка, «разово · сопровождение»
   (22 знака), всегда влезает в ячейку. Прежде четыре колонки стояли до
   820 px, и в ячейку влезало от 16 знаков. */
.spec__val > span { white-space: nowrap; }

@media (max-width: 1159px) {
  .spec { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .spec__cell:nth-child(odd) { border-left: 0; }
  .spec__cell:nth-child(n + 3) { border-top: 1px solid var(--c-rule); }
}
@media (max-width: 600px) {
  .spec { grid-template-columns: 1fr; }
  .spec__cell { border-left: 0; }
  .spec__cell + .spec__cell { border-top: 1px solid var(--c-rule); }
}

/* --------------------------------------------------------------------------
   09 · СХЕМА (рис. 01)
   -------------------------------------------------------------------------- */
.figure { margin-top: clamp(2.5rem, 5vw, 4rem); }
.figure__plate {
  position: relative;
  border: 1px solid var(--c-rule);
  background: var(--c-surface);
  padding: clamp(1.25rem, 3vw, 2.25rem);
}
.figure__stamp {
  position: absolute;
  top: .75rem;
  right: 1rem;
  font-size: var(--fs-micro);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--c-ink-4);
}
.figure__cap {
  margin-top: .875rem;
  font-size: var(--fs-small);
  color: var(--c-ink-3);
}

.diagram { width: 100%; height: auto; }
.diagram__box  { fill: var(--c-surface); stroke: var(--c-edge); stroke-width: 1; }
.diagram__line { fill: none; stroke: var(--c-edge); stroke-width: 1; }
.diagram__dash { fill: none; stroke: var(--c-ink-4); stroke-width: 1; stroke-dasharray: 4 4; }
.diagram__dot  { fill: var(--c-ink); }
.diagram__t    { fill: var(--c-ink);   font-size: 17px; letter-spacing: 1.2px; }
.diagram__s    { fill: var(--c-ink-4); font-size: 14px; letter-spacing: .6px; }
.diagram__p    { fill: var(--c-ink-3); font-size: 13px; letter-spacing: 1.6px; }

/* мобильный вариант схемы — псевдографикой */
.diagram-mini { display: none; }
.diagram-mini pre {
  font-family: var(--ff);
  /* ужимается на узких телефонах, чтобы схема влезала без прокрутки */
  font-size: clamp(.66rem, 3.1vw, .75rem);
  line-height: 1.5;
  color: var(--c-ink-2);
  overflow-x: auto;
}
.diagram-mini b { font-weight: 500; color: var(--c-ink); }
.diagram-mini i { font-style: normal; color: var(--c-ink-4); }

/* ниже этой ширины SVG-схема мельчает — показываем её псевдографикой */
@media (max-width: 1080px) {
  .diagram { display: none; }
  .diagram-mini { display: block; }
}

/* --------------------------------------------------------------------------
   10 · УСЛУГИ — строки в духе спецификации
   -------------------------------------------------------------------------- */
.svc { border-top: 1px solid var(--c-rule); }
.svc__row {
  display: grid;
  /* 28ch, а не 26: так «и инфраструктура» умещается строкой, и название
     «Серверы и инфраструктура» не оставляет одно слово внизу */
  grid-template-columns: minmax(0, 28ch) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  padding-block: clamp(1.75rem, 3.5vw, 2.5rem);
  /* воздух до краёв подсветки: линейки и фон остаются во всю ширину
     колонки, а текст отступает внутрь — как ячейка таблицы */
  padding-inline: clamp(0.875rem, 2vw, 2rem);
  border-bottom: 1px solid var(--c-rule);
  transition: background-color .12s linear;
}
/* Подсветка строки. С мышью — при наведении: листаешь колесом, и она
   бежит по строкам под указателем. На сенсорном экране наведения нет —
   браузер считал «наведённой» строку, которой коснулись, и подсветка
   залипала на ней, уезжая вместе со строкой при листании (замечание
   владельца 05.10.2026). Там подсвечивает скрипт, классом .is-lit:
   строку посреди экрана, когда листание закончилось, и строку, которой
   коснулись (main.js, раздел 8). */
@media (hover: hover) {
  .svc__row:hover { background: var(--c-surface); }
}
.svc__row.is-lit { background: var(--c-surface); }
/* На телефоне подсветка меняется сразу, без плавного перехода: каждый
   кадр перехода — перерисовка строки вместе с фактурой листа, а так она
   одна. Скрипт и меняет её только когда страница стоит — см. main.js. */
@media (hover: none) {
  .svc__row { transition: none; }
}

.svc__head { display: flex; gap: 1rem; }
.svc__idx {
  flex: none;
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--c-ink-4);
  font-variant-numeric: tabular-nums;
  padding-top: .2em;
}
/* Барабан номера: окно в одну строку, внутри — лента значений.
   Высоту окна не задаём здесь: её ставит скрипт по фактической высоте
   ячейки. Назначить свою значило бы сдвинуть номер по вертикали
   относительно названия — у .svc__idx нет своей line-height, он берёт
   её у body, и любое расхождение сразу видно. */
.idx-reel {
  display: block;
  overflow: hidden;
}
.idx-reel__strip {
  display: block;
  will-change: transform;
}
.idx-reel__cell { display: block; }
/* значение приходит из атрибута: в тексте страницы лента не появляется */
.idx-reel__cell::before { content: attr(data-v); }

.svc__title {
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.015em;
  line-height: 1.25;
  color: var(--c-ink);
}
.svc__lat {
  display: block;
  margin-top: .45rem;
  font-size: var(--fs-micro);
  font-weight: 400;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--c-ink-5);
}

.svc__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .625rem clamp(1.5rem, 3vw, 2.5rem);
  align-content: start;
}
.svc__list li {
  position: relative;
  padding-left: 1.35rem;
  font-size: var(--fs-small);
  line-height: 1.55;
  color: var(--c-ink-2);
}
.svc__list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: .62em;
  width: 8px;
  height: 1px;
  background: var(--c-edge);
}

@media (max-width: 900px) {
  .svc__row { grid-template-columns: 1fr; gap: 1.25rem; }
  .svc__list { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   11 · ОБУЧЕНИЕ — инверсный блок, смысловой центр страницы
   -------------------------------------------------------------------------- */
/* Стеклянная плита (04.10.2026, выбор владельца на пробах /try/: стекло
   силы 1, клетки сквозь стекло нет, выступ 5 px, углы 5 px — подобраны
   регулятором; 24 px выступа оказалось много). Плита шире листа —
   выступает за его края на --overhang с каждой стороны, будто лежит перед
   ним, ближе к глазу; поля внутри на ту же величину, так что текст стоит
   по линии всего листа. Выступ — не больше просвета тетради за вычетом
   3 px (на узком телефоне это 7 px), чтобы плита не упиралась в край
   экрана. Сверху и снизу — полоса бумаги, плита — отдельный предмет.
   Стекло: чёрный на 93 % (на просвет ~#1F1F1F), сверху вниз — слабый
   отсвет, по верхней кромке — блик, по периметру — едва светлая грань;
   тень короткая у края и длинная мягкая ниже — плита приподнята.
   Клетку сквозь стекло не видно: через лист и стекло от линии тетради
   остаётся меньше единицы яркости. */
.training {
  --overhang: min(5px, calc(var(--desk-gap) - 3px));
  margin: clamp(1rem, 2.5vw, 2rem) calc(var(--overhang) * -1);
  padding-inline: var(--overhang);
  border-radius: 5px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .035) 0, rgba(255, 255, 255, 0) 38%),
    rgba(15, 15, 15, .93);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .12),
    inset 0 0 0 1px rgba(255, 255, 255, .045),
    inset 0 0 28px rgba(255, 255, 255, .022),
    0 1px 2px rgba(15, 15, 15, .2),
    0 14px 24px -12px rgba(15, 15, 15, .24),
    0 40px 70px -26px rgba(15, 15, 15, .34);
  color: var(--c-inv-ink-2);
}
.training .sec-head { border-bottom-color: var(--c-inv-ink); }
.training .sec-head__title { color: var(--c-inv-ink); }
.training .label,
.training .idx { color: var(--c-inv-ink-3); }   /* 4.8:1 на стекле */
.training .sec-head__lat { color: var(--c-inv-ink-5); }

.training__lead {
  max-width: 60ch;
  font-size: var(--fs-h3);
  line-height: 1.5;
  letter-spacing: -.01em;
  color: var(--c-inv-ink);
  margin-bottom: clamp(2.25rem, 4.5vw, 3.5rem);
}

.training__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--c-inv-rule);
}
.training__col {
  padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1.25rem, 2.5vw, 2rem);
  border-left: 1px solid var(--c-inv-rule);
}
.training__col:first-child { border-left: 0; padding-left: 0; }
.training__col:last-child { padding-right: 0; }
.training__idx {
  display: block;
  font-size: var(--fs-micro);
  letter-spacing: .2em;
  color: var(--c-inv-ink-3);
  margin-bottom: .875rem;
}
.training__title {
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.015em;
  color: var(--c-inv-ink);
  margin-bottom: .75rem;
}
.training__col p { font-size: var(--fs-small); line-height: 1.6; }

.training__foot {
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--c-inv-rule);
  font-size: var(--fs-small);
  color: var(--c-inv-ink-2);
}
.training__foot b { font-weight: 500; color: var(--c-inv-ink); }

@media (max-width: 860px) {
  .training__grid { grid-template-columns: 1fr; }
  .training__col {
    border-left: 0;
    border-top: 1px solid var(--c-inv-rule);
    padding-inline: 0;
  }
  .training__grid { border-top: 0; }
}

/* Шторки у шапки. Плита шире листа, а шапка — по ширине листа: уезжая под
   шапку, плита торчала бы выступами по её бокам. Пока плита проходит под
   шапкой, по бокам от шапки, на полосу её высоты, встают шторки — копия
   того, что там и так видно: тетрадь с той же клеткой и тем же сдвигом,
   кромка листа и его тень. Тень не подобрана, а нарисована тем же способом:
   вплотную за краем шторки стоит невидимый «лист» с той же тенью
   (--sheet-edge), шторка обрезает его по своему краю.
   Выступы плиты уходят под шторки ровно по низу шапки. Шторки, как и
   шапка, стоят на месте относительно экрана, поэтому срез идёт с шапкой
   точно в такт при любой прокрутке и без расчётов на каждом кадре; сама
   плита — обычная часть листа, в ней ничего не меняется.
   Прежде (04.10.2026) срезали саму плиту: сначала менялся её clip-path —
   браузер перерисовывал плиту каждый кадр; потом плита стала отдельным
   слоем, сдвигаемым навстречу срезу, — на телефоне прокрутка спотыкалась
   там, где этот большой слой целиком скрывался под шапкой и появлялся
   снова (жалобы владельца). Шторки — маленькие и неподвижные.
   Включает их main.js (раздел 6), только пока плита рядом с шапкой: у
   верха страницы тень листа другая — она смещена вниз на 14 px. Без
   скрипта шторок нет, и выступы видны по бокам шапки — терпимо. */
.flap {
  position: fixed;
  top: 0;
  z-index: 2;                            /* над листом: у .page — 1 */
  width: calc((100% - min(var(--maxw), 100% - 2 * var(--desk-gap))) / 2);
  height: calc(var(--header-h) + 1px);   /* шапка с нижней линейкой */
  overflow: hidden;
  pointer-events: none;
  visibility: hidden;
  background: var(--c-desk);
}
.flap.is-on { visibility: visible; }
.flap--l { left: 0; }
.flap--r { right: 0; }
/* Копия клетки — от левого края окна, как у тетради. Ширину окна без
   полосы прокрутки (--cbw) ставит скрипт: правой шторке клетка нужна
   с тем же шагом от того же края. */
.flap__desk {
  position: absolute;
  top: -240px;
  left: 0;
  width: var(--cbw, 100vw);
  height: calc(var(--header-h) + 1px + 480px);
  background-color: var(--c-desk);       /* как у тетради: слой тоже непрозрачный */
}
.flap--r .flap__desk { left: auto; right: 0; }
/* Невидимый «лист» за краем шторки: от него линия кромки и тень. Выше и
   ниже полосы на 400 px — тень сбоку ровная, как посреди листа. */
.flap__edge {
  position: absolute;
  top: -400px;
  bottom: -400px;
  width: 400px;
  box-shadow: var(--sheet-edge);
}
.flap--l .flap__edge { left: 100%; }
.flap--r .flap__edge { right: 100%; }

/* --------------------------------------------------------------------------
   12 · ПОДХОД — шесть пунктов, A–F
   -------------------------------------------------------------------------- */
/* Плитка 3 × 2 (04.10.2026). Прежде было 4 + 1: пункт E тянулся отдельной
   строкой во всю ширину и смотрелся оторванным. Владелец добавил пункт F
   (о цене), и шесть пунктов легли ровно: три колонки на широком экране,
   две на планшете, одна на телефоне.
   Крайние ячейки — без полей наружу: текст встаёт по краям раздела.
   Нижний ряд — без линейки и без поля под текстом: раздел кончается
   текстом, а следующий отделяет его заголовок с чертой; линейка внизу
   висела бы без дела. */
.approach {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--c-rule);
}
.approach__item {
  --py: clamp(1.5rem, 3vw, 2rem);
  padding: var(--py) clamp(1.25rem, 2.4vw, 1.75rem);
  border-left: 1px solid var(--c-rule);
  border-bottom: 1px solid var(--c-rule);
}
/* Крайние ячейки трёх колонок. Запрос — точное дополнение к
   «max-width: 1000px» ниже: у пары min-width 1001 / max-width 1000 на
   дробной ширине окна (масштаб браузера) была бы щель в пиксель, где не
   действует ни одна раскладка. */
@media not all and (max-width: 1000px) {
  .approach__item:nth-child(3n + 1) { border-left: 0; padding-left: 0; }
  .approach__item:nth-child(3n) { padding-right: 0; }
  .approach__item:nth-last-child(-n + 3) { border-bottom: 0; padding-bottom: 0; }
}
.approach__key {
  display: block;
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: .2em;
  color: var(--c-ink-4);
  margin-bottom: .875rem;
}
.approach__title {
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: -.01em;
  color: var(--c-ink);
  margin-bottom: .625rem;
}
.approach__item p { font-size: var(--fs-small); line-height: 1.55; }

@media (max-width: 1000px) {
  .approach { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .approach__item:nth-child(odd) { border-left: 0; padding-left: 0; }
  .approach__item:nth-child(even) { padding-right: 0; }
  .approach__item:nth-last-child(-n + 2) { border-bottom: 0; padding-bottom: 0; }
}
@media (max-width: 600px) {
  .approach { grid-template-columns: 1fr; }
  .approach__item { border-left: 0; padding-inline: 0; }
  /* в столбике предпоследний пункт — уже не нижний ряд */
  .approach__item:nth-last-child(2) { border-bottom: 1px solid var(--c-rule); padding-bottom: var(--py); }
}

/* --------------------------------------------------------------------------
   13 · ЗАКАЗЧИКИ — плитка 3 × 3
   Знаки — официальные, с сайтов заказчиков; исходники, источники и
   сборщик — в папке clients/ (на хостинг не выкладывается).
   -------------------------------------------------------------------------- */
.clients__lead {
  max-width: 62ch;
  color: var(--c-ink-2);
  margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem);
}

/* Плитка — таблица на белой плашке, как полоса характеристик под
   портретом: снаружи рамка, между ячейками волосяные линейки. Белая
   плашка нужна и самим знакам: их рисовали под белый фон. */
.clients {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border: 1px solid var(--c-rule);
  background: var(--c-surface);
}
.client {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1rem, 2vw, 1.5rem);
  border-left: 1px solid var(--c-rule);
  border-top: 1px solid var(--c-rule);
}
.client:nth-child(3n + 1) { border-left: 0; }
.client:nth-child(-n + 3) { border-top: 0; }

/* Знаки разной формы — от гербов до длинных надписей, и одной высотой их
   не уравнять: герб в 48 px выглядит марочкой, надпись в 76 — вывеской.
   Высота у каждого своя (--h в разметке), подобрана под равный «вес»;
   файлы втрое крупнее показа. Коробка общая, по самому высокому знаку, —
   подписи стоят в ряд. */
.client__logo {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 84px;
  margin-bottom: 1.25rem;
}
.client__logo img {
  width: auto;
  height: var(--h);
  max-width: 100%;
  object-fit: contain;
}
.client__name {
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1.4;
  color: var(--c-ink);
}
.client__place {
  margin-top: .375rem;
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-ink-4);
}

/* Цвет знаков (выбор владельца 04.10.2026). С мышью знаки серые и
   цветнеют при наведении: сайт чёрно-белый, и девять цветных пятен
   спорили бы с текстом. На телефоне и планшете наведения нет — цвет
   появлялся бы только по нажатию, поэтому там знаки цветные всегда.
   Делит не ширина экрана, а способ ввода: (hover: hover) и точный
   указатель — это мышь или тачпад.
   Серый у знаков выходит разный по тону: золото таможенного герба —
   светлым, чёрный «Доктор Байт» — чёрным. --tone в разметке подтягивает
   каждый к общей плотности (яркость: меньше 1 — темнее). */
@media (hover: hover) and (pointer: fine) {
  .client__logo img {
    filter: grayscale(1) brightness(var(--tone, 1));
    transition: filter .2s linear;
  }
  .client:hover .client__logo img { filter: none; }
}

/* Телефон: столбик строк, как перечень в спецификации, — знак слева,
   имя и город справа. Плитка в две колонки дала бы девятому знаку
   пустого соседа, в три — подписи в 11 знаков шириной.
   Колонка знака тянется от ширины экрана (96 px на 320, 120 на 400):
   справа должно влезать «энергетическая» — самое длинное слово, 14 знаков.
   Кегль имени на узких экранах — 13 px, иначе слово упирается в край. */
@media (max-width: 600px) {
  .clients { grid-template-columns: minmax(0, 1fr); }
  .client {
    display: grid;
    grid-template-columns: clamp(80px, 30vw, 120px) minmax(0, 1fr);
    grid-template-rows: auto auto;
    align-items: center;
    column-gap: clamp(.75rem, 3vw, 1rem);
    text-align: left;
    padding: .875rem;
    border-left: 0;
    border-top: 1px solid var(--c-rule);
  }
  .client:first-child { border-top: 0; }
  /* Вторая и третья ячейки в плитке стоят в первом ряду, и черту сверху
     им снимает правило выше по файлу; в столбике она им нужна. */
  .client:nth-child(-n + 3):not(:first-child) { border-top: 1px solid var(--c-rule); }
  .client__logo {
    grid-row: 1 / 3;
    height: 60px;
    margin: 0;
  }
  .client__logo img { height: calc(var(--h) * .72); }
  .client__name { align-self: end; font-size: clamp(.8125rem, 3.6vw, .875rem); }
  .client__place { align-self: start; margin-top: .25rem; }
}

/* Окошко «Оказанные услуги» у каждого знака (обкатано на пробе /try/,
   09–10.10.2026). С мышью — при наведении на плитку, на телефоне и
   планшете — по касанию: касание снова или мимо закрывает, плитка
   уехала с экрана — тоже (main.js, раздел 9). Появляется и гаснет за
   0.5 с. Светло-серое (владелец: «не белым и не чёрным, а серым», из
   трёх серых выбрал светлое). Фон плотный на 90 %, и то, что под ним,
   проступает размытым — стекло, как у плиты «Обучение»: без размытия
   соседний текст читался бы призраком между строк. Сам текст — сплошной,
   по левому краю, без переносов: выключку по ширине владелец снял
   (дыры между словами, переносы, разрядка).

   Плитка с окошком: имя подчёркнуто точками — как термин со сноской.
   Отметка в углу не нужна, а на телефоне ей и нет места: справа от
   знака должно влезать «энергетическая». */
.client[data-case] { position: relative; }
.client[data-case] .client__name {
  text-decoration: underline dotted var(--c-ink-5) 1px;
  text-underline-offset: .3em;
  transition: text-decoration-color .5s ease;
}
.client[data-case].is-open .client__name { text-decoration-color: var(--c-ink); }

/* Висит под плиткой и заходит на её нижнее поле — видно, откуда оно
   выросло; у нижнего края экрана открывается вверх (скрипт ставит
   .is-up). Ширина — не уже 21rem (~38 знаков), иначе в плитке планшета
   строка вышла бы в 20 знаков и окошко вытянулось бы столбиком. Мышь
   сквозь окошко проходит: под ним соседняя плитка, и наведение на неё
   работает как обычно. Над шапкой окошко не встанет: у шапки z-index 50. */
.case {
  position: absolute;
  z-index: 10;
  top: calc(100% - .5rem);
  left: .75rem;
  width: max(calc(100% - 1.5rem), 21rem);
  padding: .875rem 1rem 1rem;
  text-align: left;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .5s ease, visibility 0s linear .5s;
  background: rgba(220, 220, 220, .9);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--c-ink);
  border: 1px solid rgba(15, 15, 15, .10);
  box-shadow: 0 14px 32px -14px rgba(15, 15, 15, .4), 0 2px 6px rgba(15, 15, 15, .08);
}
/* Средняя колонка — окошко посередине плитки, правая — по правому краю:
   оно шире плитки и не должно вылезать за плитку-таблицу. */
.client[data-case]:nth-child(3n + 2) .case { left: 50%; transform: translateX(-50%); }
.client[data-case]:nth-child(3n) .case { left: auto; right: .75rem; }
.client[data-case].is-up .case { top: auto; bottom: calc(100% - .5rem); }

.case__k {
  display: block;
  margin-bottom: .625rem;
  padding-bottom: .5rem;
  border-bottom: 1px solid rgba(15, 15, 15, .14);
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #4A4A4A;   /* 6.7:1 на сером окошке над белой плиткой */
}
/* Кегль 13 px (на телефоне 12) — на пункт мельче остального текста
   (владелец, 10.10.2026): окошко — справка к плитке.
   Перенос — обычный, строка за строкой (wrap), а не «pretty», как у
   остального текста: тот, чтобы последняя строка не вышла короткой,
   перетаскивает в неё слова с предыдущей, и справа остаётся дыра. Здесь
   владельцу важнее ровный правый край: одно слово на последней строке
   можно, и конец текста в разметке не склеен (склейка двух последних
   слов уносила их вдвоём — та же дыра строкой выше). */
.case__text {
  font-size: .8125rem;
  line-height: 1.55;
  text-wrap: wrap;
}

/* Видно: касанием (.is-open), с клавиатуры (фокус) — и мышью при
   наведении. Мыши — короткая задержка 0.12 с: когда курсор пролетает
   плитку насквозь, окошко не вспыхивает. */
.client.is-open .case,
.client[data-case]:focus-visible .case {
  opacity: 1;
  visibility: visible;
  transition: opacity .5s ease, visibility 0s;
}
@media (hover: hover) and (pointer: fine) {
  .client[data-case]:hover .case {
    opacity: 1;
    visibility: visible;
    transition: opacity .5s ease .12s, visibility 0s linear .12s;
  }
  .client[data-case]:hover .client__name { text-decoration-color: var(--c-ink); }
}

/* Телефон: строка списка — окошко во всю её ширину, поля внутри уже,
   кегль 12 px: в строке 35–41 знак, окошко ниже, и длинное слово
   («ИТ-инфраструктуры») реже остаётся на строке одно. */
@media (max-width: 600px) {
  .client[data-case] .case,
  .client[data-case]:nth-child(n) .case {
    left: 0;
    right: 0;
    width: auto;
    transform: none;
    padding: .75rem .875rem .875rem;
  }
  .case__text { font-size: .75rem; }
}

/* --------------------------------------------------------------------------
   14 · КНОПКИ
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: .625rem;
  padding: .8rem 1.35rem;
  border: 1px solid var(--c-ink);
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color .12s linear, color .12s linear, border-color .12s linear;
}
.btn--primary { background: var(--c-ink); color: var(--c-bg); }
.btn--primary:hover { background: var(--c-ink-2); border-color: var(--c-ink-2); }
.btn--ghost { background: transparent; color: var(--c-ink); border-color: var(--c-edge); }
.btn--ghost:hover { border-color: var(--c-ink); background: var(--c-surface); }

/* --------------------------------------------------------------------------
   15 · КОНТАКТЫ
   -------------------------------------------------------------------------- */
.contact__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}
.contact__lead { max-width: 40ch; color: var(--c-ink-2); }
/* Кнопок «Позвонить» и «Написать в Telegram» здесь больше нет (решение
   владельца 07.10.2026): список контактов рядом и так понятен, ссылки в
   нём те же. Вместо кнопок — приглашение к списку, темнее подводки: на
   широком экране «…справа», в одну колонку — «…ниже». */
.contact__pick { max-width: 40ch; margin-top: 1rem; color: var(--c-ink); }
.contact__narrow { display: none; }

/* Список без линеек — ни над ним, ни между строками: строки разведены
   одним просветом. Поля внутри строк больше не нужны — они отделяли текст
   от линеек. Без верхней линейки и поля первая строка встаёт вровень
   с первой строкой текста слева. */
.contact__list { display: grid; gap: 1rem; }
/* Строка — иконка и значение вплотную, на одной линии. Подписей
   («Телефон», «Telegram»…) на экране нет: иконка говорит то же самое,
   а подпись уводила значение в отдельную колонку, на телефоне — на строку
   ниже, и иконка стояла не на уровне того, к чему относится. Для экранного
   диктора подписи остались в разметке, под .u-sr.

   Центровка, а не базовая линия: у иконки базовой линии нет, и по ней она
   садилась бы на строку целиком, торча вверх. Центр строки шрифта лежит
   почти точно посередине высоты прописных и цифр — расхождение меньше
   пикселя, — поэтому диск встаёт ровно против текста. */
.contact__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .625rem;
  align-items: center;
  /* Кегль задаём строке, а не ссылке: высоту строки текста задаёт кегль
     самого dd, и при мелкой ссылке внутри крупного dd строки со ссылками
     выходили на 4 px выше строк с простым текстом.
     13 px, а не --fs-small: список по просьбе владельца мельче и плотнее
     остального текста, и подъём --fs-small до 14 px его не касается. */
  font-size: 0.8125rem;
  position: relative;   /* опора для зоны нажатия ссылки */
}
/* В dt осталась одна иконка. Флекс нужен, чтобы картинка не была строчной:
   строчная оставляет под собой зазор под выносные, dt выходит выше иконки
   и сбивает центровку. */
.contact__key { display: flex; }
/* Список набран мельче основного текста (кегль — у .contact__row): это
   справка, а не абзац. Иконка уменьшена в той же пропорции — около двух
   высот прописных. Исходники 96 px, показываются на 18. */
.contact__icon { flex: none; width: 18px; height: 18px; }
.contact__val { color: var(--c-ink); }

/* Подчёркиваний у ссылок нет: что это ссылки, и так ясно — номер, ник,
   «Написать в MAX» рядом со значком мессенджера. На наведении — только
   цвет. */
a.contact__val { transition: color .12s linear; }
a.contact__val:hover { color: var(--c-ink-3); }
/* Сама ссылка — строчка в 17 px, по ней трудно попасть пальцем. Зону
   нажатия растягиваем на всю строку — вместе с иконкой и половиной
   просвета сверху и снизу (просвет списка — 1rem). Соседние зоны
   стыкуются, но не налезают; вид списка не меняется. */
a.contact__val::after { content: ''; position: absolute; inset: -.5rem 0; }

/* Телефон в разметке разложен на куски: цельной строки с номером там нет,
   и дешёвая регулярка сборщика через границу атрибута («+7 988" data-p2=
   "143») не перепрыгнет — разделителей между группами цифр она допускает
   два-три знака, а тут их десяток.
   Обычно номер вписывает текстом main.js — чтобы его можно было выделить и
   скопировать. Это правило — запасное: JS может не отработать (облегчённые
   режимы мобильных браузеров, устаревший кэш, ошибка), а номер должен быть
   виден всегда. Пока ссылка пуста, номер рисуем отсюда; вписал скрипт
   текст — :empty больше не срабатывает, и рисунок гаснет. */
.contact__tel:empty::before {
  content: attr(data-p1) " " attr(data-p2) "-" attr(data-p3);
}

@media (max-width: 900px) {
  .contact__grid { grid-template-columns: 1fr; }
  .contact__wide { display: none; }
  .contact__narrow { display: inline; }
}

/* --------------------------------------------------------------------------
   16 · ПОДВАЛ
   -------------------------------------------------------------------------- */
.footer { border-top: 1px solid var(--c-ink); }
.footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.5rem;
  padding-block: 1.5rem;
  font-size: var(--fs-micro);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-ink-4);
}
.footer__mark { display: block; width: 24px; height: 24px; }
.footer__spacer { margin-left: auto; }

@media (max-width: 600px) {
  .footer__spacer { margin-left: 0; }
}
