/* ==========================================================================
   Мастерская — публичная часть сайта.
   Светлый, воздушный, чистый. Крупные фотографии, много белого фона.
   Тёмный фон — только в блоке «О мастере».
   ========================================================================== */

/* --- Шрифты (локальные, кириллица) --------------------------------------- */

@font-face {
  font-family: 'Unbounded';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url('../fonts/unbounded-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Unbounded';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url('../fonts/unbounded-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122;
}
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/golostext-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/golostext-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2122;
}

/* --- Токены ---------------------------------------------------------------- */

:root {
  --graphite: #14171C;
  --amber: #C98F4A;
  --amber-soft: #E7C9A1;
  --brass-text: #8A6A3E;
  --paper: #FFFFFF;
  --paper-2: #F7F6F3;
  --line: #E7E5E0;
  --text-on-light: #1B1D21;
  --text-muted: #6B6F76;
  --text-on-dark: #EFEFEF;
  --danger: #D14343;
  --font-heading: 'Unbounded', system-ui, sans-serif;
  --font-body: 'Golos Text', system-ui, sans-serif;
  --radius: 4px;
  /* Задание №64, пункт 5: крупные фотографии скругляются так же заметно,
     как маленькие плитки категорий (там было 16px литералом) — одно значение
     на всё, чтобы карточки, галерея и плитки не разъезжались по стилю. */
  --radius-photo: 16px;
  --pad-desktop: 32px;
  --pad-mobile: 16px;
  --photo-ratio: 3 / 4;
}

/* --- Сброс и база ------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
/* scroll-padding-top — шапка липкая (position:sticky), поэтому обычная
   прокрутка "к якорю" (например, ссылка "Перейти к содержимому" на
   #main — inc/partials/header.php, или просто открыть адрес с "#main"
   в конце) кладёт первый элемент вплотную к верху экрана, а сама шапка
   в этот момент уже "прилипла" туда же и рисуется поверх него (Задание
   №35 — были не видны и полоса категорий, и заголовок "Акции"). 80px —
   с запасом от фактической высоты шапки (69px на широком экране,
   77px на узком — там строка чуть выше из-за кнопки-бургера). */
html { -webkit-text-size-adjust: 100%; scroll-padding-top: 80px; }
/* Атрибут hidden должен побеждать всегда — иначе любое правило вида
   ".btn { display: flex }" его молча перебивает (авторские стили сильнее
   стилей браузера по умолчанию, даже когда [hidden] специфичнее по виду). */
[hidden] { display: none !important; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  background: var(--paper);
  color: var(--text-on-light);
}
img, picture { max-width: 100%; display: block; height: auto; }
a { color: inherit; }
h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 300;
  line-height: 1.25;
  margin: 0 0 0.6em;
}
h1 { font-size: clamp(24px, 3.4vw, 34px); }
h2 { font-size: clamp(20px, 2.6vw, 26px); }
p { margin: 0 0 1em; }
ul { margin: 0; padding: 0; list-style: none; }
button { font: inherit; color: inherit; }

/* Видимая рамка при переходе клавиатурой — на ЛЮБОМ фокусируемом элементе.
   Единственное место в макете, где остаётся акцентная обводка. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

.container {
  padding: 0 var(--pad-desktop);
}
@media (max-width: 640px) {
  .container { padding: 0 var(--pad-mobile); }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--amber);
  color: #fff;
  padding: 12px 20px;
  z-index: 200;
  font-weight: 600;
}
.skip-link:focus { left: 0; }

/* --- Тёмные/светлые секции ------------------------------------------------ */
/* Тёмный фон нигде на публичном сайте не используется, кроме .section-dark
   (блок «О мастере» на главной — единственное место). */

.section-dark {
  background: var(--graphite);
  color: var(--text-on-dark);
  padding: 56px 0;
}
.section-light {
  background: var(--paper);
  color: var(--text-on-light);
  padding: 56px 0;
}
.section-light.alt { background: var(--paper-2); }
/* Ряды товаров на главной (Акции/Выбор мастера, Задание 33) — стоят
   впритык друг к другу, оба .section-light без разделителя фона, поэтому
   полный padding: 56px 0 с обеих сторон складывался в 112px пустоты
   между ними, особенно заметно, когда в "Акциях" всего один-два товара
   и сама секция невысокая. */
.section-light--product-row { padding: 32px 0; }

