/* ============================================================
   Блок «Факты кейса (сетка плиток)» — case-facts-grid

   Раскладку держит Bootstrap 5 (.row + .row-cols-*), своей сетки здесь нет:
   в CSS только шкура плитки, типографика и появление при скролле.

   Цвет берётся ТОЛЬКО из контекстной палитры --ctx-* (style.css, раздел
   «КОНТЕКСТНАЯ ПАЛИТРА»). На странице кейса она собрана из пары цветов записи,
   на остальных страницах — из фирменных --macro-*; обе темы уже внутри этих
   токенов. Поэтому у блока нет ни одной ветки «если тёмная» и ни одного hex.

   ВСЕ селекторы ниже начинаются с .case-facts-grid не для красоты: страница
   кейса задаёт свою типографику через `.case-body h3` / `.case-body p`
   (специфичность 0,1,1). Два класса (0,2,0) её перебивают, один — нет.

   Редизайн 20.08.2026 (tz-case-facts-grid-redesign.md): плитка перестала быть
   коробкой. Тот же шаг, что уже сделан у .bottom_nav в теме (style.css:1758):
   вместо рамки, заливки, радиуса и стойки 4px — колонка текста под волосяной
   линейкой, вес держат типографика и контекстный цвет. Заливка осталась ровно
   одной плитке на сетку (выделенной) и берётся из --case-tint, того же токена,
   что у цитаты и отзыва заказчика на этой же странице.
   ============================================================ */

.case-facts-grid {
	/* Шаг сетки — тот же, что у рядов страницы кейса; вне кейса берётся
	   ближайший эквивалент. Значение уходит в штатные гаттеры Bootstrap
	   (ниже), поэтому utility-классы .g-* блоку не нужны. */
	--fact-gap: var(--case-gap, clamp(1rem, 2.2vw, 2rem));

	/* Вертикальный зазор больше горизонтального в полтора раза: линейка теперь
	   принадлежит плитке СНИЗУ от неё. При равных зазорах она читалась бы как
	   разделитель между рядами и висела бы посередине. */
	--fact-gap-y: calc(var(--fact-gap) * 1.5);

	/* Разделители. Волосяная линейка — тот же приём, что у шапки главной,
	   заголовка раздела кейса и .bottom_nav; акцентная (втрое толще) есть
	   ровно у выделенной плитки и потому что-то значит. */
	--fact-rule: var(--ctx-border);
	--fact-rule-accent: var(--ctx-accent);

	/* Воздух между линейкой и первой строкой текста. Значение не выдумано:
	   это padding-top у .bottom_nav (style.css:1778) — единственное место
	   в теме, где уже решён вопрос «сколько воздуха под линейкой». */
	--fact-rule-gap: clamp(0.875rem, 1.6vw, 1.25rem);

	/* Строка кикера ВМЕСТЕ с её нижним воздухом. Кикер занимает слот через
	   min-height, а плитки без кикера резервируют его паддингом — поэтому
	   кегль кикера можно менять, ничего не пересчитывая (было три связанных
	   «не забудь» в двух медиазапросах). */
	--fact-kicker-slot: 2rem;

	/* Радиус — системный токен темы, но применяется теперь только к низу
	   заливки выделенной плитки: 24px на колонке шириной 264px читались
	   пузырём, на карточке кейса шириной 1150px — нормально. */
	--fact-radius: var(--case-radius, 1.25rem);

	/* Заливка выделенной плитки — ТА ЖЕ врезка, что у цитаты и отзыва
	   (--case-tint, style.css:2113). Своего оттенка блок не заводит;
	   вне кейса срабатывает фолбэк с тем же рецептом (8% акцента). */
	--fact-tint: var(--case-tint, color-mix(in srgb, var(--ctx-accent) 8%, var(--ctx-surface)));

	/* Вынос заливки в гаттер: считается от него и потому всегда меньше
	   половины зазора — заливка не дотягивается до колонки соседа
	   (остаётся ≥2px). На 1440 это ≈14px, на 375 ≈6px. */
	--fact-featured-pad: calc(var(--fact-gap) / 2 - 0.125rem);

	/* Акцент ТЕКСТОМ ≠ акцент заливкой. На кейсе они совпадают (--case-accent-text
	   объявлен через --ctx-accent, а тот всегда противоположен фону по светлоте),
	   вне кейса в тёмной теме заливочный --macro-accent слишком тёмен для текста —
	   там берётся его «текстовая» светлота. Порядок фолбэков это и разводит. */
	--fact-accent-text: var(--case-accent-text, var(--macro-accent-text, var(--ctx-accent)));
}

