/* ============================================================
   Блок проектов (перенос из Figma, node 14:3, база 1512x844).
   Каркас макета: фон #1F1E1F, светлая панель #F5F5F5 с отступом
   20px и радиусом 16.54; внутри панели шапка и сетка карточек.
   Сетка: 4 колонки по 343px с зазором 20 (шаг 363), ряд 341px;
   крупная карточка занимает 2 колонки (706 = 343*2 + 20).

   Адаптив (mobile-first, один основной вариант):
   – до 640px: одна колонка, карточки тянутся по содержимому;
   – 640-1000px: две колонки, крупные карточки занимают обе;
   – от 1000px: четыре колонки, как в макете.
   Размеры внутри карточки заданы через clamp() – между точками
   перелома все тянется плавно, а не прыгает.
   ============================================================ */

.projects {
  /* Блок ПРИЛЕТАЕТ из глубины, поэтому лежит ВЫШЕ сцены (z-index 1), а не под
     ней: границы не видно за счет фона, который проявляется вместе с панелью
     (--pj-bg от 0 до 1) – звезды гаснут под ним постепенно, резкого края нет */
  position: relative; z-index: 2;
  background: rgba(31, 30, 31, var(--pj-bg, 1));   /* прозрачен, пока блок летит */
  padding: clamp(12px, 1.3vw, 20px);
  /* блок должен уметь занять экран целиком: иначе панель не доезжает до верха,
     шапка так и висит над темным фоном и не переключается в светлую тему */
  /* запас под фазу перехода теперь держит пролог: панель на время фазы
     закреплена на экране, а секция занимает ровно свою высоту */
  min-height: 100svh;
  display: grid; align-content: start;   /* панель прижата к верху: шапка должна попадать ВНУТРЬ нее, как в макете */
}

/* Фаза перехода: панель прилипла и проявляется, звезды под ней гаснут.
   Затемнение по всему экрану дает вейл, а не фон секции: у секции фон
   прозрачный, поэтому ее верхний край не читается на звездах */
.projects .panel { opacity: var(--pj-o, 1); }
/* Панель ПРИЛИПАЕТ и стоит неподвижно, пока проявляется. Раньше ее держал на
   месте сдвиг из скрипта (--pj-dy), и панель заметно дрожала: JS-сдвиг отстает
   от нативной прокрутки на кадр – sticky этим занимается сам.
   На узких экранах панель выше окна, sticky отпускает ее сразу, поэтому там
   она просто проявляется по мере въезда */
/* Пока идет фаза, панель ЗАКРЕПЛЕНА на экране и стоит неподвижно: карточки не
   уезжают вверх, пока блок не проявился. Отступы повторяют отступы секции,
   поэтому в момент отпускания панель уже там, где окажется в потоке. */
.projects.is-approach .panel {
  position: fixed; z-index: 2;
  left: clamp(12px, 1.3vw, 20px); right: clamp(12px, 1.3vw, 20px);
  top: clamp(12px, 1.3vw, 20px);
  max-height: calc(100svh - 2 * clamp(12px, 1.3vw, 20px));
  overflow: hidden;
}
.projects.is-approach .panel { will-change: transform, opacity, filter; }

.approach-veil {
  position: fixed; inset: 0; z-index: 1;    /* над сценой, под блоком */
  background: #1f1e1f; pointer-events: none;
  opacity: var(--pj-bg, 0);
}

@media (prefers-reduced-motion: reduce) {
  .projects .panel { transform: none; filter: none; opacity: 1; }
}

.panel {
  background: #f5f5f5;
  border-radius: clamp(12px, 1.1vw, 16.54px);
  /* сверху – место под фиксированную шапку. Высоту дает js/nav.js в --nav-h:
     на узких экранах пилюля переносится на вторую строку, и магическое число
     перестает работать. Значение до скрипта – резерв макета */
  /* Отступ сверху отмеряется от НИЗА ПИЛЮЛИ меню (--nav-pill-b ставит
     js/nav.js), а сам зазор вдвое меньше прежнего: раньше он считался от
     высоты всей шапки и на экране выглядел вдвое больше нужного. */
  padding: calc(var(--nav-pill-b, 64px) + clamp(6px, .6vw, 11px)) clamp(12px, 1.3vw, 20px) clamp(16px, 1.7vw, 26px);
  /* в макете панель занимает почти весь экран (804 из 844) */
  min-height: calc(100svh - 2 * clamp(12px, 1.3vw, 20px));
  container-type: inline-size;      /* карточки внутри реагируют на панель, не на экран */
}