.section-dark a.link-accent { color: var(--amber-soft); text-decoration: underline; }
.section-light a.link-accent { color: var(--brass-text); text-decoration: underline; }
.section-dark .muted { color: #B7BCC6; }
.section-light .muted { color: var(--text-muted); }

/* --- Шапка ------------------------------------------------------------- */

.site-header {
  background: var(--paper);
  color: var(--text-on-light);
  position: sticky;
  top: 0;
  z-index: 100;
  border-bottom: 1px solid var(--line);
}
.site-header__inner {
  /* padding-top/bottom (не шорткат "padding: 18px 0") — шорткат заодно
     обнулял padding-left/right, забивая боковые 32px/16px, которые уже
     даёт этому же элементу класс .container (Задание 32: из-за этого
     название сайта стояло вплотную к левому краю экрана). */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 18px;
  padding-bottom: 18px;
}
.site-header__logo {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 19px;
  text-decoration: none;
  color: var(--text-on-light);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Задание №66: ссылка была высотой ровно в строку текста (25px). */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.site-nav {
  display: flex;
  align-items: center;
  gap: 28px;
  flex-wrap: wrap;
}
.site-nav a {
  text-decoration: none;
  font-size: 15px;
  color: var(--text-on-light);
  padding: 4px 0;
  border-bottom: 1px solid transparent;
}
.site-nav a:hover { border-bottom-color: currentColor; }

/* --- Переход во вторую половину сайта (Задание №71) --------------------------
   Стоит последним в том же ряду, что «Каталог», «О мастере» и остальные, —
   значит виден на КАЖДОЙ странице. В Задании №70 он жил плиткой в полосе
   категорий и был только на главной: посетитель, пришедший из Instagram
   сразу на карточку товара, перехода не находил вовсе.
   Оформлен кнопкой, а не строчкой: это переход в другой раздел сайта, а не
   ещё один пункт меню, и по виду это должно быть понятно до нажатия. */
.site-nav .nav-partner {
  display: inline-flex;
  align-items: center;
  min-height: 38px;
  padding: 0 16px;
  max-width: 15em;              /* длинную надпись обрезаем, а не ломаем ряд */
  border-radius: 999px;
  border-bottom: none;          /* подчёркивание пунктов меню тут не нужно */
  background: var(--graphite);
  color: #fff;
  font-weight: 600;
  white-space: nowrap;
  transition: background 150ms ease;
}
.site-nav .nav-partner:hover {
  background: #000;
  border-bottom-color: transparent;
}
.site-nav .nav-partner > span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.site-nav .nav-partner:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.site-header__actions {
  display: flex;
  align-items: center;
  gap: 16px;
  flex: none;
}
/* Задание №66: было 40×40 — чуть меньше 44px, к которым тянутся пальцем.
   Это единственный способ открыть меню на телефоне, промахиваться по нему
   особенно обидно. */
.site-header__burger {
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  color: var(--text-on-light);
  cursor: pointer;
  padding: 0;
}
.site-header__burger svg { width: 24px; height: 24px; }

/* Совсем узкий экран: название в одну строку слева и помельче. */
@media (max-width: 640px) {
  .site-header__logo { font-size: 16px; max-width: 55vw; }
}

/* Пункты меню прячутся под кнопку-бургер, значок избранного остаётся на виду
   в правом углу.
   Задание №71: порог поднят с 640 до 900. Кнопка перехода во вторую половину
   заметно шире пункта меню (у «Корпоративных заказов» — 208px), и в промежутке
   660–1050 меню переставало помещаться в одну строку: шапка вырастала с 38 до
   98px и разъезжалась на две строки — замерено. Бургер тут уже готовый и
   проверенный способ, а кнопка в раскрывающемся меню оформлена ниже отдельно.
   Одного порога мало: надпись на кнопке задаёт владелец, и завтра она может
   стать вдвое длиннее — поэтому выше у кнопки ещё и предел ширины с
   многоточием, чтобы никакая надпись не могла разломать ряд. */
@media (max-width: 1024px) {
  .site-header__burger { display: flex; }
  .site-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 8px 16px rgba(0,0,0,0.06);
  }
  .site-nav.is-open { display: flex; }
  .site-nav a {
    padding: 14px var(--pad-mobile);
    border-bottom: 1px solid var(--line);
  }
  .site-nav a:last-child { border-bottom: none; }

  /* В раскрывающемся меню — тоже кнопка, а не ещё одна строчка списка
     (Задание №71): иначе переход в другой раздел ничем не отличался бы от
     «Контактов». Стоит первой (Задание №72), поля вокруг отделяют её от
     пунктов меню ниже. */
  .site-nav .nav-partner {
    margin: 12px var(--pad-mobile) 14px;
    padding: 0 18px;
    min-height: 46px;
    max-width: none;            /* в раскрытом меню места хватает */
    justify-content: center;
    border-bottom: none;
  }
}

.header-fav {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  color: var(--text-on-light);
  position: relative;
  /* Задание №66: сама ссылка была 22×22 — ровно по размеру значка.
     Поля доводят область нажатия до 44px, не двигая сам значок. */
  min-height: 44px;
  padding: 0 8px;
  margin-right: -8px;
}
.header-fav svg { width: 22px; height: 22px; }
.header-fav__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--amber);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
}
/* Сердечки и счётчик избранного целиком зависят от localStorage —
   без JavaScript от них нет никакой пользы, поэтому без JS их просто нет
   в интерфейсе, а не показывается сломанный нуль. */
.no-js .header-fav,
.no-js [data-fav-toggle] {
  display: none !important;
}

/* --- Кнопки -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 22px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 15px;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}
.btn-primary {
  background: var(--graphite);
  color: #fff;
}
.btn-primary:hover { background: #2b2f37; }
.btn-outline {
  background: transparent;
  border-color: var(--line);
  color: var(--text-on-light);
}
.btn-outline:hover { border-color: var(--text-on-light); }
.section-dark .btn-outline { border-color: rgba(255,255,255,0.35); color: var(--text-on-dark); }
.section-dark .btn-outline:hover { border-color: #fff; }
.btn-block { width: 100%; }

/* --- Кнопки связи (мессенджеры) — спокойные, без ярких заливок ------------- */

