/* ══════════════════════════════════════════
   shop-product.css — блок «наша книга на продажу» наверху /Book/{id}
   (24.10.2026, бриф docs/2026-10-24-own-books-shop-backend-brief.md, B1.4; разметка —
   Views/Book/_ShopProductBlock.cshtml и _ShopProductGallery.cshtml).

   Requires: shared-detail.css (токены --card/--surface/--card-radius/--shadow-c), site.css (.sr-only,
   --primary*, --text*), при 2+ фото — artwork-carousel.css (каркас ленты; грузится РАНЬШЕ этого файла,
   правила ниже переопределяют точки на миниатюры).

   Цвета — только токены сайта, поэтому тёмная тема работает сама; ниже переопределены лишь тени (на тёмном
   фоне прежние альфы не видны — как у .bk__card в dark-theme.css) и мелкий красный текст (чип, «Осталось N»,
   ссылки): красный бренда на тёмной карточке не набирает контраста.
   Поля и ширина — как у карточки книги (.bk): блок и карточка под ним стоят в одну колонку.
   ══════════════════════════════════════════ */

.shop {
  /* «В продаже» — зелёный, своя пара на обе темы (в токенах сайта зелёного нет). #2b7449, а не #2f7d4f
     (05.11.2026, браузерный QA WEB-06): мелкий текст чипа на своей зелёной подложке давал 4,4:1, стало 5:1. */
  --shop-ok: #2b7449;
  --shop-ok-bg: rgba(47, 125, 79, 0.1);
  --shop-shadow:
    0 1px 2px rgba(var(--shadow-c), 0.04),
    0 4px 12px rgba(var(--shadow-c), 0.04),
    0 16px 40px rgba(var(--shadow-c), 0.06);
  max-width: 1120px;
  margin: 0 auto 24px;
  padding: 0 32px;
  animation: cardReveal 0.6s var(--ease) both;
}

/* ── Карточка: фото | цена и кнопка ── */

.shop__card {
  display: grid;
  grid-template-columns: minmax(0, 11fr) minmax(0, 10fr);
  overflow: hidden;
  background: var(--card);
  border-radius: var(--card-radius);
  box-shadow: var(--shop-shadow);
}
.shop__card--no-media {
  grid-template-columns: minmax(0, 1fr);
}

.shop__media {
  min-width: 0;
  padding: 24px;
  background: var(--surface);
}

.shop__buybox {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  min-width: 0;
  padding: 36px 36px 32px;
}

.shop__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.shop__status {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 100px;
  background: var(--primary-light);
  /* --primary-hover: красный бренда на своей розовой подложке — 4,49:1, чуть ниже порога для мелкого текста. */
  color: var(--primary-text-hover);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
}
.shop__status--onSale {
  background: var(--shop-ok-bg);
  color: var(--shop-ok);
}
.shop__status--soldOut,
.shop__status--archived {
  background: var(--surface);
  color: var(--text-2);
}
.shop__age {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border: 1.5px solid var(--text-3);
  border-radius: 6px;
  color: var(--text-2);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.2;
}

.shop__title {
  margin: 0;
  color: var(--text);
  font-family: "Lora", Georgia, serif;
  font-size: 30px;
  font-weight: 500;
  line-height: 1.22;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
.shop__summary {
  margin: 0;
  color: var(--text-2);
  font-size: 16px;
  line-height: 1.5;
}

.shop__price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  margin: 2px 0 0;
}
.shop__price-now {
  color: var(--text);
  font-size: 30px;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.shop__price-old {
  color: var(--text-3);
  font-size: 18px;
  white-space: nowrap;
}

.shop__terms {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--text-2);
  font-size: 15px;
  line-height: 1.45;
}
.shop__term {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.shop__term-icon {
  flex-shrink: 0;
  margin-top: 2px;
}
.shop__term--remaining {
  color: var(--primary-text);
  font-weight: 600;
}

.shop__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 6px;
}
.shop__buy {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 50px;
  padding: 12px 32px;
  border: none;
  border-radius: 100px;
  background: var(--primary);
  color: #fff;
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease;
}
.shop__buy:hover {
  background: var(--primary-hover);
  color: #fff;
}
.shop__buy:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
}
/* Неактивная — причина читается (контраст текста ≥ 4.5:1 на обеих темах): кнопка и есть подпись «почему нельзя». */
.shop__buy:disabled,
.shop__buy:disabled:hover {
  background: var(--surface);
  color: var(--text-2);
  cursor: not-allowed;
}
/* Предпросмотр (трек 3, §8): кнопка выглядит как настоящая (активный вид), но инертна — по предпросмотру заказ не оформить. */
.shop__buy--preview,
.shop__buy--preview:hover {
  background: var(--primary);
  color: #fff;
  cursor: not-allowed;
}