/* ---------- сетка карточек ---------- */
.cards {
  display: grid; gap: clamp(10px, 1.3vw, 20px);
  grid-template-columns: 1fr;
  /* своего отступа сверху нет: расстояние до меню целиком задает padding
     панели, иначе зазор складывался из двух величин и выходил вдвое больше */
}
/* две колонки, крупная карточка занимает обе */
@media (min-width: 640px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card--wide { grid-column: span 2; }
}
/* четыре колонки – раскладка макета */
@media (min-width: 1000px) {
  .cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .card--wide { grid-column: span 2; }
}

.card {
  position: relative; display: flex; flex-direction: column;
  min-width: 0;                      /* иначе длинный заголовок растягивает колонку */
  background: #fff; border-radius: 10px; overflow: clip;
  min-height: clamp(220px, 22.6vw, 341px);
  padding: clamp(12px, 1.06vw, 16px) clamp(14px, 1.45vw, 22px) clamp(10px, .86vw, 13px);
  text-decoration: none;
}
.card__title {
  margin: 0; color: #a3a3a3;
  font-family: var(--font-ui, "Inter Tight"), sans-serif;
  font-weight: 400; text-transform: uppercase;
  font-size: clamp(11.5px, .93vw, 14px); line-height: 1.2; letter-spacing: 0;
  max-width: 30ch;                   /* в макете заголовок узкой карточки в 3-4 строки */
}
.card--wide .card__title { max-width: 54ch; }


/* Превью-скриншот: позиция и размер строго из макета (node 32:935), в процентах
   от карточки – поэтому не зависят от того, во сколько строк лег заголовок.
   Узкая карточка: 316x237 при отступе 13 и верхе 96 из 343x341.
   Широкая: 740x298 при x=-29, y=76 из 706x341 – превью шире карточки и
   обрезается ее рамкой, как в макете. */
.card__shot {
  position: absolute; overflow: clip; border-radius: 10px;
  left: 3.79%; top: 28.15%; width: 92.13%;
  aspect-ratio: 316 / 237;
}
.card--wide .card__shot {
  /* В макете превью 740x298 при x=-29 – оно шире карточки и обрезается ее
     рамкой. Экспорт узла отдает уже ВИДИМУЮ часть (706x265), поэтому здесь
     ширина 100% и пропорция видимой части, иначе картинка приезжает крупнее
     и уезжает влево. */
  left: 0; top: 22.29%; width: 100%;
  border-radius: 0;
  aspect-ratio: 706 / 265;
}
/* Широкая карточка-заглушка: в макете (node 40:285) превью НЕ обрезается
   рамкой, а вписано внутрь – 646x261 при отступе 30 и верхе 80. Общее правило
   широкой карточки (превью шире рамки) давало здесь 13.9% расхождения. */
.card--wide.card--soon .card__shot {
  left: 4.25%; top: 23.46%; width: 91.50%;
  aspect-ratio: 646 / 261;
}

/* У когнитивного кейса свое превью, но заголовок начинается на общей линии
   со всеми карточками. Отдельный уменьшенный верхний padding поднимал его. */
