/* ПЛИТКА ТОВАРА И СЕТКА ПЛИТОК — файл дорожки «каталог и раздел» (спецификация v10, кусок 3).

   🔴 ФАЙЛ ОДИН, А СТРАНИЦ ТРИ: главная («Выбор сезона»), корень каталога и раздел. До волны 2
   правила плитки лежали в `catalog.css` — общем файле двух из этих страниц, — и дорожка,
   взявшая одну, молча меняла вид второй. Отсюда отдельный файл и один владелец у него.
   ⚠️ Правка здесь ВИДНА НА ГЛАВНОЙ ТОЖЕ. Это не побочное действие, а устройство: один и тот же
   товар, выглядящий на двух страницах по-разному, — спор с клиентом на ровном месте.

   ⚠️ «Похожие растения» на странице сорта плиткой НЕ являются и этот файл не везут: у них свои
   имена (`similar__*`, `pohozhaya__*`) и своя разметка без снимка. Долг записан в `STATE.md`.

   🔴 НИ ОДНОГО КОДА ЦВЕТА. Все цвета — именами из `tokens.css`: UI строится тремя дорожками
   одновременно, и три «своих» зелёных на одной странице появились бы молча. Проверяется
   машиной (`core/site/tests/test_catalog_appearance.py`, проверка 9А-0в-01), а не глазами.

   🔴 В ПЕРЕХОДАХ ЭТОГО ФАЙЛА МЕНЯЮТСЯ ТОЛЬКО `transform` И `opacity`. Спецификация v10,
   раздел 8, запрет 3. Замер 04.08.2026: переходы с размытием и обрезкой держали худшие пять
   процентов кадров на 20,8 мс, без них — на 7,1 мс, длинных кадров стало 1 вместо 20.
   Поэтому яркая кромка и глубокая тень под курсором не «переезжают» свойством `box-shadow`,
   а лежат готовым слоем `.card::after`, которому меняется одна прозрачность.

   Мобильный вид первым (3.3): число колонок задаёт минимальная ширина плитки, а не точки
   перелома под устройства, — телефон получает одну колонку сам. Замер 04.08.2026: плитка
   выходит 272 точки на мониторе, 316 на планшете и 356 на телефоне. */

/* ── Ширина страницы с сеткой ─────────────────────────────────────────────── */
/* 🔴 Правило стоит ЗДЕСЬ, а не в файле каждой страницы, и это не случайность раскладки:
   страницы, которые везут этот файл, — ровно те, у которых есть сетка плиток, а сетке нужна
   ширина больше текстовой колонки (Р-2). Положи его в `home.css` и `catalog.css` — получишь
   одно правило в двух местах, и они разъедутся на первой же правке (🅱️5).

   ⚠️ Текстовые страницы (корзина, страницы обязательств) этот файл не подключают вообще,
   поэтому своей ширины не меняют, и трогать их для этого не пришлось. */
/* 🔴 КОЛОНКА СТРАНИЦЫ ЗАДАЁТСЯ ПЕРЕМЕННОЙ, А НЕ ШИРИНОЙ `main`. До 08.08.2026 здесь стояло
   `main { max-width: var(--width-catalog) }`, и этого было мало: рамка сайта рисует ещё
   и подвал, а он о ширине страницы не знал ничего и вставал во всю ширину окна. На мониторе
   2540 текст начинался на 918-й точке, подвал под ним — на 19-й.

   Теперь страница объявляет СВОЮ колонку, а `main` и подвал берут её оба (`site.css`). */
:root {
  --width-page: var(--width-catalog);
}