/* ── «В корзину» (06.11.2026, корзина; ui.md §1; разметка — Components/ShopCartButton) ──
   Вторичная кнопка рядом с «Купить»: обводка, та же высота; главная по-прежнему «Купить». Книга в корзине — ссылка «В корзине» с
   галочкой, зелёным «В продаже». Обёртка .shop__cart — flex: в ряду кнопок она по своему содержимому, в колонке (карточка «Магазина»)
   и в блоке плитки растягивается, а кнопка заполняет её. Состояния переключает атрибут hidden — у inline-flex кнопки его display
   перебил бы браузерное [hidden], отсюда правило с !important. */
.shop__cart {
  display: flex;
}
.shop__cart [hidden] {
  display: none !important;
}
.shop__cart-form {
  display: flex;
  flex: 1;
  margin: 0;
}
.shop__cart-btn {
  appearance: none;
  display: inline-flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 50px;
  padding: 12px 26px;
  border: 1.5px solid var(--primary);
  border-radius: 100px;
  background: transparent;
  color: var(--primary-text);
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition:
    background 0.15s ease,
    color 0.15s ease,
    border-color 0.15s ease;
}
.shop__cart-btn:hover {
  background: var(--primary-light);
  color: var(--primary-text-hover);
}
.shop__cart-btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
}
/* Запрос «В корзину» в пути: повторное нажатие не уходит (cart.js), кнопка это показывает. aria-disabled, а не disabled: у выключенной
   кнопки браузер снял бы фокус, и после ответа клавиатура оказалась бы в начале страницы. */
.shop__cart-btn[aria-disabled="true"] {
  opacity: 0.6;
  cursor: progress;
}
.shop__cart-btn--in {
  border-color: var(--shop-ok);
  color: var(--shop-ok);
}
.shop__cart-btn--in:hover {
  background: var(--shop-ok-bg);
  color: var(--shop-ok);
}
.shop__cart-icon {
  flex-shrink: 0;
}
/* Строка под кнопками — живая область, отрисованная пустой: пока пустая, она не в потоке (не добавляет отступ колонки), но остаётся в
   дереве доступности — display: none убрал бы её оттуда, и первое сообщение скринридер не объявил бы. */
.shop__cart-note {
  margin: 0;
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.45;
}
.shop__cart-note:empty {
  position: absolute;
}
.shop__cart-note--error {
  color: var(--primary-text);
  font-weight: 600;
}
.shop__cart-note a {
  color: var(--primary-text);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.shop__cart-note a:hover {
  color: var(--primary-text-hover);
}

.shop__legal {
  margin: 0;
  color: var(--text-3);
  font-size: 13px;
}
.shop__legal a {
  color: var(--text-2);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.shop__legal a:hover {
  color: var(--primary-text);
}

/* ── Фото ── */

.shop-gallery {
  position: relative;
  border-radius: var(--radius);
}
.shop-gallery__stage {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
}
.shop-gallery__zoom {
  display: block;
  line-height: 0;
  cursor: zoom-in;
}
.shop-gallery__zoom:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -3px;
}
/* Один кадр на все слайды (--shop-frame — пропорция первого фото): лента не меняет высоту при листании,
   а место под фото есть до загрузки. Фото другой пропорции вписывается целиком (contain). */
.shop-gallery__img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--shop-frame, 1);
  object-fit: contain;
  background: var(--card);
}
.shop-gallery__stage:hover .zoom-hint {
  opacity: 1;
  transform: scale(1);
}
/* Фото с миниатюрами целиком в экране (07.11.2026, замечание Кати). На ноутбуке с видимой высотой 650 px миниатюры уходили под край, а при
   большом масштабе, когда карточка в одну колонку, фото вырастало до 800 px. Сужаем всю галерею, а не только фото: кадр сохраняет пропорцию
   (--shop-frame), миниатюры стоят под ним, по бокам — подложка колонки. 244 px — верх карточки, поля, миниатюры и нижний зазор. Телефон не
   трогаем: там фото и так меньше экрана. */
@media (min-width: 701px) {
  .shop-gallery {
    max-width: calc(max(320px, 100vh - 244px) * var(--shop-frame, 1));
    margin-inline: auto;
  }
}