/* Плоский фолбэк там, где нет color-mix (тот же приём, что у .case-page).
   Чинить теперь надо один токен: заливка осталась только у выделенной плитки. */
@supports not (color: color-mix(in srgb, red, blue)) {
	.case-facts-grid {
		--fact-tint: var(--ctx-surface);
	}
}

/* Внешние отступы секции. Вне кейса блок сам отбивает себя от соседей;
   на странице кейса воздух между разделами задаёт единый ритм
   (.case-body > * + * { margin-top: var(--case-rhythm) }), и второй слой
   отступов ломает вертикальный ритм страницы — сетка фактов оказывалась
   отбита от соседей сильнее любого другого блока. */
.case-facts-grid {
	padding-block: clamp(1.5rem, 3vw, 2.5rem);
}

.case-body .case-facts-grid {
	padding-block: 0;
}

/* Гаттеры — штатным API Bootstrap, а не своим margin/gap: так работают
   .row-cols-*, отрицательные margin ряда и вертикальный перенос строки. */
.case-facts-grid .case-facts-grid__grid {
	--bs-gutter-x: var(--fact-gap);
	--bs-gutter-y: var(--fact-gap-y);
}

/* Паддинг .container намеренно НЕ трогаем — ни своим значением, ни нулём.
   Механика Bootstrap: у .row края уходят в минус на полгаттера, у колонки
   такой же паддинг внутрь, и в сумме КОНТЕНТНЫЙ край колонки совпадает
   с краем контента контейнера. На странице кейса тема обнуляет паддинг
   у .container встроенных секций (одна ось для всех блоков) — и текст плиток
   встаёт ровно на линию заголовков (проверено: 144px при --case-wide на 1440).
   Раньше на эту линию вставала рамка плитки, а текст был отодвинут внутрь
   паддингом — ступенька была видна на каждом скриншоте; теперь паддинга
   у плитки нет и на линии стоит сам текст.
   Вне кейса работает штатный паддинг .container, и блок выглядит как любой
   другой на сайте. Любая «компенсация» здесь сдвигает плитки внутрь на
   полгаттера и ломает как раз то, ради чего она писалась.
   Класс .case-facts-grid__inner оставлен как крючок для будущих правок. */

/* Подсказка в редакторе, когда карточек ещё нет (на фронте блок молчит) */
.case-facts-grid__hint {
	margin: 0;
	padding: 1rem;
	border: 1px dashed var(--ctx-border);
	border-radius: 0.5rem;
	color: var(--ctx-muted);
}


/* --- Плитка ---
   Ни рамки, ни заливки, ни радиуса: колонка текста под линейкой.
   padding-inline: 0 — чтобы текст стоял на общей левой линии страницы,
   а не был отодвинут внутрь своей коробки.

   height: 100% — единственная причина, по которой плитки в строке одной
   высоты: колонка .col тянется по строке сама (align-items: stretch у .row),
   а карточка внутри неё добирает эту высоту. Никакого равнения скриптом.
   Пустого дна это больше не даёт — дна у плитки нет. */

.case-facts-grid .case-fact {
	position: relative;
	height: 100%;
	padding: var(--fact-rule-gap) 0 0;
	border: 0;
	border-radius: 0;
	background: none;
}

/* Волосяная линейка сверху. Псевдоэлемент, а не border-top, осознанно:
   у выделенной плитки линейку надо поджать по горизонтали (см. ниже),
   бордером так не сделать. */
.case-facts-grid .case-fact::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 1px;
	background-color: var(--fact-rule);
}

/* Выбранный вариант. Раньше отличался четырьмя вещами сразу (гуще заливка,
   акцентная рамка, стойка 6px, тень) — и всё равно читался только рядом
   с соседями для сравнения. Теперь отличий два, и оба однозначные: он
   ЕДИНСТВЕННАЯ залитая плашка в сетке и у него акцентная линейка втрое
   толще соседней.

   ⚠ Выделенная плитка в одной сетке — ОДНА. Две подряд склеятся выносами
   заливки в одно пятно.

   Тени нет намеренно: врезка лежит в тексте, а не над страницей
   (у цитаты и отзыва её тоже нет). */