/* ── Сетка плиток ─────────────────────────────────────────────────────────── */
/* 🔴 Число колонок задаёт минимальная ширина плитки, а не точки перелома под устройства
   (Р-2): на широком экране выходит три-четыре колонки, на планшете две, на телефоне одна,
   и ни одного правила под конкретный телефон писать не нужно.

   🔴 `min(15rem, 100%)`, а НЕ голые `15rem`. Найдено общим ревью, и случай не выдуманный:
   `rem` считается от базового шрифта браузера, а человек его увеличивает. При базовом
   размере 20 точек колонка требует 300 точек в поле телефона шириной 288 — сетка вылезает
   за край. `min` говорит «не шире того, что есть», и колонка просто становится уже.

   ⚠️ Зазор и отступ взяты ступенями лесенки (`--o-4`, `--o-5`), а не набраны числом:
   замер 04.08.2026 нашёл 41 разный отступ на девять файлов оформления при написанной
   и ни разу не подключённой лесенке.

   🔴 ЗАЗОР ПОДНЯТ НА СТУПЕНЬ — с 16 точек до 24. Правка Кирилла 07.08.2026, дословно:
   «раздвинь друг от друга окна каталогов, примерно на 35% от того как сейчас, со всех
   четырех сторон раздвинь друг от друга, и по бокам, и снизу/сверху».

   Одно свойство `gap` разводит плитки и по горизонтали, и по вертикали сразу — «со всех
   четырёх сторон» это и есть.

   ⚠️ Он назвал 35 % (это 21,6 точки), а взята следующая ступень лесенки — 24. Между ними
   две точки, различить их на экране нельзя; зато число вне лесенки заводит сорок второй
   отступ в проекте, и следующее окно уже не знает, откуда оно взялось. */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  gap: var(--o-5);
  margin: var(--o-5) 0 0;
  padding: 0;
  list-style: none;
}

/* Плитка раздела несёт два слова вместо снимка и цены, и колонка под неё нужна уже:
   растянутая до товарной, она была бы почти пустой. Имя блока заведено куском 11
   (`home/_categories.html`), ширину колонки перебивает `category-card.css` весом селектора. */
.cards--categories {
  grid-template-columns: repeat(auto-fill, minmax(min(11rem, 100%), 1fr));
}

/* ── Плитка ───────────────────────────────────────────────────────────────── */
/* 🔴 ПЛИТКА — ПРЕДМЕТ, ЛЕЖАЩИЙ ПОВЕРХ ЖИВОГО ФОНА, А НЕ ПРЯМОУГОЛЬНИК КРАСКИ. Спецификация
   v10, раздел 4: под каждой страницей идёт один закреплённый слой видео, и сплошная заливка
   плитки закрыла бы его наглухо. Поэтому корпус полупрозрачный (`--fon-kartochki`, доля 0,86
   снята с прототипа), а край не обводится тёмной линией, а подсвечивается светлой кромкой:
   тёмный штрих поверх видео читается как царапина на стекле.

   ⛔ `overflow: hidden` СНЯТ С ПЛИТКИ и перенесён на рамку снимка. Он здесь стоял, чтобы
   увеличенный снимок не вылезал за скруглённый угол, — и заодно обрезал бы слой `::after`
   с тенью, потому что тень лежит СНАРУЖИ границ плитки. Обрезает теперь тот, кому это нужно:
   рамка снимка (`.card__frame`) и рамка значка раздела (`category-card.css`). */
.card {
  position: relative;
  background: var(--fon-kartochki);
  border: 1px solid var(--frame);
  border-radius: var(--r-l);
  box-shadow: var(--ten-1);
  transition: transform var(--hod-medlenno);
}

/* 🔴 ЯРКАЯ КРОМКА И ГЛУБОКАЯ ТЕНЬ ЛЕЖАТ ГОТОВЫМ СЛОЕМ И ПРОЯВЛЯЮТСЯ ПРОЗРАЧНОСТЬЮ.
   Прямая запись — `transition: box-shadow, border-color` — выглядит короче и запрещена
   замером (шапка файла): браузер пересчитывает тень каждый кадр заново, а прозрачность
   отдаёт видеокарте. Слой повторяет границы плитки: `inset: -1px` покрывает её вместе
   с собственной кромкой в 1 точку, поэтому скругление совпадает точка в точку.

   `pointer-events: none` обязателен: слой лежит поверх ссылки, и без этой строки нажатие
   по плитке уходило бы в него, а не в товар. */