.msg-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 0 18px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: #fff;
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-on-light);
  transition: border-color 150ms ease;
}
.msg-btn:hover { border-color: var(--text-on-light); }
.msg-btn svg { width: 20px; height: 20px; flex: none; }
.msg-btn--telegram svg { color: #26A5E4; }
.msg-btn--whatsapp svg { color: #25D366; }
.msg-btn--viber svg { color: #7360F2; }
.msg-btn--instagram svg { color: #C1367B; }

/* --- Главная: первый экран ------------------------------------------------ */

/* Название магазина уже есть в шапке — здесь не повторяем. Короткая
   строка + кнопка, без серой заливки (фон совпадает с остальным сайтом),
   отступы вдвое меньше прежних — первый экран не должен целиком уходить
   под заголовок, ниже сразу видна витрина. */
.hero {
  background: var(--paper);
  color: var(--text-on-light);
  padding: 28px 0 24px;
}
.hero__tagline {
  font-size: 17px;
  color: var(--text-muted);
  margin-bottom: 16px;
  /* Ширина НЕ ограничена жёстко (никакого max-width в ch) — строка
     переносится, только когда реально не хватает ширины окна, а не всегда
     после условных 46 символов. На телефоне перенос всё равно случится
     сам, по ширине контейнера. */
}

/* --- Полоса категорий на главной (как на bodo.ua) --------------------------- */

/* Задание №56, пункт 2: вместо широких плиток с названием ПОВЕРХ фото —
   вертикальные плитки: скруглённое фото сверху, название под ним обычным
   тёмным текстом. На широком экране помещается около десяти, остальные
   листаются круглыми стрелками по краям полосы; на телефоне стрелок нет,
   полоса листается пальцем. В полосе — только категории верхнего уровня
   (подкатегории видны на странице самой категории и в фильтре каталога). */
.cat-strip-wrap { padding-top: 18px; }

.cat-strip {
  position: relative;
  /* Стрелки должны стоять по центру ФОТО, а не всей плитки вместе с
     подписью. Высота фото в CSS неизвестна (она вычисляется из ширины по
     aspect-ratio), зато известна высота блока подписи — на неё и делаем
     поправку: отступ 8px + подпись фиксированной высоты 34px. */
  --cat-label-block: 42px;
}

.cat-strip__track {
  --cat-per-view: 10;
  --cat-gap: 14px;
  display: flex;
  gap: var(--cat-gap);
  align-items: flex-start;
  list-style: none;
  margin: 0;
  padding: 4px 0 8px;
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  /* Полосу прокрутки прячем — листание идёт стрелками или пальцем. */
  scrollbar-width: none;
}
.cat-strip__track::-webkit-scrollbar { display: none; }

/* flex-basis стоит на самом флекс-элементе (<li>), а не на ссылке внутри —
   иначе реальная ширина плитки бралась бы из длины названия и плитки
   вставали бы "лесенкой" (это уже ловили в Задании №28).
   Ширина считается от видимой ширины полосы, чтобы в неё влезало ровно
   --cat-per-view плиток, сколько бы их ни было всего. */
.cat-strip__track > li {
  flex: 0 0 calc((100% - (var(--cat-per-view) - 1) * var(--cat-gap)) / var(--cat-per-view));
  scroll-snap-align: start;
  /* Задание №69. flex-basis задаёт ширину, но НЕ запрещает элементу стать
     шире: у флекс-элемента min-width по умолчанию auto, то есть он не может
     быть уже своего min-content. Название без пробелов («Сверхдлинноеодно…»)
     — это одно слово, и его min-content равен всей длине: плитка раздувалась
     до 478px там, где соседние были 120px. Ровно это и выглядит как
     «маленькие, средние, большие». Замерено в браузере. */
  min-width: 0;
}
@media (max-width: 1200px) { .cat-strip__track { --cat-per-view: 8; } }
@media (max-width: 1000px) { .cat-strip__track { --cat-per-view: 6; } }
@media (max-width: 780px)  { .cat-strip__track { --cat-per-view: 4.5; } }
@media (max-width: 560px)  { .cat-strip__track { --cat-per-view: 3.2; --cat-gap: 10px; } }

.cat-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: inherit;
}
.cat-tile__photo {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-photo);
  overflow: hidden;
  background: var(--paper-2);
  border: 1px solid var(--line);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.cat-tile__photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* --- Как фотография вписывается в плитку (Задание №72) -----------------------
   Плитка вертикальная 4:5, и горизонтальный снимок в неё целиком не влезает.
   Автоматически тут не выбрать: где-то важен центр кадра (тогда обрезать),
   где-то вся композиция (тогда вписать целиком). Выбор делает владелец у
   каждой категории отдельно, см. category_photo_fit().

   «Вписать целиком»: сам снимок показываем полностью (contain), а поля вокруг
   закрываем сильно размытой и растянутой копией его же. Белые поля выглядели
   бы как незагрузившаяся картинка, а размытая подложка держит цвет плитки и
   не спорит с самим снимком. */
.cat-tile__photo--contain > picture > img,
.cat-tile__photo--contain > img:not(.cat-tile__blur) {
  object-fit: contain;
  /* Над размытой подложкой, но без своего фона — иначе поля снова стали бы
     сплошной заливкой. */
  z-index: 1;
}
.cat-tile__blur {
  z-index: 0;
  object-fit: cover;
  filter: blur(14px) saturate(1.15);
  /* Растягиваем сильнее плитки: край размытия иначе виден светлой каймой. */
  transform: scale(1.25);
}
/* Без фото — светлая плитка с крупной первой буквой названия: пустой
   прямоугольник в ряду с фотографиями выглядит как незагрузившаяся
   картинка, буква сразу читается как оформление. */
.cat-tile__letter {
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: 28px;
  color: var(--text-muted);
  opacity: 0.65;
}
.cat-tile__label {
  /* Ровно две строки — не «минимум две», как было (Задание №69). min-height
     выравнивал одно- и двухстрочные названия, но названия из трёх и более
     строк спокойно росли дальше, и плитки в полосе вставали разной высоты:
     замерено 287px против 191px у соседей. Теперь высота задана жёстко, а
     лишнее отсекается многоточием — как у названий товаров в каталоге.
     Отсюда же берётся --cat-label-block выше, которым центруются стрелки. */
  height: 34px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* Длинное слово рвём — иначе оно не поместится ни в какое число строк. */
  overflow-wrap: anywhere;
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: 13px;
  line-height: 1.25;
  text-align: center;
  color: var(--text-on-light);
}
.cat-tile:hover .cat-tile__photo {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0,0,0,0.10);
}
.cat-tile:focus-visible .cat-tile__photo { outline: 2px solid var(--amber); outline-offset: 2px; }

.cat-strip__arrow {
  position: absolute;
  top: calc(50% - var(--cat-label-block) / 2 - 2px);
  transform: translateY(-50%);
  z-index: 2;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--paper);
  color: var(--text-on-light);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0,0,0,0.12);
}
.cat-strip__arrow:hover { background: var(--paper-2); }
.cat-strip__arrow svg { width: 18px; height: 18px; }
.cat-strip__arrow--prev { left: -14px; }
.cat-strip__arrow--next { right: -14px; }
/* Стрелки показываются, только когда есть куда листать: скрывает их
   assets/js/main.js атрибутом hidden, а display:flex выше сам по себе
   сильнее стандартного [hidden] — гасим его явно. */
.cat-strip__arrow[hidden] { display: none; }

@media (max-width: 780px) {
  /* Телефон/планшет — листание пальцем, стрелки не нужны (и мешали бы
     первому и последнему названию). */
  .cat-strip__arrow { display: none; }
  .cat-strip__track {
    /* Полоса дотягивается до краёв экрана, как весь остальной контент под
       ней, но первая плитка остаётся на своём месте по левому краю текста. */
    margin: 0 calc(var(--pad-mobile) * -1);
    padding-left: var(--pad-mobile);
    padding-right: var(--pad-mobile);
  }
}