.card--cog { padding-top: clamp(12px, 1.06vw, 16px); }
.card--cog .card__shot {
  /* Верх превью – общий для всех узких карточек (28.15%, как у базовой).
     В макете у этой карточки превью стоит на 76px из 341 (22.29%), но ее
     заголовок длиннее всех и занимает 4 строки: превью НАЕЗЖАЛО на него на
     7.2px, а в ряду оно стояло на 20px выше соседей («почему кейс когнитивная
     идет выше остальных, выравнивание должно быть по верхнему краю»).
     Пропорция превью сохранена, поэтому кадр не искажается. */
  left: 3.79%; top: 28.15%; width: 89.80%;
  border-radius: 0;
  aspect-ratio: 308 / 235;
}
/* Кейс доказательного управления портфелем: в обновленном макете главной
   (node 16:26) на месте прежней заглушки стоит живой кейс. Превью – композиция
   полос с процентами 535x257 при x=22, y=62 из карточки 706x341, то есть
   3.12% / 18.18% / 75.78% / 75.38%. Числа из макета, не подобраны. */
.card--evidence .card__shot {
  left: clamp(14px, 1.45vw, 22px); top: 18.18%; width: 75.78%;
  border-radius: 0;
  aspect-ratio: 535 / 257;
}
.card--evidence .card__shot::after { display: none; }

/* Кейс регионов: в обновленном макете (node 1:2085) превью заменено на светлую
   диаграмму сигналов 363x134 при x=-21, y=146 из карточки 343x341 – оно шире
   рамки и обрезается ею, а начинается заметно ниже остальных превью. Числа
   взяты из макета, не подобраны. */
.card--signals .card__shot {
  left: -6.12%; top: 42.82%; width: 105.83%;
  border-radius: 0;
  aspect-ratio: 363 / 134;
}

.card__shot img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top left;
}

/* На адаптиве превью показывает весь кадр без обрезки. Карточка растет по
   натуральной пропорции изображения, поэтому ни верх, ни низ макета не
   скрываются фиксированной рамкой десктопной карточки. */
@media (max-width: 1199px) {
  .card { min-height: 0; padding-bottom: 52px; }
  /* В исходном десктопном макете карточки имеют разные ограничения заголовка.
     В одной адаптивной колонке это выглядело как случайный ранний обрез.
     Берем ширину первой карточки как общий эталон. */
  .card__title,
  .card--wide .card__title { max-width: min(403px, 100%) !important; }
  .card__shot,
  .card--wide .card__shot,
  .card--wide.card--soon .card__shot,
  .card--signals .card__shot,
  .card--cog .card__shot {
    /* inset: auto дублируется четырьмя длинными свойствами намеренно. Только
       эта строка гасит десктопный сдвиг влево (например left: -6.12% у кейса
       регионов), а движки старше Safari 14.1 и Chrome 87 свойство inset не
       знают: там превью оставалось сдвинутым и обрезалось рамкой слева. */
    position: relative; inset: auto;
    top: auto; right: auto; bottom: auto; left: auto;
    width: 100%; height: auto;
    margin-top: clamp(18px, 3vw, 28px); aspect-ratio: auto; overflow: visible;
    border-radius: 8px;
  }
  .card__shot img { width: 100%; height: auto; object-fit: contain; object-position: center top; }
  .card--wide .card__shot::after { display: none; }
}

/* Кейс регионов, адаптив: на десктопе кадр 363px намеренно шире рамки 343px и
   обрезается ею слева. Здесь кадр показан целиком, но упирался в края поля
   карточки – просьба «картинку совсем чуть чуть уменьши, чтобы влезала».
   Ширина взята как в эталоне мобильной картинки (100% минус 14px, по центру):
   к полю карточки 14px добавляется по 7px с каждой стороны, кадр стоит на
   21px от края и ни во что не упирается. Пропорция не меняется: на 390px
   кадр 314x123 становится 300x117, высота идет за шириной. */
@media (max-width: 1199px) {
  .card--signals .card__shot {
    width: calc(100% - 14px);
    margin-left: auto;
    margin-right: auto;
  }
}

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

   Фрейм – сама рамка .card__shot: ее левый край и так стоит на padding карточки,
   то есть РОВНО на левом крае заголовка, и padding по бокам этого не меняет.
   Цвет – замеренный цвет фона самих файлов (мода пикселей рамки изображения):
   card-lab.webp – rgb(28,28,28), card-proof.webp – чистый черный. У обоих файлов
   углы прозрачные с радиусом 20 из 632 ширины, то есть 3.17% ширины – поэтому и
   радиус фрейма задан в процентах, а не числом: тогда он равен закруглению
   картинки на ЛЮБОЙ ширине адаптива. Второе значение (по вертикали) приведено к
   тому же радиусу в пикселях с учетом пропорции кадра 632/474.
   overflow: clip – страховка, что картинка не выйдет за скругление фрейма. */