/* Каркас artwork-carousel.css режет корень карусели (overflow:hidden) — у нас под лентой полоса миниатюр со своей
   прокруткой, поэтому режет только сцена с фото (.shop-gallery__stage), а корень — нет. */
.shop-gallery.carousel {
  overflow: visible;
}
.shop-gallery .carousel__track {
  border-radius: var(--radius);
}

/* Фокус с клавиатуры (ИСПРАВЛЕНО 24.10.2026, ревью блока). Каркас прячет стрелки на компьютере до наведения мыши
   (opacity: 0) — стрелка в фокусе Tab была невидимой вместе со своим кольцом, и Enter листал ленту «сам». Показываем
   стрелки, пока фокус клавиатуры внутри галереи (:has — отдельным правилом: где его нет, отпадёт только оно), и рисуем
   кольцо. Мышь не трогаем: :focus-visible после клика не срабатывает. */
.shop-gallery .carousel__arrow:not(:disabled):focus-visible {
  opacity: 1;
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.shop-gallery:has(:focus-visible) .carousel__arrow:not(:disabled) {
  opacity: 1;
}
/* Кольцо фото рисует сама ссылка зума (.shop-gallery__zoom:focus-visible). Кольцо слайда из каркаса (:focus-within)
   ложилось вторым поверх: синим запасным цветом в светлой теме (--color-primary там не задан) и даже после клика мышью —
   фокус возвращается на фото при закрытии лайтбокса. */
.shop-gallery .carousel__slide:focus-within {
  outline: none;
}

/* Миниатюры вместо точек: те же .carousel__dot (клик и подсветку ведёт artwork-carousel.js).
   Полоса — контейнер прокрутки и режет всё, что за её padding-box: поле 4px — место под кольцо фокуса миниатюры
   (2px с отступом 2px, ниже). При поле 2px кольцо срезалось сверху, снизу и с внешних краёв крайних миниатюр — оставались
   две полоски по бокам, неотличимые от рамки выбранной (ИСПРАВЛЕНО 24.10.2026). Отрицательные поля сбоку и меньший
   отступ сверху держат миниатюры там же, где они были. Меняете кольцо или поле — сохраните «поле ≥ ширина + отступ»
   (стережёт ShopWebStaticGuardTests). */
.shop-gallery__thumbs {
  display: flex;
  gap: 8px;
  margin: 10px -2px 0;
  padding: 4px;
  overflow-x: auto;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
.shop-gallery .carousel__dot.shop-gallery__thumb {
  flex: 0 0 auto;
  width: 64px;
  height: 64px;
  padding: 0;
  overflow: hidden;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--card);
  opacity: 0.72;
  transform: none;
  transition:
    border-color 0.15s ease,
    opacity 0.15s ease;
}
.shop-gallery .carousel__dot.shop-gallery__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.shop-gallery .carousel__dot.shop-gallery__thumb.carousel__dot--active {
  border-color: var(--primary);
  opacity: 1;
  transform: none;
}
.shop-gallery .carousel__dot.shop-gallery__thumb:hover {
  opacity: 1;
}
/* Кольцо — снаружи рамки выбранной (рамка, зазор, кольцо): с roving tabindex фокус всегда на выбранной миниатюре, и
   кольцо внутри рамки слилось бы с ней. */
.shop-gallery .carousel__dot.shop-gallery__thumb:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* ── Видео и подробности ── */

.shop__panel {
  min-width: 0;
  padding: 24px 28px;
  background: var(--card);
  border-radius: var(--radius-lg, 16px);
  box-shadow: var(--shop-shadow);
}
.shop__heading {
  margin: 0 0 14px;
  color: var(--text);
  font-size: 18px;
  font-weight: 600;
  line-height: 1.3;
}

.shop__video,
.shop__about {
  margin-top: 16px;
}
.shop__video-player {
  display: block;
  width: 100%;
  height: auto;
  max-height: 72vh;
  border-radius: var(--radius);
  background: #000;
}

/* Две независимые колонки: характеристики | доставка и продавец. Каждая колонка — своя стопка, поэтому длинный
   список характеристик не растягивает соседние карточки пустым местом. */
.shop__details {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: 16px;
  margin-top: 16px;
}
.shop__details--single {
  grid-template-columns: minmax(0, 1fr);
}
.shop__col {
  display: grid;
  gap: 16px;
  min-width: 0;
}

/* Список «подпись: значение» — характеристики и реквизиты продавца. */
.shop__dl {
  margin: 0;
}
.shop__dl-row {
  display: grid;
  grid-template-columns: minmax(min-content, 2fr) minmax(0, 3fr);
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
  font-size: 15px;
  line-height: 1.45;
}
.shop__dl-row:first-child {
  padding-top: 0;
}
.shop__dl-row:last-child {
  padding-bottom: 0;
  border-bottom: none;
}
.shop__dl dt {
  color: var(--text-3);
}
.shop__dl dd {
  margin: 0;
  color: var(--text);
  overflow-wrap: anywhere;
}
.shop__dl a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.shop__dl a:hover {
  color: var(--primary-text);
}

.shop__note {
  margin: 0 0 10px;
  color: var(--text-2);
  font-size: 15px;
  line-height: 1.6;
}
.shop__note:last-child {
  margin-bottom: 0;
}
.shop__note--prepay {
  color: var(--text);
  font-weight: 600;
}

/* Описание — редакторский HTML (Quill после санитайзера). Карточка во всю ширину, строка — читаемой длины. */
.shop__desc {
  max-width: 72ch;
  color: var(--text-2);
  font-size: 15px;
  line-height: 1.65;
  overflow-wrap: break-word;
}
.shop__desc > :first-child {
  margin-top: 0;
}
.shop__desc > :last-child {
  margin-bottom: 0;
}
.shop__desc p,
.shop__desc ul,
.shop__desc ol {
  margin: 0 0 10px;
}
.shop__desc ul,
.shop__desc ol {
  padding-left: 22px;
}
.shop__desc h2,
.shop__desc h3 {
  margin: 18px 0 8px;
  color: var(--text);
  font-size: 16px;
  font-weight: 600;
}
.shop__desc a {
  color: var(--primary-text);
}
.shop__desc img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}