/* Подкатегории над списком товаров на странице родительской категории —
   та же полоса, только заголовком помечено, что это внутри категории. */
.subcats { margin: 0 0 36px; }
.subcats__title {
  margin: 0 0 10px;
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: 15px;
  color: var(--text-muted);
}
/* --- Витрина "Выбор мастера" на главной ------------------------------------- */
/* Два вида на выбор владельца (настройка hits_style): .hits-row — один
   ряд обычных карточек каталога; .hits-mosaic — крупная плитка и мелкие
   вокруг, плитки заполняются фото целиком с обрезкой (.hits-tile ниже,
   общий для мозаики). */

/* Задание 46 — откат Задания 40: тогда ряды на главной свели к тем же
   брейкпоинтам, что и у сетки каталога (.product-grid), и обе полосы
   стали визуально неотличимы от каталога. Это была ошибка — на главной
   специально ДВА разных по размеру ряда: мелкие плитки "Акции" (карточка
   примерно как в каталоге) и заметно крупнее "Выбор мастера". У обоих —
   свой, более короткий набор брейкпоинтов (3 ступени, не 5, как у
   каталога) — на главной не нужна такая же плавность, а нужен контраст
   между двумя полосами.

   "Акции" — базовый .hits-row: 2 колонки на телефоне, 3 на узком экране,
   6 на широком (число и брейкпоинты подобраны так, чтобы на типичных
   широких экранах карточка была примерно того же размера, что и в
   каталоге — тот же смысл, что был у "тех же чисел", просто теперь не
   единым набором брейкпоинтов на все случаи, а отдельно подобранным). */
.hits-row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 18px 24px;
}
/* Ровно один ряд — PHP уже отдаёт не больше карточек, чем нужно на самой
   широкой ступени, но при более узких брейкпоинтах колонок меньше, чем
   карточек в DOM — без explicit display:none лишние просто перенеслись
   бы на вторую строку. Прячем их по номеру, открывая обратно на более
   широких брейкпоинтах.
   Задание №66: на телефоне не прячем НИЧЕГО. Раньше правило начиналось с
   базового уровня, и на экране 390 из шести акционных товаров были видны
   ровно два — остальные четыре лежали в разметке под display:none, и добыть
   их было нельзя никак: ряд не листается пальцем (это сетка, а не лента).
   Теперь на телефоне ряд переносится на следующие строки — все товары
   достаются обычной вертикальной прокруткой, без жеста, о котором надо
   догадаться. Требование "ровно один ряд" остаётся с 600px, где ряд и
   правда помещается целиком. */
@media (min-width: 600px) {
  .hits-row { grid-template-columns: repeat(3, 1fr); }
  .hits-row > .product-card:nth-child(n+4) { display: none; }
}
@media (min-width: 1200px) {
  .hits-row { grid-template-columns: repeat(6, 1fr); }
  .hits-row > .product-card:nth-child(n+4) { display: flex; }
}

/* "Выбор мастера" — тот же .hits-row, плюс модификатор --featured: те же
   три ступени, но вдвое меньше колонок (2 / 2 / 4, см. №66 ниже) — при той же ширине
   экрана карточка ощутимо крупнее, чем в "Акциях" и в каталоге.

   Задание №64, пункт 4: правил "спрятать лишние по счёту" здесь больше НЕТ.
   Раньше карточки сверх удвоенного числа колонок гасились display:none — из-за
   этого сколько бы товаров ни отдал PHP (8, 12, 16), на экране всегда
   оставалось ровно два ряда, а остальные были в разметке, но невидимы:
   владелец менял число в настройках и не видел разницы. Теперь сетка просто
   переносит всё, что не поместилось, на следующую строку — сколько задано
   (или сколько отмечено), столько и показывается. У "Акций" выше правила
   остаются: там нужен ровно ОДИН ряд. */
/* Задание №66: на телефоне здесь была ОДНА колонка — карточка во всю ширину
   экрана, и девять отмеченных товаров давали девять этажей подряд, а рядом
   «Акции» шли в две колонки: две полосы на одной странице вели себя
   по-разному. На 390 контрасту размеров всё равно негде проявиться, поэтому
   до 600px обе полосы — две колонки, а разница в крупности остаётся там,
   где на неё есть место: 3 против 2 и 6 против 4. */
.hits-row--featured { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 600px) {
  .hits-row--featured { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1200px) {
  .hits-row--featured { grid-template-columns: repeat(4, 1fr); }
}

/* Раньше здесь был max-width:315px на карточку — при снятом ограничении
   ширины страницы (Задание 31) шесть широких колонок при таком потолке
   стали давать пустой зазор ПОСЛЕ каждой карточки: та самая пустота по
   краям, только теперь между карточками, а не у стен экрана. Фото и так
   не "взрывается" по высоте без этого предела — aspect-ratio держит
   пропорцию 3:4 при любой ширине колонки, просто пропорционально выше. */

/* Вид "Мозаика": первая плитка крупная (на всю высоту), остальные —
   в один столбец сбоку, по одной на строку. Число строк задаёт PHP
   через --hm-rows, поэтому пустых ячеек не бывает при любом количестве
   товаров. Высота всей мозаики ограничена, чтобы плитки не растягивали
   первый экран. */
.hits-mosaic {
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: repeat(var(--hm-rows, 1), 1fr);
  gap: 16px;
  height: min(560px, 58vh);
}
.hits-mosaic--single { grid-template-columns: 1fr; height: min(460px, 50vh); }
.hits-mosaic .hits-tile--big { grid-column: 1; grid-row: 1 / -1; }
.hits-mosaic .hits-tile--small { grid-column: 2; }
@media (max-width: 639px) {
  .hits-mosaic {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    height: auto;
  }
  .hits-mosaic .hits-tile--big,
  .hits-mosaic .hits-tile--small { grid-column: 1; grid-row: auto; height: auto; aspect-ratio: var(--photo-ratio); }
}

.hits-tile {
  position: relative;
  display: block;
  height: 100%;
  border-radius: var(--radius-photo);
  overflow: hidden;
  text-decoration: none;
  background: var(--paper-2);
}
.hits-tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 300ms ease;
}
.hits-tile:hover img, .hits-tile:focus-visible img { transform: scale(1.04); }
/* Подпись всегда прижата к нижнему краю плитки — position:absolute не
   зависит ни от размера плитки, ни от того, есть фото или нет, поэтому
   не может "повиснуть" посередине пустого места. */