.card::after {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 2;
  border: 1px solid var(--frame-navedenie);
  border-radius: var(--r-l);
  box-shadow: var(--ten-3);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--hod-medlenno);
}

/* При наведении плитка приподнимается, и зажигается слой с кромкой и тенью. ⛔ Ничего
   не выезжает и не появляется содержимым: на телефоне наведения нет вообще, и спрятанное
   туда содержимое стало бы недоступным. */
.card:hover { transform: translateY(-6px); }
.card:hover::after { opacity: 1; }

/* Название подсвечивается зелёным: человек видит, что плитка целиком является ссылкой,
   ещё до нажатия.

   ⛔ ПОДЧЁРКИВАНИЕМ ЭТО НЕ ДЕЛАЕТСЯ. Здесь стояло `text-decoration: underline` — приём
   HyperUI, — и он прямо против требования Кирилла 02.08.2026, дословно: «нижние
   подчеркивания все убираем в визуале сразу». */
.card:hover .card__name {
  color: var(--akcent);
}

/* 🔴 Снятое с продажи ПРИГЛУШАЕТСЯ, а не прячется и не красится в красный: человек должен
   видеть, что мы этим торгуем, и позвонить. Предзаказ этого класса не получает — он обычный
   товар, его можно заказать сегодня, и путать эти два состояния уже пробовали (ревью 30.07). */
.card--none {
  opacity: 0.6;
}

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

   `position: relative` держит плашку остатка: она лежит поверх снимка, а стоит в разметке
   последней (разбор — у `.card__availability`).

   🔴 `overflow-wrap` стоит на ССЫЛКЕ, а не на названии: он наследуется, и одна строка
   закрывает все четыре факта плитки сразу — имя, латынь, цену и наличие. Текст здесь ЧУЖОЙ:
   названия сортов приезжают из справочников, а латинское имя вида с длинным сортом в кавычках
   перенести браузеру без разрешения нечем. Вырезаем класс, а не место, где он вылез (🅰️4-1). */
.card__link {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding-bottom: var(--o-5);
  color: var(--tekst);
  text-decoration: none;
  overflow-wrap: anywhere;
}

/* ── Рамка снимка ─────────────────────────────────────────────────────────── */
/* 🔴 ЗАЧЕМ У СНИМКА ПОЯВИЛАСЬ ОТДЕЛЬНАЯ РАМКА (перенос v10). Снимок теперь несёт на себе три
   слоя: пятно света под курсором, затемнение низа и — над ним — плашку остатка. Все три
   обязаны обрезаться по скруглённому углу и не задевать текст плитки, а лечь им некуда,
   пока снимок стоит прямо в колонке.

   `flex: none` обязателен: без него длинное название соседа по ряду отжало бы рамку по высоте,
   и снимки поехали бы разного размера.

   Скругление уменьшено на толщину кромки (`calc(... - 1px)`): рамка стоит ВНУТРИ кромки
   плитки, и одинаковый радиус оставил бы по углу светлый серп в одну точку. */
.card__frame {
  position: relative;
  display: block;
  flex: none;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: calc(var(--r-l) - 1px) calc(var(--r-l) - 1px) 0 0;
  background: var(--steklo-plitka);
}

/* 🔴 ПЯТНО СВЕТА ПОД КУРСОРОМ СТОИТ НЕПОДВИЖНО У ВЕРХНЕГО КРАЯ, А НЕ ХОДИТ ЗА МЫШЬЮ.
   В прототипе (`11-choreography.html`, `.tovar__blik`) координаты пятна ставит скрипт двумя
   переменными `--bx`/`--by`. Своего скрипта у этой дорожки нет и заводить его ей нельзя,
   а правило с `var(--bx, 50%)` без скрипта — мёртвая запись: она вечно берёт запасное
   значение и выглядит работающей. Поэтому свет падает сверху, как от солнца в фоне страницы,
   и зажигается прозрачностью. */
.card__frame::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(70% 60% at 50% 0%, var(--blik-navedeniya), transparent 70%);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--hod-medlenno);
}