/* ── Тёмная тема ── */

html[data-theme="dark"] .shop {
  --shop-ok: #7ccfa2;
  --shop-ok-bg: rgba(124, 207, 162, 0.14);
  --shop-shadow:
    0 1px 2px rgba(0, 0, 0, 0.2),
    0 4px 12px rgba(0, 0, 0, 0.2),
    0 16px 40px rgba(0, 0, 0, 0.25);
}
/* Красный бренда на тёмной карточке (≈ 3:1) мелким текстом не читается — светлее в чипе и в «Осталось N». */
html[data-theme="dark"] .shop__status,
html[data-theme="dark"] .shop__term--remaining {
  color: #f19a8f;
}
html[data-theme="dark"] .shop__status--onSale {
  color: var(--shop-ok);
}
html[data-theme="dark"] .shop__status--soldOut,
html[data-theme="dark"] .shop__status--archived {
  color: var(--text-2);
}
html[data-theme="dark"] .shop__desc a,
html[data-theme="dark"] .shop__legal a:hover,
html[data-theme="dark"] .shop__dl a:hover {
  color: #f19a8f;
}
/* «В корзину» на тёмной карточке: красный бренда и текстом, и обводкой не набирает контраста — коралловый, как у ссылок выше. */
html[data-theme="dark"] .shop__cart-btn:not(.shop__cart-btn--in),
html[data-theme="dark"] .shop__cart-btn:not(.shop__cart-btn--in):hover {
  border-color: #f19a8f;
  color: #f19a8f;
}
html[data-theme="dark"] .shop__cart-note--error,
html[data-theme="dark"] .shop__cart-note a,
html[data-theme="dark"] .shop__cart-note a:hover {
  color: #f19a8f;
}

/* ── Адаптив (ступени — как у карточки книги) ── */

@media (max-width: 960px) {
  .shop__buybox {
    padding: 28px 28px 24px;
  }
  .shop__title {
    font-size: 26px;
  }
}

@media (max-width: 860px) {
  .shop__card {
    grid-template-columns: minmax(0, 1fr);
  }
  .shop__details {
    grid-template-columns: minmax(0, 1fr);
  }
  .shop__actions,
  .shop__buy,
  .shop__actions > .shop__cart {
    width: 100%;
  }
}

@media (max-width: 700px) {
  .shop {
    margin-bottom: 20px;
    padding: 0 16px;
  }
  .shop__card {
    border-radius: 20px;
  }
  .shop__media {
    padding: 12px;
  }
  .shop__buybox {
    gap: 12px;
    padding: 22px 20px 22px;
  }
  .shop__title {
    font-size: 23px;
  }
  .shop__price-now {
    font-size: 26px;
  }
  .shop__panel {
    padding: 20px;
  }
  .shop-gallery .carousel__dot.shop-gallery__thumb {
    width: 56px;
    height: 56px;
  }
}