.hits-tile__info {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 14px;
  color: #fff;
  background: linear-gradient(0deg, rgba(0,0,0,0.6), transparent 65%);
}
.hits-tile__title {
  /* Как в каталоге: длинное название — в две строки, дальше многоточие,
     а не три строки, съедающие всю плитку. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: 15px;
  margin-bottom: 4px;
}
.hits-tile__price {
  display: block;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 16px;
}
/* .price-old по умолчанию серый — на тёмной подложке плитки (фото + градиент)
   он теряется, нужен светлый полупрозрачный, а не приглушённый тёмный. */
.hits-tile__info .price-old { color: rgba(255, 255, 255, 0.65); }
/* Без фото — не серый ящик с "Фото пока нет" (для витрины это выглядит
   как ряд поломок), а просто светлая карточка с названием и ценой прямо
   на фоне, тем же текстом, что и везде. */
.hits-tile--no-photo {
  border: 1px solid var(--line);
}
.hits-tile--no-photo .hits-tile__info {
  color: var(--text-on-light);
  background: none;
}

/* --- Сетка товаров --------------------------------------------------------- */

/* Задание 40 — откат Задания 38: фиксированное число колонок по ширине
   экрана, без auto-fill и без JS. Если товаров больше, чем колонок,
   лишние просто уходят на следующую строку — так и задумано, ряд не
   обязан быть заполнен идеально ровно. Свой набор брейкпоинтов: 2 / 3 /
   4 / 5 / 6 колонок — каталог не трогаем (Задание 46), у рядов на
   главной (.hits-row выше) теперь СВОИ, более короткие брейкпоинты. */
.product-grid {
  display: grid;
  gap: 18px 24px;
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 600px) {
  .product-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 900px) {
  .product-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (min-width: 1200px) {
  .product-grid { grid-template-columns: repeat(5, 1fr); }
}
@media (min-width: 1600px) {
  .product-grid { grid-template-columns: repeat(6, 1fr); }
}

/* Задание №66. Грид-элемент по умолчанию имеет min-width:auto, то есть не
   может стать уже своего min-content. Название из одних цифр без пробелов
   (артикул, размер, «8901234567890…») — это одно слово, его min-content равен
   всей его длине: колонка раздвигалась до 506px при экране 390, и ВСЯ
   страница уходила в горизонтальную прокрутку — главная, каталог, избранное,
   поиск. Замерено: документ 671px при окне 390.
   min-width:0 разрывает эту связь, а overflow-wrap:anywhere разрешает рвать
   слово в любом месте — только тогда -webkit-line-clamp ниже получает две
   настоящие строки и ставит многоточие. (break-word здесь не годится: он не
   влияет на расчёт min-content, то есть сетку бы всё равно раздувало.) */
.product-grid > *,
.hits-row > * { min-width: 0; }

.product-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: var(--text-on-light);
  min-width: 0;
}
.product-card__body,
.product-card__title,
.product-card__price { min-width: 0; }
.product-card__photo {
  position: relative;
  aspect-ratio: var(--photo-ratio);
  overflow: hidden;
  background: var(--paper-2);
  border-radius: var(--radius-photo);
}
.product-card__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 300ms ease;
}
.product-card:hover .product-card__photo img,
.product-card:focus-visible .product-card__photo img { transform: scale(1.06); }
.product-card__body { padding: 14px 2px 0; }
.product-card__title {
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: 15px;
  margin: 0 0 6px;
  line-height: 1.35;
  /* Длинные названия — в две строки, дальше многоточие, а не обрезка
     на произвольном месте и не растягивание карточки вниз до бесконечности. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}
/* Задание №66. Цена вместе с обозначением валюты — один неразрывный кусок:
   «89 999,10 Br» рвалось прямо посередине числа, потому что и разделитель
   тысяч, и пробел перед «Br» были обычными пробелами (это чинится в money()
   в inc/template.php — там теперь неразрывные), а строка при этом переносилась
   где придётся и давала три-четыре этажа вместо одного.
   Здесь — вторая половина: старая и новая цена становятся двумя отдельными
   кусками flex-строки. Перенос разрешён МЕЖДУ ними и запрещён внутри каждой,
   а row-gap задаёт зазор, чтобы зачёркнутая цена не наезжала на новую.
   Места мало — уменьшаем шрифт, а не переносим. */
.product-card__price {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 18px;
  color: var(--text-on-light);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  line-height: 1.35;
}
.product-card__price > * { white-space: nowrap; }
@media (max-width: 480px) {
  .product-card__price { font-size: 16px; }
}

/* --- Скидка (Задание 28) — одна и та же разметка/классы на карточке,
   странице товара и в избранном (см. render_product_price()/
   render_discount_badge() в inc/template.php). ------------------------- */
.price-old {
  text-decoration: line-through;
  color: var(--text-muted);
  font-weight: 400;
  font-size: 0.82em;
}
.price-new { color: var(--danger); }
.discount-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  background: var(--danger);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 13px;
  line-height: 1;
  padding: 5px 8px;
  border-radius: var(--radius);
}

/* --- Заглушка "фото пока нет" ----------------------------------------------- */

.photo-placeholder {
  /* position:absolute — гарантированно вынимает подпись из потока, чтобы
     она никогда не могла растянуть кадр с заданной пропорцией (родитель
     всегда position:relative). Раньше была просто width/height:100%, и в
     кадре 3:4 это иногда давало на глаз 3:4.3 — рамка растягивалась под
     содержимое, а не содержимое сжималось под рамку. */
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: #FAFAFA;
  color: #B0B3B8;
}
.photo-placeholder svg { width: 40px; height: 40px; }
.photo-placeholder span { font-size: 13px; }