.case-facts-grid .case-fact--featured {
	/* вынос: заливка растёт наружу в гаттер, текст остаётся на той же
	   вертикали, что у соседей и у заголовков раздела */
	margin-inline: calc(-1 * var(--fact-featured-pad));
	padding-inline: var(--fact-featured-pad);
	padding-bottom: var(--fact-featured-pad);
	background-color: var(--fact-tint);
	/* верх плоский: там лежит линейка, общая для всего ряда */
	border-radius: 0 0 var(--fact-radius) var(--fact-radius);
}

.case-facts-grid .case-fact--featured::before {
	height: 3px;
	background-color: var(--fact-rule-accent);
	/* линейка выделенной плитки идёт по КОНТЕНТНОМУ краю колонки, а не по
	   краю заливки: иначе она уедет наружу на ширину выноса и разъедется
	   с линейками соседей по ряду */
	left: var(--fact-featured-pad);
	right: var(--fact-featured-pad);
}


/* --- Содержимое плитки ---
   Вертикальный ряд один для плиток со значением и без:
   линейка → --fact-rule-gap → [слот кикера] → [значение] → заголовок → текст. */

/* Кикер над карточкой: та же микро-типографика, что у нумерации разделов
   (.case-body h2::before), номера карточки на главной и подписей «ПРЕДЫДУЩИЙ
   КЕЙС» — одна система подписей на всю страницу. Единственный носитель
   акцентного цвета в тексте, менять нельзя.
   Нижний воздух не отдельным margin, а частью слота (--fact-kicker-slot). */
.case-facts-grid .case-fact__kicker {
	display: flex;
	align-items: flex-start;
	min-height: var(--fact-kicker-slot);
	margin: 0;
	font-size: 0.8125rem;
	font-weight: 500;
	line-height: 1;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--fact-accent-text);
}

/* Ряд, в котором кикер есть хотя бы у одной плитки, выравниваем по кикеру:
   остальным резервируем ту же строку сверху. Иначе крупные цифры и заголовки
   соседей встают выше «выбранного» варианта, и ряд читается кривым (ровно так
   выглядела тройка вариантов IN HAUS: «2» ниже «1» и «3»).

   Резерв включается ровно там, где плитки стоят В РЯД, — выравнивать нечего,
   когда колонка одна: в стопке это просто дырка над цифрой. Точку берём из
   самого класса раскладки (row-cols-sm-2 у сеток 3/4, row-cols-md-2 у сетки
   на 2), а не общим 576px: у двухколоночной сетки стопка держится до 768.

   Без поддержки :has() правило не сработает вовсе — плитки останутся как были,
   ничего не сломается. */
@media (min-width: 576px) {
	.case-facts-grid .row-cols-sm-2:has(.case-fact__kicker) .case-fact:not(:has(.case-fact__kicker)) {
		padding-top: calc(var(--fact-rule-gap) + var(--fact-kicker-slot));
	}
}

@media (min-width: 768px) {
	.case-facts-grid .row-cols-md-2:has(.case-fact__kicker) .case-fact:not(:has(.case-fact__kicker)) {
		padding-top: calc(var(--fact-rule-gap) + var(--fact-kicker-slot));
	}
}

/* Крупное значение. Кегль выбирается СТУПЕНЬЮ по длине строки, и считает её
   PHP: «1» и «3 недели» физически не могут жить в одном кегле, а флаг
   «это цифра / это фраза» в админке — настройка, которую в кейсе некому
   осмысленно выставлять. Модификатор ставится вычислением, не редактором.

   База — короткая мера («3 недели»): крупно, но заведомо тише h2 раздела
   (было 60px против 48px у h2 — подпись к плитке оказывалась самым громким
   элементом страницы). */
.case-facts-grid .case-fact__value {
	margin: 0;
	font-size: clamp(2rem, 3.6vw, 2.75rem);
	font-weight: 700;
	line-height: 1;
	letter-spacing: -0.02em;
	color: var(--fact-accent-text);
	/* длинное значение на 375 не должно распирать колонку */
	overflow-wrap: break-word;
	/* «3 недели» не рвётся на «3 / недели», где не надо */
	text-wrap: balance;
}

