/* Перевёрстка главной study.it-ontime.ru — прототип.

   Цель: тот же дизайн, но обычной вёрсткой вместо Zero-блоков Тильды.
   У оригинала каждый элемент — абсолютно позиционированный div с
   координатами из data-атрибутов; отсюда 5 276 узлов DOM, 198 картинок
   и ~10 секунд загрузки на телефоне.

   Размеры и цвета не придуманы, а сняты с боевой страницы через
   getComputedStyle: контейнер 1221 px, карточки 391 с зазором 24,
   кнопки высотой 41 и радиусом 8, заголовок PPNeueMachina 50/50 с
   letter-spacing -1.5. Расхождения проверяются скриншотами. */

/* Шрифты свои, а не с CDN Тильды: у оригинала они грузятся оттуда, то есть
   боевой сайт до сих пор зависит от Тильды в отрисовке — отвалится их CDN,
   поедут шрифты. Лицензия на PP Neue Machina подтверждена владельцем
   04.08.2026, поэтому файлы лежат рядом и в репозитории. */
@font-face {
  font-family: 'PPNeueMachina';
  src: url('/fonts/PPNeueMachina-Light_.woff2') format('woff2');
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: 'PPNeueMachina';
  src: url('/fonts/PPNeueMachina-Regula.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'PPNeueMachina';
  src: url('/fonts/PPNeueMachina-Ultrab.woff2') format('woff2');
  font-weight: 800;
  font-display: swap;
}
/* Кнопки набраны НЕ PP Neue Machina. У Тильды в стопке зарегистрирован
   «PPNeueMachina» с font-weight: 500, а по этому весу подставлен совсем
   другой шрифт — Pitagon Sans Mono Medium. Отсюда моноширинный вид
   «{ Подробнее }», который иначе не повторить: при 500 без этого файла
   браузер берёт Regular, и надпись выходит не той гарнитурой.

   Лицензия: права на PP Neue Machina владелец подтвердил 04.08.2026, на
   Pitagon Sans Mono — 05.08.2026. Оба файла раздаём со своего сервера. */
@font-face {
  font-family: 'PitagonSansMono';
  src: url('/fonts/PitagonSansMono-Medi.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'RobotoFlex';
  src: url('/fonts/RobotoFlex_slnt_wdth.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
}

:root {
  --bg: #1e1e1e;
  --card: #2d2d2d;
  --blue: #0088ff;
  --lime: #c1f742;
  --red: #f02522;
  --violet: #6c63ff;
  --ink: #0a0f17;
  --muted: #c4c6cf;
  --container: 1221px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: #fff;
  font-family: 'RobotoFlex', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; }
a { text-decoration: none; color: inherit; }

.wrap {
  width: var(--container);
  max-width: calc(100% - 40px);
  margin: 0 auto;
  position: relative;
}

/* ── Шапка ───────────────────────────────────────────────────────────
   В оригинале она абсолютным слоем лежит поверх первого экрана
   (высота блока — 1 px), поэтому фон героя начинается под ней. */
.hdr {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 20;
  padding-top: 32px;
}
.hdr .wrap { display: flex; align-items: center; gap: 24px; }
.hdr__logo { width: 185px; height: 40px; object-fit: contain; }

.nav {
  display: flex;
  align-items: center;
  gap: 28px;
  margin-left: 39px;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 16px;
  font-weight: 300;
  line-height: 25px;
}
.nav__all {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 8px 18px;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 8px;
}
.nav__burger { display: block; width: 16px; height: 12px; position: relative; }
.nav__burger::before,
.nav__burger::after,
.nav__burger span {
  content: '';
  position: absolute;
  left: 0;
  width: 16px;
  height: 2px;
  background: #fff;
  border-radius: 2px;
}
.nav__burger::before { top: 0; }
.nav__burger span { top: 5px; }
.nav__burger::after { top: 10px; }

/* Выпадающие списки. В оригинале это те же зеро-элементы, только уведённые
   за экран; панель со скруглением 10, «Все курсы» на белом, остальные на
   #2d2d2d. Открываются по наведению и по фокусу с клавиатуры — фокус важен,
   иначе с клавиатуры до пунктов не добраться вовсе. */
.nav__item { position: relative; display: flex; align-items: center; }
.nav__drop {
  position: absolute;
  top: 100%;
  left: -13px;
  z-index: 5;
  display: none;
  flex-direction: column;
  min-width: 304px;
  gap: 7px;
  margin-top: 12px;
  padding: 13px;
  border-radius: 10px;
  background: var(--card);
}
.nav__drop--light { background: #fff; }
.nav__drop a {
  display: flex;
  align-items: center;
  height: 47px;
  padding: 0 13px;
  border-radius: 6px;
  font-size: 16px;
  font-weight: 400;
  line-height: 25px;
  white-space: nowrap;
}
.nav__drop--light a { color: var(--ink); }
.nav__drop a:hover { background: rgba(255, 255, 255, .08); }
.nav__drop--light a:hover { background: rgba(10, 15, 23, .07); }
.nav__item:hover .nav__drop,
.nav__item:focus-within .nav__drop { display: flex; }

.hdr__right { margin-left: auto; display: flex; align-items: center; gap: 26px; }
.hdr__login {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 18px;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 8px;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 16px;
  font-weight: 300;
  line-height: 25px;
}
/* Это <button>, а не ссылка: она открывает форму, а не ведёт по адресу.
   Отсюда сброс кнопочных умолчаний — выглядеть должна как прежняя ссылка. */
.hdr__fb {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--lime);
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 16px;
  font-weight: 300;
  line-height: 25px;
  cursor: pointer;
}

/* ── Первый экран ────────────────────────────────────────────────── */
.hero {
  position: relative;
  background: var(--bg);
  overflow: hidden;
  padding: 173px 0 100px;
}
.hero .wrap { z-index: 2; }

/* Свечение за заголовком и три предметных картинки. Позиции — от левого
   края контейнера, ровно как в оригинале (сняты с боевой страницы). */
.hero__deco { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.hero__deco .wrap { height: 100%; }
.hero__deco img { position: absolute; }
.deco--glow { left: 75px; top: 18px; width: 1072px; height: 592px; }
.deco--zabbix { left: -22px; top: 249px; width: 264px; height: 264px; }
.deco--win { left: 940px; top: 69px; width: 280px; height: 280px; }
.deco--linux { left: 863px; top: 392px; width: 300px; height: 296px; }

.hero__title {
  margin: 0 auto;
  /* Ширина не декоративная: на 810 px заголовок ломается на те же четыре
     строки, что в оригинале. Шире — «с нуля до уровня» уезжает на третью. */
  max-width: 810px;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 50px;
  font-weight: 400;
  line-height: 50px;
  letter-spacing: -1.5px;
  text-align: center;
}
.hero__lead {
  margin: 44px auto 0;
  max-width: 664px;
  font-size: 22px;
  font-weight: 400;
  line-height: 33px;
  letter-spacing: -.5px;
  text-align: center;
}

.chips {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 52px;
  flex-wrap: wrap;
}
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 41px;
  padding: 0 26px;
  border-radius: 8px;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 20px;
  font-weight: 400;
  line-height: 30px;
  letter-spacing: -.5px;
  white-space: nowrap;
}
.chip--blue { background: var(--blue); color: #fff; }
.chip--white { background: #fff; color: var(--ink); }
.chip--lime { background: var(--lime); color: var(--ink); }
.chip--red { background: var(--red); color: #fff; }
.chip--violet { background: var(--violet); color: #fff; }

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  margin-top: 102px;
}
.stat {
  background: var(--card);
  border-radius: 20px;
  /* Низ 44, а не 36: в оригинале подпись отстоит от нижнего края карточки
     именно на столько, и с равными отступами она садилась на 8 px ниже. */
  padding: 36px 30px 44px;
  min-height: 226px;
  display: flex;
  flex-direction: column;
}
.stat__n {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 40px;
  font-weight: 400;
  line-height: 40px;
  letter-spacing: -1.5px;
}
.stat__n img { width: 43px; height: 41px; }
.stat__t {
  margin-top: auto;
  font-size: 18px;
  font-weight: 300;
  line-height: 27px;
  letter-spacing: -.5px;
}

/* ── Мобильная раскладка ─────────────────────────────────────────── */
@media (max-width: 1260px) {
  .hero__deco { display: none; }
}
/* Мобильная раскладка не повторяет оригинал поэлементно: у Тильды под 360 px
   свои координаты на каждый элемент (заголовок кегль 25 и ширина 333, кнопка
   курса 181×35), здесь обычный перенос в одну колонку. Результат тот же по
   смыслу, но переживает изменение длины текста.

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

@media (max-width: 900px) {
  .hdr__login { display: none; }
  .hdr { padding-top: 20px; }
  .hero { padding: 104px 0 56px; }
  .hero__title {
    font-size: 28px;
    line-height: 32px;
    letter-spacing: -.8px;
    max-width: 100%;
  }
  .hero__title br { display: none; }
  .hero__lead { font-size: 17px; line-height: 25px; margin-top: 24px; }
  .chips { margin-top: 28px; gap: 8px; }
  .chip { height: 36px; font-size: 15px; padding: 0 16px; }
  .stats { grid-template-columns: 1fr; gap: 16px; margin-top: 40px; }
  .stat { min-height: 0; padding: 24px 22px; }
  .stat__n { font-size: 32px; line-height: 32px; }
  .stat__t { margin-top: 16px; font-size: 16px; line-height: 24px; }
}

/* Бургер — только на узких экранах; на десктопе меню развёрнуто. */
.hdr__menu {
  display: none;
  margin-left: auto;
  order: 3;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 8px;
  background: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
}
.hdr__menu span { width: 16px; height: 2px; background: #fff; border-radius: 2px; }

/* Меню прячется в бургер раньше остальной мобильной раскладки: строка из
   пяти пунктов, «Войти» и «Обратной связи» перестаёт помещаться уже около
   1200, и пункты начинают переноситься в две строки. */
@media (max-width: 1200px) {
  .nav { display: none; }
  .hdr__menu { display: inline-flex; }
  /* Бургер раскрывает то же меню списком: выпадашки по наведению на
     телефоне не работают в принципе, поэтому оба уровня показываем сразу. */
  .hdr.is-open .nav {
    display: flex;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 30;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin: 14px 0 0;
    padding: 8px;
    border-radius: 10px;
    background: var(--card);
  }
  .hdr.is-open .nav > a,
  .hdr.is-open .nav__item > a {
    display: flex;
    align-items: center;
    min-height: 48px;
    padding: 0 12px;
  }
  .hdr.is-open .nav__all { border: 0; }
  .hdr.is-open .nav__item { display: block; position: static; }
  .hdr.is-open .nav__drop,
  .hdr.is-open .nav__item:hover .nav__drop {
    display: flex;
    position: static;
    min-width: 0;
    margin: 0 0 8px;
    padding: 0 0 0 24px;
    background: none;
  }
  .hdr.is-open .nav__drop a {
    height: auto;
    min-height: 40px;
    padding: 6px 12px;
    font-size: 15px;
    line-height: 20px;
    white-space: normal;
    color: var(--muted);
  }
}
@media (max-width: 900px) {
  .hdr__fb span, .hdr__fb { font-size: 0; }
  .hdr__fb img { width: 26px; height: 26px; }
}

/* Показ бургера — последним правилом: базовый `display:none` объявлен ниже
   медиазапроса выше по файлу и иначе перебивает его. */
@media (max-width: 900px) {
  .hdr__menu { display: inline-flex; }
}

/* ── Секция «О школе» ─────────────────────────────────────────────────
   Три ряда карточек. Сетка сверена с оригиналом: колонка 599, зазор 24,
   внутренний отступ карточки 41 слева и 35–39 сверху, радиус 20.
   Высота секции у оригинала 1488 px. */
/* Сверху 12, а не 100: у оригинала заголовок секции стоит вплотную к её
   верхней границе, а зазор между экранами даёт нижний отступ первого. */
.about { background: var(--bg); padding: 12px 0 91px; }

.sec-title {
  margin: 0 0 46px;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 50px;
  font-weight: 400;
  line-height: 50px;
  letter-spacing: -1.5px;
  text-align: center;
}

.about__row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.about__row + .about__row { margin-top: 24px; }

.card { border-radius: 20px; overflow: hidden; }
.card--dark { background: var(--card); }
.card--light { background: #fff; }
.card--blue { background: var(--blue); }

/* Подсветка кусков текста — у Тильды это отдельные элементы с заливкой,
   здесь обычный <mark>: так текст остаётся единым и переносится сам. */
.mk {
  padding: 0 .12em;
  border-radius: 3px;
  color: #fff;
  background: var(--blue);
}
.mk--lime { background: var(--lime); color: var(--ink); }

.about__intro { padding: 35px 41px 41px; }
.about__lead {
  margin: 0;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 30px;
  font-weight: 400;
  line-height: 36px;
  letter-spacing: -.5px;
}
.about__lead--sm { font-size: 24px; line-height: 31px; }
.about__note {
  margin: 36px 0 0;
  max-width: 408px;
  font-size: 20px;
  font-weight: 300;
  line-height: 30px;
  letter-spacing: -.5px;
}
.about__photo { width: 100%; height: 449px; object-fit: cover; border-radius: 20px; }

.about__clients { padding: 39px 33px; min-height: 276px; display: flex; flex-direction: column; }
.about__h {
  margin: 0;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 30px;
  font-weight: 400;
  line-height: 36px;
  letter-spacing: -.5px;
}
.about__clients img { margin-top: auto; width: 100%; max-width: 517px; height: auto; }
.about__origin { padding: 36px 41px; min-height: 276px; display: flex; align-items: flex-start; }

/* Документы. Картинка здесь — не иллюстрация внизу карточки, а вся карточка
   целиком: файл 1176×1014 это ровно 2× от 599×516, то есть синий фон и скан
   нарисованы в нём вместе. Поэтому она кладётся фоном, а текст поверх. */
.about__doc { position: relative; }
.about__doc-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.about__doc-txt { position: relative; padding: 36px 41px; }
.about__h--doc { max-width: 470px; }
.about__num {
  margin: 16px 0 0;
  font-size: 20px;
  font-weight: 400;
  line-height: 24px;
  color: #bfe1ff;
}
.about__doc--license { height: 516px; }
.about__doc--accred { height: 354px; align-self: start; }

@media (max-width: 900px) {
  .about { padding: 8px 0 48px; }
  .sec-title { font-size: 28px; line-height: 32px; letter-spacing: -.8px; margin-bottom: 28px; }
  .about__row { grid-template-columns: 1fr; gap: 16px; }
  .about__row + .about__row { margin-top: 16px; }
  .about__intro, .about__origin { padding: 24px 22px; }
  .about__clients { padding: 24px 22px; }
  .about__lead { font-size: 22px; line-height: 28px; }
  .about__lead--sm { font-size: 19px; line-height: 26px; }
  .about__note { margin-top: 20px; font-size: 16px; line-height: 24px; max-width: 100%; }
  .about__h { font-size: 22px; line-height: 28px; }
  .about__photo { height: 240px; }
  .about__clients img { margin-top: 20px; }
  .about__doc--license, .about__doc--accred { height: auto; aspect-ratio: 599 / 516; }
  .about__doc--accred { aspect-ratio: 599 / 354; }
  .about__doc-txt { padding: 24px 22px; }
  .about__clients, .about__origin { min-height: 0; }
}

/* ── Секция «Продукты школы» ──────────────────────────────────────────
   Сверено с оригиналом: широкая карточка 1222 с радиусом 30, внутренний
   отступ 30, медиа 538 шириной; колоночные карточки 601 с зазором 20;
   кнопка высотой 71 и радиусом 10; плашки-теги 14 px. */
/* Снизу почти ничего: в оригинале карточки кончаются за 5 px до конца
   блока, а весь зазор до «Отзывов» — это верхний отступ их шапки (93.6).
   Раньше здесь стояло 148, и разрыв выходил 241 вместо 99 — это и был
   «в оригинале отступ меньше». */
.products { background: var(--bg); padding: 0 0 5px; }

.sec-title--left { text-align: left; margin-bottom: 0; }
/* «Стартовые продукты» и «Обучающие программы для сисадминов» набраны
   RobotoFlex, а не Machina, — в отличие от заголовков секций. Выглядит
   как недосмотр автора макета, но повторяем как есть. */
.sub-title {
  margin: 31px 0 51px;
  font-size: 30px;
  font-weight: 400;
  line-height: 30px;
  letter-spacing: -1px;
}
.sub-title--mt { margin-top: 102px; }

.prod { background: var(--card); border-radius: 30px; padding: 30px 30px 41px; }
/* Только между широкими карточками. Раньше правило было на любых .prod
   и протекало в сетку курсов: карточки получали margin-top и переставали
   тянуться до высоты ряда — соседи в ряду расходились на те самые 20 px. */
.prod--wide + .prod--wide { margin-top: 20px; }
/* stretch, а не start: в оригинале низ кнопки совпадает с низом медиа,
   а при start колонка с текстом короче и кнопка повисает в воздухе. */
.prod--wide { display: grid; grid-template-columns: 1fr 538px; gap: 24px; align-items: stretch; }
.prod__body { display: flex; flex-direction: column; }

/* Плашки над заголовком карточки: 35.6 в высоту, полная скругление,
   RobotoFlex 14/21 с letter-spacing -0.5 — всё с оригинала.

   Одно отличие намеренное. У оригинала нейтральная плашка залита ровно
   тем же rgb(45,45,45), что и сама карточка, то есть подложки не видно
   вовсе — виден только белый текст. Похоже на недосмотр, поэтому здесь
   она чуть светлее фона. Если это не недосмотр, а так и задумано, —
   поменяйте background на var(--card), остальное совпадает. */
.tags { display: flex; flex-wrap: wrap; gap: 10px; }
.tag {
  display: inline-flex;
  align-items: center;
  height: 35.6px;
  padding: 0 20px;
  border-radius: 100px;
  background: rgba(255, 255, 255, .08);
  font-size: 14px;
  font-weight: 400;
  line-height: 21px;
  letter-spacing: -.5px;
  white-space: nowrap;
}
.tag--light { background: #fff; color: var(--ink); }

.prod__h {
  margin: 24px 0 0;
  max-width: 540px;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 35px;
  font-weight: 400;
  line-height: 42px;
  letter-spacing: -1px;
}
/* Описания карточек в оригинале белые и нормального начертания (400), а
   не приглушённые светлые, как я поставил сначала. Проверено по всем
   шести описаниям продуктов разом: RobotoFlex 20/30/400, #fff. */
.prod__d {
  margin: 25.5px 0 0;
  max-width: 540px;
  font-size: 20px;
  font-weight: 400;
  line-height: 30px;
  letter-spacing: -.5px;
  color: #fff;
}

/* Все кнопки оригинала набраны Pitagon Sans Mono Medium (см. @font-face
   выше) с letter-spacing -1 и интерлиньяжем 1.55 — проверено по всем
   восьми: «{ Подробнее }», «{ Получить бесплатно }», «{ Подписаться на
   канал }» и остальным. Больше эта гарнитура нигде на странице не
   встречается: только кнопки.

   Здесь до этого стоял RobotoFlex 300 из body. Сверка это исправно
   показывала («шрифт: ppneuemachina → robotoflex» на всех кнопках) —
   просто до правок дошли не все её строки. Отчёт надо дочитывать до
   конца, а не до первого знакомого пункта. */
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: auto;
  min-height: 71px;
  padding: 12px 24px;
  border-radius: 10px;
  background: var(--blue);
  color: #fff;
  font-family: 'PitagonSansMono', ui-monospace, monospace;
  font-size: 18px;
  font-weight: 500;
  line-height: 1.55;
  letter-spacing: -1px;
  text-align: center;
}
.btn--sm { min-height: 61px; font-size: 14px; }
/* «Курс в разработке» — не кнопка вовсе: в оригинале это белая надпись без
   заливки. Кликать там нечего. */
.btn--ghost {
  background: none;
  color: #fff;
  cursor: default;
}

.prod__media { width: 100%; border-radius: 20px; object-fit: cover; }
/* Картинка широкой карточки тянется на всю её внутреннюю высоту, а не
   стоит фиксированной: в оригинале обе карточки так и устроены — 376.7 у
   первой и 419.4 у второй, ровно высота карточки минус отступы 30.5/40.7.
   Высоту задаёт левая колонка с текстом, картинка подстраивается.

   Отсюда обёртка вокруг картинки. Сам <img> так не растянуть: у замещаемых
   элементов height:100% в строке грид-сетки неопределённой высоты
   откатывается к собственным пропорциям файла — карточка тогда раздувается
   под картинку, а не наоборот. Проверено: без обёртки вторая карточка
   выходила 567 вместо 491. */
.prod--wide .prod__media { height: 100%; min-height: 377px; }
.prod--wide .prod__media--fit { position: relative; overflow: hidden; }
.prod--wide .prod__media--fit img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Внутренние отступы левой колонки — с оригинала: 25.5 до заголовка и
   40.8 от описания до кнопки. У узких карточек курсов свои числа. */
.prod--wide .tags + .prod__h { margin-top: 25.5px; }
.prod--wide .prod__d { margin-bottom: 40.8px; }
/* Карточка MikroTik: синяя заливка, поверх — вырезанный роутер. Кадр как в
   оригинале — картинка шире окна на 21.7 % и прижата к верху со сдвигом
   влево, поэтому не object-fit, а абсолютная картинка в обёртке. */
.prod__media--tint {
  position: relative;
  overflow: hidden;
  background: var(--blue);
}
.prod__media--tint img {
  position: absolute;
  top: 0;
  left: -4.69%;
  width: 121.732%;
  max-width: none; /* иначе общий сброс img обрежет ширину до 100% */
  height: auto;
}

/* Видео мини-курса. Это не плеер: в оригинале здесь зацикленный ролик без
   звука с автозапуском, поэтому и кнопки воспроизведения быть не должно.
   Сначала я принял его за ленивый ютуб и поставил заглушку с треугольником —
   она, разумеется, никуда не вела.

   Файл пережат под реальный размер показа: исходник 1920×1080 на 2178 КБ
   отдавался в окно 528×370. Тот же ролик в 1056 по ширине — 144 КБ. */
.prod__media--video {
  background: #000;
  border-radius: 15px;
  height: 377px;
  object-fit: cover;
}

/* Материалы: картинка слева, текст и кнопка справа. */
.mats { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px; }
.mat {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 31px;
  align-items: stretch;
  background: var(--card);
  border-radius: 30px;
  padding: 24px 30px;
  min-height: 355px;
}
/* Высота задана числом, а не 100%: у замещаемых элементов проценты в строке
   грид-сетки неопределённой высоты откатываются к пропорциям файла, и
   карточка раздувается под картинку (было 362 вместо 355). 307 — это
   355.3 оригинала минус отступы 24 сверху и снизу. */
.mat > img {
  width: 100%; height: 307px;
  object-fit: contain;
  background: #f5f5f8;
  border-radius: 10px;
  padding: 24px;
}
.mat__body { display: flex; flex-direction: column; }
.mat__h {
  margin: 5px 0 0;
  /* RobotoFlex, а не PPNeueMachina: сверка слепков показала, что у
     оригинала заголовки материалов набраны текстовым шрифтом. */
  font-size: 24px;
  font-weight: 400;
  line-height: 34px;
  letter-spacing: -.5px;
}

.courses { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.prod--col { display: flex; flex-direction: column; }
.prod--col .prod__media { height: 356px; margin-bottom: 21px; }
.prod--col .prod__body { flex: 1; }
/* У колоночных карточек кнопка НЕ прижимается к низу: в оригинале до неё
   ровно 51 от описания, и высота карточки складывается из отступов.
   Выравнивание соседей по ряду делает сама сетка. */
.prod--col .prod__d { margin-bottom: 51px; }
.prod--col .btn { margin-top: 0; }
.prod--col .tags + .prod__h { margin-top: 24px; }

@media (max-width: 900px) {
  .products { padding: 0 0 56px; }
  .sub-title { font-size: 22px; line-height: 28px; margin: 20px 0 20px; }
  .sub-title--mt { margin-top: 44px; }
  .prod { padding: 20px; border-radius: 20px; }
  .prod--wide { grid-template-columns: 1fr; gap: 20px; }
  .prod--wide .prod__media, .prod__media--video { min-height: 200px; height: 200px; }
  .prod__h { font-size: 24px; line-height: 30px; margin-top: 18px; }
  .prod__d { font-size: 16px; line-height: 24px; margin-top: 14px; }
  .btn { min-height: 56px; font-size: 16px; margin-top: 20px; }
  .btn--sm { min-height: 50px; }
  .mats, .courses { grid-template-columns: 1fr; gap: 16px; }
  .mat { grid-template-columns: 120px 1fr; gap: 18px; padding: 20px; min-height: 0; border-radius: 20px; }
  .mat > img { padding: 12px; height: auto; align-self: stretch; }
  .mat__h { font-size: 18px; line-height: 24px; }
  .prod--col .prod__media { height: 200px; margin-bottom: 18px; }
  /* Отступ 51 под описанием — из десктопного макета; на узком экране он
     оставляет дыру между текстом и кнопкой, здесь хватает margin у .btn. */
  .prod--col .prod__d { margin-bottom: 0; }
  .tag { height: 32px; padding: 0 16px; }
}

/* ── Отзывы ──────────────────────────────────────────────────────────
   У оригинала это слайдер на плагине Тильды: две светлые карточки в кадре
   из четырнадцати, счётчик и стрелки. Здесь — обычная горизонтальная
   прокрутка со snap: листается пальцем, стрелками и с клавиатуры, без
   60 КБ чужого плагина. */
/* Шапка отзывов повторяет блок оригинала целиком: он ровно 239 высотой,
   заголовок начинается на 93.6 и переносится в две строки (ширина 470.3),
   счётчик стоит на второй строке, стрелки прижаты к правому краю сетки.
   Счётчик и стрелки позиционированы абсолютно, потому что у оригинала это
   свободно висящие элементы, а не соседи заголовка в строке; по горизонтали
   счётчик задан долей, чтобы не отрывался от заголовка на узком экране.

   Снизу у секции отступа нет: в оригинале сразу под лентой начинается блок
   Яндекс.Карт со своим отступом 100. */
.reviews { background: var(--bg); padding: 0; }
.reviews__top { position: relative; padding: 93.6px 0 45.4px; }
.reviews__h { max-width: 470.3px; margin: 0; }
.reviews__nav { position: absolute; right: 0; bottom: 49.6px; display: flex; gap: 15.2px; }
/* Счётчик в оригинале набран Gilroy — единственным местом на всей
   странице, и явно по недосмотру: шрифт на элементе просто не задан, а
   Gilroy у Тильды стоит по умолчанию. Тащить ради двух цифр ещё два
   файла шрифта не стоит, берём RobotoFlex — он уже загружен и по
   рисунку ближе к Gilroy, чем угловатая Machina. Расхождение осознанное.
   Разнобой в начертании (цифра 400, «/ 14» 500) у оригинала тоже есть. */
.reviews__count {
  position: absolute;
  left: 31.26%;
  bottom: 42.5px;
  font-size: 24px;
  font-weight: 500;
  line-height: 21.6px;
}
.reviews__count b { font-weight: 400; }
.rv-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 61.1px; height: 61.1px;
  border: 0;
  border-radius: 5px;
  background: var(--lime);
  color: var(--ink);
  cursor: pointer;
}
.rv-arrow svg { width: 16.3px; height: 28.5px; display: block; }
.rv-arrow:hover { opacity: .85; }

/* Лента вылезает из контейнера вправо до края экрана: в оригинале артборд
   во всю ширину окна, и справа всегда виден кусок следующей карточки —
   по нему и понятно, что лента листается. Клип на секции нужен, чтобы
   вылет не породил горизонтальную прокрутку всей страницы. */
.reviews { overflow-x: clip; }
.rv-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 599px;
  gap: 24px;
  margin-right: calc(-50vw + 50%);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  align-items: start;
  padding-bottom: 4px;
}
.rv-track::-webkit-scrollbar { display: none; }

/* Карточка отзыва. Сверено с оригиналом: светлая подложка #F5F5F8 радиусом
   20, внутренний отступ 31 (снизу 41), аватар 153 кругом, скриншот
   переписки во всю ширину колонки (538), должность — лаймовая плашка внизу.
   Числа взяты из макета Тильды (30/40 при сетке 1200) и умножены на 1.0175 —
   во столько раз наш контейнер 1221 шире её сетки.

   Высота у слайдов разная, потому что скриншоты разной длины: выравниваем
   по верху (align-items: start), а не растягиваем до самого длинного. Полоса
   при этом всегда высотой с самый длинный отзыв — ровно как артборд
   оригинала (1590). Поэтому порядок важен: первым идёт Дмитрий Попов, он же
   самый длинный, и на первом экране пустоты под карточкой нет.

   Порядок отзывов взят из макета по координате left, а не из порядка в DOM:
   у Тильды они лежат в разметке задом наперёд, и если верстать по DOM —
   лента разворачивается в обратную сторону. */
.rv {
  scroll-snap-align: start;
  margin: 0;
  padding: 31px 31px 41px;
  background: #f5f5f8;
  color: var(--ink);
  border-radius: 20px;
  display: flex;
  flex-direction: column;
}
.rv__top { display: flex; align-items: center; gap: 30px; }
.rv__ava { width: 153px; height: 153px; border-radius: 50%; object-fit: cover; flex: none; }
.rv__name {
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 25px;
  font-weight: 400;
  line-height: 35px;
  letter-spacing: -.5px;
}

/* Цитата подсвечена синим — это не фон карточки, а заливка самого текста,
   поэтому подсветка обтекает строки, а не рисует прямоугольник на всю
   ширину. Отсюда span внутри blockquote и box-decoration-break. */
.rv__q { margin: 30px 0 0; }
.rv__q span {
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 30px;
  font-weight: 400;
  line-height: 36px;
  letter-spacing: -.5px;
  color: #fff;
  background: var(--blue);
  padding: 2px 4px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.rv__shot { width: 100%; height: auto; margin: 41px 0 0; border-radius: 10px; }
.rv__role {
  align-self: flex-start;
  margin: 30px 0 0;
  padding: 5px 8px;
  border-radius: 3px;
  background: var(--lime);
  color: var(--ink);
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 20px;
  font-weight: 400;
  line-height: 20px;
  letter-spacing: -.5px;
}

/* ── Отзывы с Яндекс.Карт ─────────────────────────────────────────
   Блок из оригинала (rec1980590571). Раньше я его пропустил, посчитав
   пустым: в разметке это «html-элемент», внутри которого один iframe, и
   обмер по тексту с картинками ничего не находит. Пустым он не был.

   Размеры сняты с оригинала: секция 900, само окно 560×700 по центру,
   отступ сверху 100. Оно не тянется с окном (у Тильды upscale="grid"),
   поэтому числа тут абсолютные, а не умноженные на 1.0175, как в
   остальной странице. */
.ymaps { background: var(--bg); padding: 100px 0; }
.ymaps__box {
  width: 560px;
  max-width: calc(100% - 40px);
  height: 700px;
  margin: 0 auto;
  overflow-y: auto;
  border-radius: 12px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ymaps__box::-webkit-scrollbar { display: none; }
.ymaps__box iframe {
  display: block;
  width: 100%;
  height: 1200px;
  border: 1px solid #e6e6e6;
  border-radius: 12px;
}

/* ── Соцсети ─────────────────────────────────────────────────────────
   Обмер оригинала при ширине 1425 (там сетка совпадает с нашим
   контейнером 1221): заголовок 491.7 в две строки, карточка ютуба
   787.9×386.8, телеграма 413.3×386.8, зазор 20.4, скруглание 30,
   внутренний отступ 30.5. Всё, что по горизонтали, задано в процентах
   от карточки — иначе на 1000 px, где контейнер уже сжат, абсолютные
   координаты разъехались бы. */
.socials { background: var(--bg); padding: 0 0 101.5px; }
.soc__title { max-width: 491.7px; margin: 0 0 52.7px; }
.soc {
  display: grid;
  grid-template-columns: 787.9fr 413.3fr;
  gap: 20.4px;
  align-items: start;
}
.soc__card {
  position: relative;
  min-height: 386.8px;
  padding: 30.5px 30.5px 40.7px;
  border-radius: 30px;
  background: var(--card);
  display: flex;
  flex-direction: column;
}
.soc__t {
  margin: 0;
  font-family: 'PPNeueMachina', sans-serif;
  font-size: 30px;
  font-weight: 400;
  line-height: 36px;
  letter-spacing: -1px;
}
/* Проценты у текста и кнопки считаются от содержимого карточки (726.9),
   а у logo/clips — от её падинг-бокса (787.9): они позиционированы
   абсолютно. Разные базы, поэтому и доли разные при одинаковой на вид
   ширине. */
.soc__card .btn { width: 48.48%; margin-top: auto; }

/* Ютуб. Объёмный логотип в оригинале лежит поверх текста, а не рядом с
   ним, — отсюда абсолютное позиционирование, флексом такое не собрать. */
.soc__card--yt .soc__t { max-width: 44.55%; }
.soc__logo {
  position: absolute;
  top: 23.4px;
  left: 36.44%;
  width: 108.9px;
  height: 108.9px;
}
.soc__clips {
  position: absolute;
  top: 30.5px;
  right: 30.5px;
  width: 34.89%;
  display: grid;
  gap: 10.2px;
}
.soc__clip { position: relative; display: block; }
/* Превью 16:9, а окошко в оригинале 270×150 — это 1.8. Разница в полтора
   пикселя по высоте, но она копится на двух рядах, поэтому кадрируем
   по месту, а не растягиваем. */
.soc__clip img {
  display: block;
  width: 100%;
  aspect-ratio: 274.9 / 152.7;
  height: auto;
  object-fit: cover;
  border-radius: 10px;
}
/* Кружок «плей». В оригинале это кнопка без текста с треугольником
   внутри; рисуем треугольник рамками, чтобы не тащить ещё одну картинку. */
.soc__play {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 50.9px;
  height: 50.9px;
  margin: -25.45px 0 0 -25.45px;
  border-radius: 50%;
  background: #fff;
}
.soc__play::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -9px 0 0 -6px;
  border: 9px solid transparent;
  border-left: 14px solid var(--ink);
  border-right: 0;
}

/* Телеграм. Бледный самолётик — фон карточки, а не картинка в потоке:
   он подрезается её краями. Размер, положение и прозрачность 0.23 сняты
   с оригинала; без прозрачности логотип забивает текст, что и было видно
   на первом прогоне. */
.soc__card--tg {
  margin-top: 2px;
  overflow: hidden;
  background: var(--blue);
}
.soc__card--tg::before {
  content: '';
  position: absolute;
  inset: 0;
  opacity: .23;
  background: url('img/yt-logo.webp') 49.486% 57.817% / 149.167% no-repeat;
}
.soc__card--tg > * { position: relative; }
.soc__card--tg .btn { width: 100%; }

.btn--yt { background: #f92626; }
.btn--tg { background: #fff; color: var(--ink); }

/* ── Обратная связь ──────────────────────────────────────────────────
   Единственная рабочая форма главной. Своя, без скриптов Тильды: их форма
   тянет за собой tilda-forms (55 КБ) ради валидации двух полей. */
.fb { position: fixed; inset: 0; z-index: 100; display: flex; align-items: center; justify-content: center; padding: 20px; }
.fb[hidden] { display: none; }
.fb__back { position: absolute; inset: 0; background: rgba(0, 0, 0, .7); }
.fb__win {
  position: relative;
  width: 480px;
  max-width: 100%;
  padding: 40px;
  border-radius: 20px;
  background: var(--card);
}
.fb__x {
  position: absolute;
  top: 12px; right: 12px;
  width: 40px; height: 40px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--muted);
  /* Кнопки не наследуют шрифт: без этой строки крестик рисовался системным
     Arial и выбивался из набора. */
  font-family: inherit;
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
}
.fb__x:hover { color: #fff; }
.fb__h { margin: 0; font-family: 'PPNeueMachina', sans-serif; font-size: 30px; font-weight: 400; line-height: 36px; letter-spacing: -1px; }
.fb__d { margin: 12px 0 0; color: var(--muted); font-size: 16px; line-height: 24px; }
.fb__form { display: flex; flex-direction: column; gap: 16px; margin-top: 24px; }
.fb__field { display: flex; flex-direction: column; gap: 6px; }
.fb__label { color: var(--muted); font-size: 14px; line-height: 20px; }
.fb__field input {
  height: 52px;
  padding: 0 16px;
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: 10px;
  background: var(--bg);
  color: #fff;
  font-family: inherit;
  font-size: 17px;
}
.fb__field input:focus { outline: 2px solid var(--blue); outline-offset: 1px; }
.fb__check { display: flex; align-items: flex-start; gap: 10px; color: var(--muted); font-size: 14px; line-height: 20px; }
.fb__check input { margin: 2px 0 0; flex: none; width: 18px; height: 18px; accent-color: var(--blue); }
.fb__check a { color: var(--muted); text-decoration: underline; }
/* Ошибку видно сразу: цвет плюс текст, а не только цвет — на цвет нельзя
   опираться как на единственный признак. */
.fb__err { margin: 0; color: #ff8a80; font-size: 15px; line-height: 21px; }
.fb__submit { margin-top: 4px; }
.fb__ok { margin: 24px 0 0; font-size: 17px; line-height: 25px; }
@media (max-width: 900px) {
  .fb__win { padding: 28px 20px 24px; }
  .fb__h { font-size: 24px; line-height: 30px; }
}

/* ── Подвал ──────────────────────────────────────────────────────── */
.ft { background: var(--bg); padding: 32px 0 8px; color: var(--muted); }
.ft__cols { display: grid; grid-template-columns: 1.1fr 1fr 1fr 1fr; gap: 40px; }
.ft__col { display: flex; flex-direction: column; gap: 10px; font-size: 16px; font-weight: 300; line-height: 18px; }
.ft__col h3 {
  margin: 0 0 6px;
  color: #fff;
  font-size: 20px;
  font-weight: 400;
  line-height: 22px;
  font-family: 'PPNeueMachina', sans-serif;
  letter-spacing: -.5px;
}
.ft__col a:hover { color: #fff; }
.ft__col p { margin: 0; }
.ft__col--brand img { width: 249px; height: auto; margin-bottom: 10px; }
.ft__bottom {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 24px;
  font-size: 15px;
  color: #fff;
}
.ft__bottom img { width: 107px; height: auto; }

@media (max-width: 900px) {
  .reviews { padding: 0 0 24px; }
  .socials { padding: 0 0 56px; }
  .reviews__top { padding: 48px 0 24px; display: flex; align-items: center; flex-wrap: wrap; gap: 16px; }
  .reviews__h { max-width: none; }
  .reviews__count { position: static; margin-right: auto; }
  .reviews__nav { position: static; gap: 12px; }
  .rv-track { grid-auto-columns: calc(100% - 24px); gap: 16px; }
  .rv { padding: 20px; }
  .rv__top { gap: 16px; }
  .rv__ava { width: 84px; height: 84px; }
  .rv__name { font-size: 19px; line-height: 24px; }
  .rv__q { margin-top: 20px; }
  .rv__q span { font-size: 19px; line-height: 26px; }
  .rv__shot { margin-top: 20px; }
  .rv__role { font-size: 15px; line-height: 18px; margin-top: 20px; }
  .rv-arrow { width: 46px; height: 46px; font-size: 24px; }
  /* Яндекс отдаёт под узкий экран другую вёрстку виджета — она длиннее,
     поэтому и айфрейму нужна другая высота, иначе окно прокрутки упрётся
     в обрезанный низ. Числа те же, что у оригинала. */
  .ymaps { padding: 40px 0; }
  .ymaps__box { width: 100%; height: 600px; }
  .ymaps__box iframe { height: 2600px; }
  /* На узком экране оригинал меняет карточки местами: телеграм наверх. */
  .soc { grid-template-columns: 1fr; gap: 16px; }
  .soc__title { max-width: none; margin-bottom: 24px; }
  .soc__card { padding: 20px; border-radius: 20px; min-height: 0; gap: 20px; }
  .soc__card--tg { margin-top: 0; order: -1; }
  .soc__card .btn, .soc__card--tg .btn { width: 100%; }
  .soc__card--yt .soc__t { max-width: calc(100% - 80px); }
  .soc__logo { top: 20px; right: 20px; left: auto; width: 64px; height: auto; }
  .soc__clips { position: static; width: 100%; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 20px; }
  .soc__play { width: 40px; height: 40px; margin: -20px 0 0 -20px; }
  .soc__play::after { margin: -7px 0 0 -5px; border-width: 7px; border-left-width: 11px; border-right-width: 0; }
  .soc__t { font-size: 20px; line-height: 26px; }
  .ft { padding: 32px 0; }
  .ft__cols { grid-template-columns: 1fr; gap: 28px; }
  .ft__col--brand img { width: 200px; }
  .ft__bottom { margin-top: 28px; }
}

/* Кнопка в широкой карточке — 540, как в оригинале, а не во всю колонку. */
.prod--wide .btn { max-width: 540px; }
/* Заголовок широкой карточки в оригинале ограничен 511 — из-за этого
   «Интенсив-практикум…» переносится в три строки, и вторая карточка
   выше первой на 42. Без ограничения он влезал в две, и карточка
   получалась ниже оригинала. */
.prod--wide .prod__h { max-width: 511px; }
@media (max-width: 900px) { .prod--wide .btn { max-width: 100%; } }

/* ── Анимации ─────────────────────────────────────────────────────────
   В оригинале их две и обе скромные: 15 блоков проявляются при попадании
   в кадр и 8 элементов меняют прозрачность на наведении. Разобрано по
   data-animate-sbs-opts: mx/my = 0, sx/sy = 1 — то есть ни сдвига, ни
   масштаба, только op 0 → 1.

   Исключение — два кружка «плей» на роликах: у них sx/sy = 1.06 при
   ti = 200, то есть они всё-таки подрастают на 6 % за 0.2 с. Раньше я
   написал, что масштаба нет нигде; это верно для остальных, но не для
   них. */

.btn, .rv-arrow, .hdr__login, .nav__all, .hdr__fb, .ft__col a {
  transition: opacity .1s ease, background-color .1s ease, color .1s ease;
}
.btn:hover, .hdr__login:hover, .nav__all:hover, .hdr__fb:hover { opacity: .85; }
.btn--ghost:hover { opacity: 1; }

/* Плашки курсов под первым экраном ведут себя иначе прочих кнопок: на
   наведении заливка становится цветом фона страницы, проступает тонкая
   светлая рамка, а текст белеет. Значения взяты из переменных оригинала:
   --t396-bgcolor-hover-color: #1e1e1e, --t396-bordercolor-hover:
   rgba(255,255,255,0.2), --t396-borderwidth: 1px, скорость 0.2s.

   Рамка объявлена и в покое, прозрачной: иначе при наведении плашка
   подрастала бы на 2 px и строка дёргалась. */
.chip {
  border: 1px solid transparent;
  transition: background-color .2s ease-in-out, color .2s ease-in-out, border-color .2s ease-in-out;
}
.chip:hover {
  background-color: var(--bg);
  border-color: rgba(255, 255, 255, .2);
  color: #fff;
}
/* У «Zabbix» рамка задана не для наведения, а для покоя — то есть видна
   всегда, в отличие от остальных четырёх. Похоже на недосмотр, но так на
   боевой странице, поэтому повторяю. */
.chip--red { border-color: rgba(255, 255, 255, .2); }
.soc__play { transition: transform .2s ease; }
.soc__clip:hover .soc__play { transform: scale(1.06); }

/* Появление при прокрутке.

   Прятать контент разрешено ТОЛЬКО когда скрипт уже взял управление:
   класс js-anim вешается на <html> непосредственно перед подпиской
   наблюдателя. Иначе получается неприемлемый отказ — при любой осечке JS
   (ошибка, блокировщик, старый браузер) страница остаётся пустой.
   Проверено на стенде: без этой развязки 21 блок был невидим. */
.js-anim .reveal { opacity: 0; transition: opacity .6s ease; }
.js-anim .reveal.is-in { opacity: 1; }

/* Кому анимация мешает — тому её не показываем. Это не вкусовщина:
   у части людей от движения на экране в буквальном смысле кружится голова,
   и системная настройка существует ровно для этого. */
@media (prefers-reduced-motion: reduce) {
  .js-anim .reveal, .js-anim .reveal.is-in { opacity: 1; transition: none; }
  .btn, .chip, .rv-arrow, .soc__play { transition: none; }
  .soc__clip:hover .soc__play { transform: none; }
}