/* --- Сердечко избранного ----------------------------------------------------- */

/* Задание №66: было 36×36 — меньше 44px, к которым тянутся пальцем.
   Кружок вырос, значок внутри остался прежним. */
.fav-heart {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.9);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
}
.fav-heart svg { width: 19px; height: 19px; color: var(--text-on-light); }
.fav-heart svg path { fill: none; stroke: currentColor; stroke-width: 1.8; }
.fav-heart.is-active svg { color: var(--danger); }
.fav-heart.is-active svg path { fill: currentColor; }

.fav-heart--inline {
  position: static;
  width: 46px;
  height: 46px;
  background: #fff;
  border: 1px solid var(--line);
}

/* --- Фильтры и поиск каталога ----------------------------------------------- */

.catalog-controls { margin-bottom: 24px; }
.search-bar {
  display: flex;
  align-items: center;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
}
.search-bar__input {
  flex: 1;
  min-width: 0;
  min-height: 52px;
  padding: 0 22px;
  border: none;
  background: none;
  font-size: 16px;
  font-family: var(--font-body);
  color: var(--text-on-light);
}
.search-bar__input:focus { outline: none; }
.search-bar__btn {
  flex: none;
  width: 44px;
  height: 44px;
  margin-right: 4px;
  border: none;
  border-radius: 50%;
  background: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--text-on-light);
}
.search-bar__btn:hover { background: var(--paper-2); }
.search-bar__btn svg { width: 20px; height: 20px; }
/* Обводка фокуса — на самой строке, а не на скрытом системном контуре
   инпута без границы, иначе при переходе клавиатурой не видно, что
   вообще в фокусе. */
.search-bar:focus-within {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
  border-radius: 999px;
}
.search-bar__input:focus-visible { outline: none; }

/* Одна ровная строка категорий верхнего уровня — и всё (Задание №60).
   Подкатегории здесь не показываются ни в каком виде: до №59 они лежали
   вложенным списком внутри пункта родителя, в №59 стали вторым рядом — и то
   и другое дублировало плитки блока «В категории „…“», через которые в
   подраздел и заходят. */
.cat-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 24px;
  margin-bottom: 44px;
}
.cat-filter a {
  text-decoration: none;
  font-size: 15px;
  color: var(--text-muted);
  padding: 6px 0;
  border-bottom: 2px solid transparent;
  /* Задание №69: третье место, где неразрывное название всё ломало. Флекс-
     элемент не может стать уже своего min-content, а название без пробелов —
     это одно слово: ссылка растягивалась до 431px при экране 390 и уводила
     каталог в горизонтальную прокрутку (замерено). Плитки категорий и
     карточки товаров чинились тем же способом. */
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.cat-filter a.is-active {
  color: var(--text-on-light);
  border-bottom-color: var(--text-on-light);
}
/* Открыта подкатегория — самой её в строке нет, подсвечиваем родителя:
   тёмный текст, но подчёркивание слабее, чем у выбранной напрямую
   категории — мы не на его странице, а внутри его раздела. */
.cat-filter a.is-ancestor {
  color: var(--text-on-light);
  border-bottom-color: var(--line);
}
.cat-filter a:hover { color: var(--text-on-light); }

/* Задание №66: ссылки категорий были 32px в высоту — меньше 44px, к которым
   тянутся пальцем, а промахнуться легко, соседи стоят вплотную.
   Растягивать саму ссылку нельзя: подчёркивание выбранной категории — это её
   border-bottom, и с большими полями оно отъехало бы от текста. Поэтому
   область нажатия добирает невидимый слой поверх ссылки, а вид не меняется.
   Зазор между рядами поднят с 8 до 14px, чтобы области соседних рядов не
   перекрывали друг друга (32 + 14 = 46 > 44). */
@media (max-width: 640px) {
  .cat-filter { gap: 14px 20px; }
  .cat-filter a { position: relative; }
  .cat-filter a::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
  }
}

/* --- Пагинация -------------------------------------------------------------- */