.card:hover .card__frame::before { opacity: 1; }

/* Низ снимка затемнён, потому что под ним стоит название: без затемнения светлая макушка
   куста и светлые буквы сходятся вплотную, и подпись перестаёт читаться. Верхняя половина
   кадра остаётся чистой — товар важнее подписи. */
.card__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--zatemnenie-snimka);
  pointer-events: none;
}

/* Снимок заполняет рамку целиком и обрезается по короткой стороне, а не сплющивается:
   миниатюры приезжают 4:3, но плитка на узком экране бывает другой пропорции, и растянутое
   дерево видно сразу.

   ⚠️ `filter` здесь ПОСТОЯННЫЙ, а не анимируемый, и запрету это не противоречит: дорого
   пересчитывать размытие КАЖДЫЙ КАДР, а один раз при отрисовке — нет. Приглушение снято
   с прототипа: наши фотографии сняты в разную погоду, и на тёмной гамме самые яркие из них
   выпрыгивают из ряда. */
.card__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(.88) brightness(.94);
  transition: transform 700ms cubic-bezier(.22, .68, .24, 1);
}

.card:hover .card__photo { transform: scale(1.07); }

/* 🔴 БЕЗ СНИМКА ПЛИТКА ВСЁ РАВНО ДЕРЖИТ ФОРМУ — 04.08.2026, спецификация v7, пункт 3.
   Правка Кирилла по карточке абрикоса, дословно: «это выглядит некрасиво и нечитаемо».

   Что происходило до неё. Растению без единого снимка (в бою это «Шарафуга» и «Ива
   цельнолистная Хакуро Нишики») картинка не рисовалась вовсе, и название начиналось прямо
   от верхнего края плитки — на 167–210 точек выше, чем у соседей по ряду со снимком. Числа
   не на глаз: замер `tools/zamer/site-appearance.py` на трёх ширинах, страницы `/katalog/`,
   `/katalog/plodovye/`, `/katalog/abrikos/`.

   Высоту теперь держит рамка выше, а заглушка просто заполняет её. Пустой она не выглядит:
   в ней крупно стоит название растения.

   ⚠️ Это НЕ рамка «фото скоро» — такая рамка обещает то, чего нет. Это подпись вместо
   картинки: тот же предмет, названный словом. Приглушение снимка ей ни к чему — гасить
   нечего, а серые буквы стали бы ещё тише. */
.card__photo--net {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--o-4);
  background: var(--steklo-plitka);
  color: var(--tekst-tihiy);
  font-size: var(--t-m);
  text-align: center;
  filter: none;
  transition: none;
}

/* Увеличения под курсором у заглушки нет: увеличивать нечего, а поехавший текст читался бы
   как поломка. */
.card:hover .card__photo--net { transform: none; }

.card__name,
.card__latin,
.card__price {
  padding: 0 var(--o-4);
}

/* 🔴 КЕГЛЬ 19, А НЕ 17, И ЭТО ЗАМЕР ЧИТАЕМОСТИ. По APCA размер входит в контраст наравне
   с цветом: у 17 точек порог Lc 90, у 19 он падает до 75. Название плитки даёт в светлой
   комнате 79 — на 17 точках не проходит, на 19 проходит, и краску трогать не нужно.
   Замер 06.08.2026, `tools/zamer/text-contrast.py`. */
.card__name {
  margin-top: var(--o-4);
  font-size: var(--t-lid);
  font-weight: 600;
}

/* 🔴 Латынь на плитке ПЕСОЧНАЯ, а не приглушённая. Исправлено 01.08.2026 по макету:
   спецификация волны 2 говорила обратное, и это была ошибка координатора — макет утверждён
   Кириллом, спецификацию писал я. Обе дорожки, B и C, нашли расхождение независимо друг
   от друга и обе честно оставили как в спецификации, назвав его в отчёте.
   ⚠️ Роль `--akcent` с тех пор сменила краску с песочной на зелёную (v10, раздел 3), имя
   осталось прежним: перекрашивалась палитра, а не решение «латынь того же цвета, что ссылки». */