@media (max-width: 1199px) {
  .card[href="case-lab.html"] .card__shot,
  .card[href="case-proof.html"] .card__shot {
    padding-inline: clamp(10px, 3.6vw, 20px);
    padding-block: 0;
    border-radius: 3.2% / 4.65%;
    overflow: clip;
  }
  .card[href="case-lab.html"] .card__shot { background: rgb(28, 28, 28); }
  .card[href="case-proof.html"] .card__shot { background: #000; }
}
/* В полосе двух колонок карточка – subgrid, и строка превью РАСТЯГИВАЕТСЯ по
   самой высокой карточке ряда: замерено, что на 1199px рамка была на 49px выше
   картинки. Пока у рамки не было фона, этого не было видно; с фоном снизу
   появлялась бы темная полоса, а условие «добавляем только по бокам» запрещает
   любой вертикальный привес. Рамка обжимается по картинке.
   Проверка на subgrid обязательна: в запасной flex-ветке (см. ниже) колонка
   flex, и align-self там ужал бы рамку по ГОРИЗОНТАЛИ. */
@media (min-width: 640px) and (max-width: 1199px) {
  @supports (grid-template-rows: subgrid) {
    .card[href="case-lab.html"] .card__shot,
    .card[href="case-proof.html"] .card__shot { align-self: start; }
  }
}

/* Пока карточки стоят в РЯД, превью обязаны начинаться на одной линии
   («выравнивание должно быть по верхнему краю и все на одной линии»). Здесь
   превью уже в потоке, поэтому его верх тянется за высотой заголовка, а
   заголовки разной длины.

   Резерв под фиксированное число строк не годится: замерено, что при резерве в
   четыре строки на 1000px заголовок когнитивной берет пятую и все равно уезжает
   вниз (разброс 13.8px), а в полосе 750-999px, где никто не берет больше двух,
   под каждым заголовком копится мертвое поле 28.4px. Поэтому строки карточки
   выравниваются по РЯДАМ РОДИТЕЛЯ (subgrid): высоту строки заголовка задает
   самый длинный заголовок ряда, и превью встают на общую линию сами, без
   магических чисел и без пустот. */
@media (min-width: 640px) and (max-width: 1199px) {
  .cards { grid-auto-rows: auto; }
  .card {
    display: grid; grid-template-rows: subgrid; grid-row: span 2;
    align-content: start;
  }
  /* запасной путь, если subgrid не поддерживается: та же линия, но с резервом */
  @supports not (grid-template-rows: subgrid) {
    .card { display: flex; }
    .card__title, .card--wide .card__title { min-height: calc(4 * 1.2em); }
  }
}

/* Десктоп: две строки проектов обязаны помещаться в один экран целиком.
   Планшет и мобильный поток выше не затрагиваем. Панель получает ровно
   доступную высоту viewport, строки делят оставшееся место поровну, а превью
   вписываются в свою область целиком и пропорционально уменьшаются.

   Раньше здесь стояло условие max-height: 840px, и на любом экране выше 840
   ветка не включалась: строки сетки замирали на 341px (max у min-height
   карточки), панель при этом тянулась на весь экран – под карточками
   оставалось мертвое поле, замерено 58px на 1600x900, 237px на 1920x1080,
   597px на 2560x1440, 757px на 2560x1600. Одновременно превью считало высоту
   от ШИРИНЫ карточки (aspect-ratio) и вылезало за рамку 341px: на 2560 узкое
   превью 557x418 при верхе 96 обрезалось на 173px, широкое превью кейса
   доказательного управления 932x448 при верхе 62 – на 169px (из пяти полос
   было видно три, кнопка уходила под картинку). Условие по высоте снято:
   ветка работает на всем десктопе, поэтому обрезки нет ни на одной высоте. */
@media (min-width: 1200px) {
  .panel {
    height: calc(100svh - 2 * clamp(12px, 1.3vw, 20px));
    min-height: 0;
  }
  .cards {
    height: 100%;
    grid-template-rows: repeat(2, minmax(0, 1fr));
  }
  .card {
    height: 100%;
    min-height: 0;
  }

  /* Верх рамки превью и отбивка от низа карточки – числа макета, у каждой
     карточки свои (верх – доля высоты 341, низ – остаток под превью: у узкой
     341-(96+237)=8, у когнитивной 10, у широкой 0 – превью доходит до низа и
     гасится белым градиентом, у кейса доказательного управления 22).
     У верха есть пол в px: на невысокой карточке процент дает меньше, чем
     занимает заголовок, и превью наезжало на текст. Обе величины – переменные,
     потому что участвуют и в расчете потолка высоты (ниже): числа не должны
     разъехаться между двумя правилами. */
  .card { --shot-top: max(28.15%, 84px); --shot-bot: 8px; }
  .card--wide { --shot-top: max(22.29%, 58px); --shot-bot: 0px; }
  .card--wide.card--soon { --shot-top: max(23.46%, 62px); --shot-bot: 0px; }
  .card--wide.card--evidence { --shot-top: max(18.18%, 58px); --shot-bot: 22px; }
  /* та же общая линия узких карточек, что и в основной ветке */
  .card--cog { --shot-top: max(28.15%, 84px); --shot-bot: 10px; }
  /* у кейса регионов макетный остаток снизу 61px – это просто место, где
     кончается плоская диаграмма, а не отбивка. Ставим обычную: с 61px потолок
     срабатывал бы там, где превью и так помещается */
  .card--signals { --shot-top: max(42.82%, 128px); --shot-bot: 13px; }

  /* Рамка превью держит МАКЕТНУЮ пропорцию (aspect-ratio из основных правил),
     пока та влезает в карточку, и упирается в потолок, когда не влезает.
     Потолок – это ровно свободное место карточки: ее высота минус верх рамки
     минус отбивка от низа. Без потолка высота рамки считалась от ШИРИНЫ и на
     широком экране уезжала за низ карточки (обрезка до 362px на 3440). */
  .card__shot,
  .card--wide .card__shot,
  .card--wide.card--soon .card__shot,
  .card--signals .card__shot,
  .card--cog .card__shot {
    top: var(--shot-top);
    max-height: calc(100% - var(--shot-top) - var(--shot-bot));
  }
  /* Когда потолок сработал (широкий и невысокий экран), рамка ниже своей
     пропорции – кадр ВПИСЫВАЕТСЯ в нее целиком и пропорционально уменьшается,
     а не обрезается. Пропорции рамок совпадают с пропорциями файлов
     (316/237 = 632/474, 706/265 = 1412/530, 308/235 = 924/705,
     535/257 ≈ 1605/772), поэтому там, где потолок не сработал, contain не
     меняет ни одного пикселя относительно макета.

     Уменьшенный кадр стоит по ЦЕНТРУ рамки по горизонтали (просьба клиента).
     Раньше он был прижат влево, и на широком невысоком экране справа
     оставалась пустота: замер на 1993x983 – 96px у портфеля, по 44 у
     лаборатории и исследования рынка, 42 у когнитивной. Там, где потолок не
     сработал (например 1440x900), кадр точно по рамке и выключка ничего не
     меняет – проверено замером на всех шести карточках.
     По левому краю остается только кейс доказательного управления: у него
     свое правило с important ниже по файлу. */
  .card__shot img { object-fit: contain; object-position: 50% 0%; }
  /* Кейс регионов – единственный, у кого рамка макета не совпадает с кадром
     (363/134 против 1029/403, то есть макет режет рамкой 5.7% высоты). Под
     потолком такая рамка либо режет кадр сильнее (cover), либо оставляет пустые
     поля по бокам (contain). Поэтому здесь рамке дана пропорция самого файла:
     кадр показан целиком и уменьшается ровно вместе с рамкой. */
  .card--signals .card__shot { aspect-ratio: 1029 / 403; }
}
/* затухание вниз – в макете это белый прямоугольник с размытием 30 */
.card--wide .card__shot::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 28%;
  background: linear-gradient(180deg, rgba(255,255,255,0) 0%, #fff 78%);
}

/* кнопка «Исследовать»: 99x28, белая, обводка #EDEDED, радиус 51.7 */
.card__cta {
  /* отступ справа ОДИН для всех карточек – 22, как у узкой (в макете у широкой
     он 30, но по просьбе кнопки выровнены между карточками) */
  position: absolute; right: clamp(14px, 1.45vw, 22px); bottom: clamp(10px, .86vw, 13px);
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 99px; height: 28px; padding: 0 12px;
  background: #fff; border: 1px solid #ededed; border-radius: 51.69px;
  color: rgba(24, 24, 26, .8);
  font-family: var(--font-ui, "Inter Tight"), sans-serif;
  font-size: 12px; line-height: 1; white-space: nowrap;
  transition: border-color .18s ease, color .18s ease, background-color .18s ease;
}
.card[href]:hover .card__cta { border-color: #d5d5d5; color: #18181a; }
.card[href]:focus-visible { outline: 2px solid #1f1e1f; outline-offset: 3px; }
.card[href]:focus-visible .card__cta { border-color: #b9b9b9; }

/* карточка-заглушка: кнопка на месте, но не притворяется ссылкой */
.card--soon { cursor: default; }
.card--soon .card__cta { color: rgba(24, 24, 26, .45); }

@media (prefers-reduced-motion: reduce) {
  .card__cta { transition: none; }
}

/* Карточка «система доказательного управления»: рисунок дашборда должен
   начинаться РОВНО под заголовком карточки – просьба клиента, повторенная
   несколько раз. Рамка картинки и так стоит на padding карточки, но в мобильном
   блоке у картинок стоит object-fit: contain с центрированием по горизонтали:
   стоит рамке оказаться хоть на пиксель шире пропорции файла (1605/772), и
   рисунок центрируется – уезжает вправо, слева остается поле. Прижимаем к
   левому краю на всех ширинах, тогда сдвиг невозможен в принципе. */
.card--evidence .card__shot img,
.card--evidence .card__shot picture img {
  object-position: left top !important;
}
@media (max-width: 1199px) {
  .card--evidence .card__shot img { object-position: left top !important; }
}


/* ============================================================
   Большие мониторы: заголовки кейсов крупнее.

   Базовый кегль – clamp(11.5px, .93vw, 14px), и потолок 14px держит его мелким
   на всем, что шире ноутбука: на 2560 заголовок читался с трудом. Здесь потолок
   поднимаем ступенями по ширине, а вместе с ним растет и ширина строки
   (max-width в ch), чтобы число строк осталось макетным и заголовок не наехал
   на превью – его верх задан в процентах от карточки и места не уступает.
   Правки только на странице проектов: внутри кейсов свои файлы.
   ============================================================ */
@media (min-width: 1600px) {
  .card__title { font-size: clamp(14px, .95vw, 17px); max-width: 31ch; }
  .card--wide .card__title { max-width: 55ch; }
  /* inline-ширина из разметки задана в px по макету 1512 – на широком экране она
     держала бы старое число строк при новом кегле */
  .card__title[style] { max-width: 31ch !important; }
  .card--wide .card__title[style] { max-width: 55ch !important; }
}
@media (min-width: 1920px) {
  .card__title { font-size: clamp(17px, .95vw, 19px); }
}
@media (min-width: 2400px) {
  /* потолок 22, а не 26: при 26 зазор до превью падал до 7px – заголовок почти
     касался кадра. На 22 остается около 20px воздуха */
  .card__title { font-size: clamp(20px, .9vw, 22px); }
}