.pagination {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-top: 32px;
}
.pagination a, .pagination span {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius);
  text-decoration: none;
  border: 1px solid var(--line);
}
.pagination .is-current { background: var(--graphite); color: #fff; border-color: var(--graphite); }

/* --- Карточка товара --------------------------------------------------------- */

.product-page {
  display: grid;
  gap: 40px;
  grid-template-columns: 1fr;
}
/* Задание №66: та же ловушка min-width:auto, что и у карточек каталога —
   без этого длинное название товара (или кадр галереи) раздвигало колонку
   и уводило страницу в горизонтальную прокрутку. Замерено: документ 820 при
   окне 390. */
.product-page > * { min-width: 0; }
.product-info__title { overflow-wrap: anywhere; }
@media (min-width: 900px) {
  .product-page { grid-template-columns: 1.1fr 1fr; align-items: start; }
  /* justify-self: start — колонка с фото не растягивается на всю ширину
     грид-трека, а сжимается до собственной ширины (см. .gallery ниже) —
     так фото и миниатюры под ним остаются одной ширины, а не расползаются
     на весь столбец, когда высота (не ширина) — то, что реально ограничивает
     размер кадра. */
  .gallery-col { justify-self: start; max-width: 100%; }
}

/* Высота главного фото ограничена ~70vh — вместе с названием, ценой и
   кнопками ниже страница должна помещаться в окно без прокрутки на
   типичной высоте экрана. Ширина считается от высоты через aspect-ratio,
   а не наоборот — на широком мониторе колонка "1.1fr" сама по себе шире,
   чем нужно для кадра 3:4 при разумной высоте.
   ВАЖНО: именно height:70vh, а не max-height — с max-height высота
   формально остаётся "auto" (не считается definite-размером), и браузер
   не может вывести из неё ширину через aspect-ratio: при снятом пределе
   ширины страницы (Задание 31) колонка галереи схлопывалась в 0×0 — фото
   и заглушка пропадали целиком (Задание 32). max-width:100% ниже всё
   равно подстрахует на узком экране, если 70vh в пересчёте на ширину
   не помещается в колонку. */
.gallery {
  position: relative;
  aspect-ratio: var(--photo-ratio);
  height: 70vh;
  width: auto;
  max-width: 100%;
  margin: 0 auto;
  border-radius: var(--radius-photo);
  overflow: hidden;
  background: var(--paper-2);
}
/* Задание №66. На телефоне ограничивающий размер — ШИРИНА, а не высота.
   height:70vh при пропорции 3:4 давало ширину 0.75 × 70vh: на экране 390×844
   это 443px — кадр шире экрана, и КАЖДАЯ страница товара уходила в
   горизонтальную прокрутку (замерено: документ 459 при окне 390; на товаре
   без фото — ровно то же, значит дело не в снимке, а в правиле).
   max-width:100% выше не спасал: колонка галереи — грид-элемент с
   min-width:auto, и он сам раздвигался под кадр (см. .product-page ниже).
   До 900px, где колонка всё равно одна, считаем кадр от ширины. */
@media (max-width: 899px) {
  .gallery { height: auto; width: 100%; }
}
.gallery__viewport {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  touch-action: pan-y;
}
.gallery__track {
  display: flex;
  height: 100%;
  transition: transform 250ms ease;
}
.gallery__slide {
  flex: 0 0 100%;
  height: 100%;
}
.gallery__slide img { width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.gallery__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px; height: 44px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.85);
  color: var(--text-on-light);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.gallery__arrow[data-dir="prev"] { left: 12px; }
.gallery__arrow[data-dir="next"] { right: 12px; }
.gallery__dots {
  position: absolute;
  bottom: 12px;
  left: 0; right: 0;
  display: flex;
  justify-content: center;
  /* Кнопки теперь 44px, зазор между точками задаёт не gap, а сами кнопки. */
  gap: 0;
}
/* Задание №66: точки были 8×8 — попасть пальцем нельзя. Сама точка осталась
   8px, но рисуется фоном по центру кнопки 44×44: область нажатия большая,
   вид прежний. */
.gallery__dot {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  position: relative;
}
.gallery__dot::before {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 8px; height: 8px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: rgba(255,255,255,0.7);
  border: 1px solid rgba(0,0,0,0.15);
}
.gallery__dot.is-active::before { background: var(--graphite); border-color: var(--graphite); }
.gallery__empty {
  position: relative;
  aspect-ratio: var(--photo-ratio);
  height: 70vh;
  width: auto;
  max-width: 100%;
  margin: 0 auto;
  border-radius: var(--radius-photo);
  overflow: hidden;
}
.gallery__fav {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
}

/* Миниатюры остальных фото — строкой под главным кадром. Показываются,
   только когда фото больше одной (это решает сам PHP-шаблон, не рисуя
   разметку вовсе). */
.gallery__thumbs {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  overflow-x: auto;
  padding-bottom: 2px;
}
.gallery__thumb {
  flex: 0 0 60px;
  aspect-ratio: var(--photo-ratio);
  border-radius: 4px;
  overflow: hidden;
  border: 2px solid transparent;
  padding: 0;
  background: var(--paper-2);
  cursor: pointer;
}
.gallery__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery__thumb.is-active { border-color: var(--graphite); }

.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 22, 0.94);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 300;
  padding: 24px;
}
.lightbox.is-open { display: flex; }
.lightbox img { max-width: 100%; max-height: 90vh; object-fit: contain; touch-action: pan-y; }
.lightbox__close {
  position: absolute;
  top: 16px; right: 16px;
  width: 44px; height: 44px;
  border-radius: 999px;
  background: rgba(255,255,255,0.15);
  color: #fff;
  border: none;
  font-size: 22px;
  cursor: pointer;
  z-index: 1;
}
.lightbox__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px; height: 48px;
  border-radius: 999px;
  background: rgba(255,255,255,0.15);
  color: #fff;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 1;
}
.lightbox__arrow[data-dir="prev"] { left: 16px; }
.lightbox__arrow[data-dir="next"] { right: 16px; }
.lightbox__arrow svg { width: 24px; height: 24px; }

.product-info { position: relative; }
.product-info__title { font-weight: 400; }
.product-info__price {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 26px;
  margin-bottom: 20px;
  /* Задание №66 — то же, что и у цены на карточке выше. */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 10px;
  line-height: 1.3;
}
.product-info__price > * { white-space: nowrap; }
@media (max-width: 480px) {
  .product-info__price { font-size: 22px; }
}
.product-info__category {
  display: inline-block;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 12px;
}
/* white-space: pre-line убран (Задания №64-65): описание приходит готовой
   разметкой из rich_text(), а кнопки редактора расставляют в ней переносы и
   отступы для читаемости исходника — с pre-line эти переносы становились
   видимыми пустотами, и список растягивался вдвое.
   Ссылки внутри описания подчёркиваем: без подчёркивания их не отличить от
   обычного текста, цвет у них тот же. */
.product-info__description { margin-bottom: 28px; }
.product-info__description a { text-decoration: underline; }
.contact-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.product-reviews { margin-top: 48px; }

/* --- Отзывы ------------------------------------------------------------------ */
/* Владелец вносит сам, из переписок — карточка нарочно скромная, без
   декоративных звёзд-мишуры: оценка (если есть) — маленькая строка того
   же приглушённого цвета, что и остальной приграничный текст, не яркая
   плашка. */

.review-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 20px 24px;
  grid-template-columns: repeat(1, 1fr);
  align-items: start;
}
@media (min-width: 640px) { .review-list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .review-list { grid-template-columns: repeat(3, 1fr); } }

.review-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}
.review-card__photo {
  aspect-ratio: var(--photo-ratio);
  max-height: 260px;
  overflow: hidden;
  margin: -20px -20px 0;
}
.review-card__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.review-card__head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.review-card__author { font-family: var(--font-heading); font-weight: 600; font-size: 16px; }
.review-card__city { color: var(--text-muted); font-size: 14px; }
.review-card__rating { color: var(--brass-text); font-size: 14px; letter-spacing: 1px; }
.review-card__text {
  margin: 0;
  line-height: 1.6;
  color: var(--text-on-light);
  white-space: pre-wrap;
}
.review-card__text--clamp {
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.review-card__foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-top: auto;
  padding-top: 6px;
  flex-wrap: wrap;
}
.review-card__date { font-size: 13px; color: var(--text-muted); }
.review-card__product { font-size: 13px; }