/* 🔴🔴 ЛАТЫНЬ НЕ ЧИТАЛАСЬ ДАЖЕ В ТЁМНОЙ КОМНАТЕ, и наш прежний замер этого не видел.
   06.08.2026, APCA: 13 точек обычного начертания требуют Lc 90, а зелёный `--akcent`
   на плитке давал 59. Проверка по WCAG 2 при этом печатала «ОК, с запасом» (7,64) —
   отношение яркостей не годится для тёмной темы, это сказано авторами APCA прямо.

   Взяты все три рычага сразу, потому что поодиночке ни один не закрывает порог:
     · кегль 13 → 17 и полужирное начертание опускают порог с 90 до 60;
     · краска `--akcent-latyn` светлее зелёного действия — 74 в тёмной комнате, 60 в светлой.
   Подложкой это не лечилось вовсе: расчёт показал, что порог не берётся даже полностью
   чёрным фоном под текстом.

   ⚠️ Курсив остаётся: видовое имя растения набирается курсивом, это правило ботаники,
   а не оформления. ⚠️ Зелёный остаётся зелёным — краска своя, но того же семейства,
   и решение «латынь того же цвета, что ссылки» этим не снято, а уточнено по замеру. */
.card__latin {
  font-style: italic;
  font-size: var(--t-m);
  font-weight: 700;
  color: var(--akcent-latyn);
}

/* 🔴 Цена крупно и прижата к низу (Р-1): `auto` сверху съедает весь свободный остаток ячейки,
   поэтому у всех плиток ряда цена оказывается на одной линии — глаз сравнивает цены,
   а не ищет их. Это и есть причина, по которой ссылка выше сделана колонкой во всю высоту. */
.card__price {
  margin-top: auto;
  padding-top: var(--o-3);
  font-size: var(--t-l);
  font-weight: 700;
  color: var(--tekst);
}

/* 🔴 ОСТАТОК ЛЕЖИТ ПЛАШКОЙ ПОВЕРХ СНИМКА, А В РАЗМЕТКЕ ОСТАЁТСЯ ПОСЛЕДНИМ. Приём взят
   у прототипа (`.tovar__est`: «160 шт.» в углу кадра), а порядок разметки менять нельзя
   по двум причинам сразу: страницу без стилей читает робот нейросети сверху вниз, и там
   наличие обязано стоять после цены, а проверка `test_9a_2b_23` держит этот порядок числом.
   Поэтому место назначается оформлением, а не перестановкой строк.

   ⚠️ Зелёную галочку рисует класс `in-stock` из рамки сайта, и здесь он не переопределяется:
   решение Кирилла 04.08.2026, дословно, — «галка просто визуальный декоративный эффект».

   `max-width` не украшение: самая длинная строка — «сейчас нет в продаже», и без предела
   плашка на узкой колонке телефона упёрлась бы в правый край рамки. */
.card__availability {
  position: absolute;
  z-index: 1;
  top: var(--o-3);
  left: var(--o-3);
  max-width: calc(100% - var(--o-6));
  padding: var(--o-1) var(--o-3);
  background: var(--fon-metki);
  border: 1px solid var(--frame);
  border-radius: var(--r-pill);
  color: var(--tekst-myagkiy);
  font-size: var(--t-xs);
  line-height: 1.5;
}

/* 🔴 ЧЕЛОВЕК, ПОПРОСИВШИЙ СИСТЕМУ УБРАТЬ ДВИЖЕНИЕ, ПОЛУЧАЕТ НЕПОДВИЖНУЮ ПЛИТКУ. Просьба
   приходит настройкой системы, а не кнопкой на сайте, и означает она не «мне не нравится»,
   а тошноту от движения. Плитка при этом остаётся полностью рабочей: кромка и тень
   зажигаются, просто мгновенно. */
@media (prefers-reduced-motion: reduce) {
  .card,
  .card::after,
  .card__frame::before,
  .card__photo {
    transition: none;
  }

  .card:hover { transform: none; }
  .card:hover .card__photo { transform: none; }
}