/* 1–2 символа: порядковый номер варианта — здесь кегль работает как графика */
.case-facts-grid .case-fact__value--short {
	font-size: clamp(2.75rem, 5.5vw, 4rem);
	line-height: 0.9;
	letter-spacing: -0.03em;
	/* оптический вынос цифры на линию колонки: у «1»/«3» левый сайдбар
	   заметно больше, чем у строчного текста под ней */
	margin-left: -0.045em;
}

/* 10+ символов: это уже фраза, ведёт себя как подзаголовок */
.case-facts-grid .case-fact__value--long {
	font-size: clamp(1.5rem, 2.4vw, 2rem);
	line-height: 1.1;
}

/* Отступ под значением принадлежит ПАРЕ «значение + заголовок», а не самому
   значению: у плитки без заголовка лишнего зазора под цифрой не появляется. */
.case-facts-grid .case-fact__value + .case-fact__title {
	margin-top: 0.75rem;
}

/* Трекинг отрицательный: нулевого у крупного текста на сайте нет нигде,
   кроме кикеров. +2px кегля компенсируют потерю рамки. */
.case-facts-grid .case-fact__title {
	margin: 0;
	font-size: clamp(1.125rem, 1.5vw, 1.375rem);
	font-weight: 600;
	line-height: 1.25;
	letter-spacing: -0.01em;
	color: var(--ctx-ink);
}

.case-facts-grid .case-fact__text {
	margin-top: 0.625rem;
}

/* --ctx-ink, а не --ctx-muted: это основной текст плитки, а не подпись.
   Приглушённый на сайте занят служебным (figcaption, bottom_nav__label,
   «кейсов не найдено»), и приглушённый текст на приглушённой плашке
   и давал ту самую «мутность». */
.case-facts-grid .case-fact__text p {
	margin: 0 0 0.625rem;
	font-size: clamp(0.9375rem, 0.95vw, 1rem);
	line-height: 1.55;
	color: var(--ctx-ink);
}

.case-facts-grid .case-fact__text p:last-child {
	margin-bottom: 0;
}


/* --- Появление при скролле ---
   Механика и тайминги — общие с сеткой кейсов (cases-grid.css/js): класс
   .reveal-ready вешает скрипт, поэтому без JS и при prefers-reduced-motion
   плитки просто видны, а не спрятаны навсегда.
   Длительность здесь ОБЯЗАНА совпадать с REVEAL_MS в case-facts-grid.js. */

.case-facts-grid .case-facts-grid__grid.reveal-ready .case-fact {
	opacity: 0;
	transform: translateY(16px);
	transition: opacity 0.45s cubic-bezier(0.22, 0.61, 0.36, 1),
		transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
	transition-delay: 0s;
}

/* Шахматка — только на первом экране (класс вешает JS): ниже задержка копилась
   бы и плитка «догоняла» бы пользователя уже после того, как он долистал. */
.case-facts-grid .case-facts-grid__grid.reveal-ready .case-fact.is-stagger {
	transition-delay: calc(min(var(--i, 0), 3) * 60ms);
}

.case-facts-grid .case-facts-grid__grid.reveal-ready .case-fact.is-inview {
	opacity: 1;
	transform: none;
}

/* Мгновенный показ (быстрый флик, переход по якорю, восстановление скролла):
   плитка уже в кадре, анимировать её — значит показать дырку. Класс живёт
   ровно один кадр, JS его снимает. */
.case-facts-grid .case-facts-grid__grid.reveal-ready .case-fact.is-instant {
	transition: none;
}

/* Гард обязателен и в CSS, и в JS. Подключённый на сайте AOS свой
   prefers-reduced-motion НЕ обрабатывает (в его css такого медиазапроса нет),
   поэтому на его поведение опираться нельзя. */
@media (prefers-reduced-motion: reduce) {
	.case-facts-grid .case-facts-grid__grid.reveal-ready .case-fact,
	.case-facts-grid .case-facts-grid__grid.reveal-ready .case-fact.is-stagger,
	.case-facts-grid .case-facts-grid__grid.reveal-ready .case-fact.is-inview {
		opacity: 1;
		transform: none;
		transition: none;
		transition-delay: 0s;
	}
}