/* --- Плавающая кнопка связи -------------------------------------------------- */

.floating-contact {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 150;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}
.floating-contact__menu {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 4px;
}
.floating-contact:not(.is-open) .floating-contact__menu { display: none; }
.floating-contact__item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 16px 0 14px;
  border-radius: 999px;
  background: #fff;
  color: var(--text-on-light);
  border: 1px solid var(--line);
  text-decoration: none;
  font-size: 14px;
  box-shadow: 0 4px 18px rgba(0,0,0,0.12);
}
.floating-contact__item svg { width: 20px; height: 20px; flex: none; }
.floating-contact__item--telegram svg { color: #26A5E4; }
.floating-contact__item--whatsapp svg { color: #25D366; }
.floating-contact__item--viber svg { color: #7360F2; }
.floating-contact__item--instagram svg { color: #C1367B; }
.floating-contact__toggle {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--graphite);
  color: #fff;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0,0,0,0.25);
}
.floating-contact__toggle svg { width: 26px; height: 26px; }

/* Не перекрывать кнопки карточки товара на телефоне — снизу страницы всегда
   свободное поле под плавающую кнопку. */
body { padding-bottom: 84px; }
@media (min-width: 900px) { body { padding-bottom: 0; } }

/* --- Подвал ------------------------------------------------------------- */

.site-footer {
  background: var(--paper-2);
  color: var(--text-muted);
  border-top: 1px solid var(--line);
  padding: 40px 0;
  font-size: 14px;
}
.site-footer a { text-decoration: none; color: var(--text-muted); }
.site-footer a:hover { color: var(--text-on-light); }
.footer-grid {
  display: grid;
  gap: 24px;
  grid-template-columns: 1fr;
}
@media (min-width: 700px) { .footer-grid { grid-template-columns: repeat(3, 1fr); } }
.footer-heading { color: var(--text-on-light); font-family: var(--font-heading); margin-bottom: 8px; }
.footer-contacts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}
/* Задание №66: в подвале кнопки мессенджеров были 38px — ниже 44px.
   Сделаны компактнее основных не высотой, а полями и кеглем. */
.footer-contacts .msg-btn { min-height: 44px; padding: 0 12px; font-size: 13px; }
.footer-contacts .msg-btn svg { width: 16px; height: 16px; }

/* --- Простые текстовые страницы --------------------------------------------- */

.prose { max-width: 68ch; }
.prose h1 { margin-bottom: 0.4em; }

/* Задание №64, пункт 8: описание товара и тексты страниц приходят сюда уже
   готовой разметкой (rich_text(), inc/richtext.php), а не сырым текстом.
   white-space: pre-line убран: он был нужен, пока переносы выводились как
   есть, а теперь абзацы и <br> расставлены заранее — иначе каждый перенос в
   исходнике давал бы ЕЩЁ один пустой отступ поверх настоящего абзаца.
   Маркеры списков возвращаем: глобальное ul { list-style: none } в начале
   файла снимает их у меню и полос, но в описании товара список без маркеров
   читается как набор оборванных строк. */
.prose p { margin: 0 0 1em; }
.prose ul, .prose ol { margin: 0 0 1em; padding-left: 1.4em; }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }
.prose li { margin-bottom: 0.3em; }
.prose a { color: var(--text-on-light); }

/* --- Избранное --------------------------------------------------------------- */

.fav-empty {
  padding: 60px 0;
  text-align: center;
  color: var(--text-muted);
}
.fav-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
}

/* --- 404 ------------------------------------------------------------------- */

.state-page {
  min-height: 50vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 16px;
  padding: 60px 20px;
}
.state-page__code {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 56px;
  color: var(--text-muted);
}

/* --- Страница «Контакты» (Задание №64, пункт 7) ----------------------------- */
/* Две колонки «подпись — значение»: на широком экране читается таблицей, на
   телефоне подпись встаёт над значением, а не жмётся рядом с ним. */
.contact-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 10px 24px;
  margin: 0 0 28px;
  max-width: 640px;
}
.contact-list dt { color: var(--text-muted); font-size: 15px; }
.contact-list dd { margin: 0; font-size: 16px; }
.contact-list dd a { color: var(--text-on-light); }
@media (max-width: 560px) {
  .contact-list { grid-template-columns: 1fr; gap: 2px; }
  .contact-list dd { margin-bottom: 12px; }
  /* Задание №66: телефон и почта на странице «Контакты» — главное, ради чего
     на неё заходят с телефона, а ссылки были высотой в строку текста (20px). */
  .contact-list dd a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* --- Ссылки в подвале на телефоне (Задание №66) ------------------------------
   Разделы, телефон и почта в подвале — это просто ссылки внутри <p>, высотой
   ровно в строку текста (17px). На телефоне это единственная навигация в
   нижней половине страницы, и промахнуться по ней очень легко. Разводим
   строки и добавляем каждой ссылке область нажатия по высоте строки. */
@media (max-width: 640px) {
  .footer-grid p > a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .footer-grid p { margin: 0; }
}
/* Отдельно стоящие ссылки-призывы на главной («Все отзывы →», «Подробнее →»,
   почта в блоке контактов) — тоже высотой в строку текста (Задание №66). */
@media (max-width: 640px) {
  a.link-accent {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}
/* --- Поля ввода на телефоне (Задание №66) ------------------------------------
   Safari на iPhone САМ увеличивает страницу, когда встаёшь в поле с кеглем
   меньше 16px, — и содержимое «уезжает» из виду прямо во время набора,
   а обратно страница уже не возвращается. Замерено: подпись к фотографии в
   форме товара — 13px, выбор категории в списке товаров — 14px.
   16px — это порог самого браузера, а не круглое число «для красоты». */
@media (max-width: 640px) {
  input[type="text"], input[type="password"], input[type="number"],
  input[type="email"], input[type="tel"], input[type="url"],
  input[type="search"], input[type="date"], textarea, select {
    font-size: 16px;
  }
}