@media (max-width: 400px) {
  .shop {
    padding: 0 8px;
  }
  .shop__card {
    border-radius: 16px;
  }
  .shop__buybox {
    padding: 20px 16px;
  }
  .shop__panel {
    padding: 18px 16px;
  }
  .shop__dl-row {
    grid-template-columns: minmax(min-content, 1fr) minmax(0, 1.4fr);
    gap: 10px;
  }
}

/* Подпись и значение не наезжают друг на друга (30.10.2026): колонка подписи не у́же своего самого длинного слова. Подписи — и свободный
   текст (ExtraSpecs), подбирать пропорции бесполезно. Каждая строка — своя сетка, поэтому без subgrid столбец значений шёл бы зубцами:
   колонки — общие на весь список. Блок — ПОСЛЕ правила для узких экранов: иначе оно перебьёт subgrid. */
@supports (grid-template-columns: subgrid) {
  .shop__dl {
    display: grid;
    grid-template-columns: minmax(min-content, 2fr) minmax(0, 3fr);
    column-gap: 12px;
  }
  .shop__dl-row {
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
  }
  @media (max-width: 400px) {
    .shop__dl {
      grid-template-columns: minmax(min-content, 1fr) minmax(0, 1.4fr);
      column-gap: 10px;
    }
  }
}
/* Редакторские блоки: оформление одинаково в карточке и в форме, без цветов из буфера. */
.shop-rich { overflow-wrap: anywhere; }
.shop__heading, .shop__term, .shop__dl dt, .sco-details__heading,
.sco-field__hint, .sco-choice__hint, .sco-summary__term { overflow-wrap: anywhere; }
.shop-rich > :first-child { margin-top: 0; }
.shop-rich > :last-child { margin-bottom: 0; }
.shop-rich p { margin: 0 0 .8em; }
.shop-rich img { display: block; max-width: 100%; height: auto; margin: 1em auto; border-radius: 10px; }
.shop-rich a, .shop__heading a, .sco-field__hint a, .sco-choice__hint a, .shop__dl a { color: var(--primary-text); text-decoration: underline; }
.shop-rich ul, .shop-rich ol { padding-inline-start: 1.5em; }
.shop-rich blockquote { margin-inline: 0; padding-inline-start: 1em; border-inline-start: 3px solid var(--border); }
.shop-delivery-instructions { margin-top: .65rem; }
.shop-delivery-instructions summary { cursor: pointer; text-decoration: underline; text-underline-offset: .2em; }
.shop-delivery-instructions[open] summary { margin-bottom: .65rem; }

/* Картинка редакторского текста на компьютере — не выше 60 % окна и 520 px (09.11.2026, замечание Кати: «в мобильном прекрасно, а на
   сайте гигантомания»). Ширину картинке даёт колонка текста (у описания 72ch ≈ 708 px), поэтому портретное фото 1122×1402 вставало
   708×885 — выше экрана ноутбука, а на телефоне колонка узкая, и то же фото занимает полэкрана. Телефон не трогаем, как и галерею выше.
   Почему не width: auto: ширину ставит атрибут width (его пишет загрузчик), а без неё Chrome не резервирует место под ещё не загруженную
   ленивую картинку (рамка 0×0, текст прыгает при загрузке; WebKit резервирует). Поэтому рамка остаётся шириной колонки, а contain
   вписывает в неё фото без искажения; у урезанного фото углы прямые. */
@media (min-width: 701px) {
  .shop-rich img { max-height: min(60vh, 520px); object-fit: contain; }
}
/* Две и больше картинок в одном абзаце — рядом, равной ширины (09.11.2026, просьба Кати). Так их и кладёт редактор админки: картинка
   встаёт в позицию курсора, поэтому выбранные разом файлы и вторая, вставленная сразу за первой, оказываются в одной строке, а Enter
   между ними снова ставит их одну под другой. Редактор показывает ряд тем же правилом (shop-text-editor.css). Браузер без :has
   оставит их одну под другой. */
.shop-rich p:has(> img + img) { display: flex; align-items: flex-start; gap: 12px; margin: 1em 0; }
.shop-rich p:has(> img + img) > img { flex: 1 1 0; min-width: 0; margin: 0; }
