/* Визуальный словарь карточки товара: флаг, чипы протоколов, цена, наличие.
 *
 * Был <style> внутри jinja-включения (_card_visuals.html), которое подключали
 * только шаблоны витрины. Кабинету продавца эти правила недоступны, а карточка
 * его товара должна выглядеть ТАК ЖЕ, как на витрине: продавец смотрит на свой
 * товар глазами покупателя. Вторая, похожая карточка разошлась бы с первой на
 * следующей же правке - ровно та ошибка, ради ухода от которой карточку
 * витрины когда-то и свели в один файл.
 *
 * Значения перенесены с боевой страницы /GiveMeAccounts/ один в один.
 *
 * Строчный вид (.view-list) и анимации сетки остались в marketplace.html: они
 * про страницу витрины, а не про саму карточку.
 *
 * ПРАВИЛО: наличие товара - это ОДНО число `data-stock` на узле-носителе
 * состояния, и больше ничего.
 *
 * Носитель - .product-card на карточке и .product-info на странице товара, то
 * есть ближайший общий предок наличия и кнопки покупки. Весь вид распроданного
 * собран здесь, в селекторах [data-stock="0"]: рамка, приглушение, красная
 * строка наличия, спрятанные поле количества и сумма.
 *
 * Раньше то же состояние выражалось пятью классами (.sold-out на карточке,
 * .sold-out на кнопке, .offline на строке наличия, .is-out-of-stock и .empty на
 * самом числе), и расставляли их ТРИ независимых места: jinja при сборке,
 * live_stock.js после ответа API и кабинет продавца своей строкой в JS. Каждое
 * знало свой набор, ни одно - весь. Отсюда карточка с товаром под заголовком
 * "Out of stock" и кнопка покупки без выбора количества у пополненного товара.
 *
 * Поэтому: НИКОГДА не заводите второй класс для этого состояния и не решайте
 * его наличием, отсутствием или порядком разметки - только этим числом. Кто
 * узнал новый остаток, тот пишет число; вид - забота этого файла.
 * Проверяется тестом Backend/test_storefront_invariant.py.
 */

    /* Карточка перенесена с боевой страницы /GiveMeAccounts/ - той самой, где
       уже покупают: те же градиенты, отступы, цвета и флаги. Витрина, страница
       магазина и страница товара должны показывать один и тот же товар
       одинаково, иначе покупатель дважды знакомится с одним магазином. */
    /* transition пишем поимённо, а не `all`. `all` включает в анимацию padding,
       font-size и gap - а они меняются при переключении карточек на строки.
       Браузер честно пересчитывал раскладку всей сетки каждый кадр эти 300 мс,
       и переключение выглядело трясущимся. Ниже - только те свойства, которые
       компонуются на видеокарте и раскладку не трогают. */
    .product-card { position: relative; background: linear-gradient(145deg, #1a1f3a 0%, #0f1229 100%); border: 1px solid rgba(102, 126, 234, 0.3); border-radius: 16px; padding: 1.75rem; text-align: center; text-decoration: none; color: var(--text-primary); display: flex; flex-direction: column;
                    transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1), border-color 0.22s ease, box-shadow 0.22s ease; }
    .product-card:hover { border-color: #667eea; transform: translateY(-4px); box-shadow: 0 14px 38px rgba(102, 126, 234, 0.22); }
    /* Нажатие должно чувствоваться: карточка - ссылка, а ссылка без отклика
       кажется неживой. Возврат резче подъёма, поэтому отдельная длительность. */
    .product-card:active { transform: translateY(-1px); transition-duration: 0.09s; }

    .product-header { margin-bottom: 1rem; }
    .product-icon { font-size: 2rem; margin-bottom: 0.3rem; }
    /* display:block обязателен: название стало ссылкой, а ссылка строчная - без
       этого магазин под ней вставал в ту же строку, и название товара читалось
       как "GMX.net Amastore". */
    .product-name { display: block; font-size: 1.2rem; font-weight: 700; }

    /* Название - ссылка на товар, растянутая на всю карточку псевдоэлементом.
       Так вся карточка остаётся кликабельной, но ссылок в разметке две, а не
       ссылка внутри ссылки: ярлык магазина лежит слоем выше и ведёт к себе. */
    .product-link { color: inherit; text-decoration: none; }
    .product-link::after { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
    .product-card:hover .product-link { color: #fff; }
    .product-subtitle { color: #a8b3f0; font-size: 0.85rem; margin-top: 0.2rem; }
    /* Магазин - ярлыком в верхнем углу карточки. Мелкой подписью под названием
       его не замечали: он отвечает на вопрос "у кого я покупаю", а это первое,
       что различает два одинаковых с виду товара разных продавцов. Угол выбран
       не случайно - рядом встанет рейтинг, когда его будет из чего считать
       (сегодня в базе нет ни поля, ни отзывов, и рисовать звёзды нечем). */
    /* Магазин - тихая строка под названием, но самостоятельная ссылка: лежит
       слоем выше растянутого названия товара, поэтому промахнуться друг по
       другу нельзя. Рамка и заливка отсюда убраны - в них строка спорила с
       названием за внимание; осталcя кружок с буквой, чтобы её не читали как
       случайную подпись. */
    .product-shop { position: relative; z-index: 2; display: inline-flex; align-items: center; gap: 0.35rem;
                    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
                    margin-top: 0.3rem; color: var(--text-muted); font-size: 0.78rem; font-weight: 500;
                    text-decoration: none; transition: color 0.16s ease; }
    .product-shop .shop-avatar { width: 17px; height: 17px; flex-shrink: 0; border-radius: 50%; overflow: hidden;
                                 display: inline-flex; align-items: center; justify-content: center;
                                 background: rgba(102, 126, 234, 0.3); color: #c7d2fe;
                                 font-size: 0.6rem; font-weight: 700; }
    .product-shop .shop-avatar img { width: 100%; height: 100%; object-fit: cover; transform: scale(2.6) translateY(14%); }
        /* Кроп посчитан по самому файлу: знак занимает 20-52% высоты
           картинки, ниже идёт слово "amasoft". Масштаб 2.6 показывает
           только знак, сдвиг 14% ставит его центр в центр кружка. */
    /* У знака сайта своя подложка не нужна - он сам себе фон. */
    .product-shop .shop-avatar:has(img) { background: none; }
    .product-shop:hover { color: #c7d2fe; text-decoration: underline; text-underline-offset: 2px; }
    .product-shop:hover .shop-avatar { background: rgba(102, 126, 234, 0.5); color: #fff; }

    /* Метка в углу: только "New" или "Bestseller", и только когда это правда.
       Шкалы здесь намеренно нет - см. комментарий к highlight() в
       storefront_urls.py. */
    /* Метка не реагирует ни на курсор, ни на наведение: она сообщает, а не
       предлагает нажать. "Bestseller" отсюда убран совсем - он выглядел
       кнопкой и требовал объяснения, а "New" не требует. */
    .product-flag { position: absolute; top: 0.85rem; right: 0.9rem; z-index: 2; pointer-events: none;
                    padding: 0.2rem 0.55rem; border-radius: 999px; font-size: 0.66rem; font-weight: 700;
                    letter-spacing: 0.04em; text-transform: uppercase;
                    background: rgba(34, 197, 94, 0.16); border: 1px solid rgba(34, 197, 94, 0.4); color: #86efac; }

    /* Покупка одним элементом: количество, действие и сумма в одной рамке.
       Ширина задана, поле фиксировано, сумма прижата вправо моноширинными
       цифрами - от "$0.50" до "$5,000.00" не двигается ничего. Лежит слоем выше
       растянутой ссылки товара, поэтому в карточке по нему можно попасть. */
    .buy-control { position: relative; z-index: 2; display: flex; align-items: stretch; margin-top: auto;
                   border: 1px solid rgba(102, 126, 234, 0.32); border-radius: 10px; overflow: hidden;
                   background: rgba(255, 255, 255, 0.04); }
    .bc-qty { width: 62px; flex: 0 0 auto; padding: 0.6rem 0.35rem; text-align: center;
              border: none; border-right: 1px solid rgba(102, 126, 234, 0.32);
              background: transparent; color: #fff; font-size: 0.95rem; font-family: inherit;
              -moz-appearance: textfield; }
    .bc-qty::-webkit-outer-spin-button,
    .bc-qty::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
    .bc-qty:focus { outline: none; background: rgba(102, 126, 234, 0.16); }
    /* Подпись и сумма стоят рядом по центру, а не по краям кнопки: при
       space-between между ними зияла пустая середина в полкарточки, и кнопка
       выглядела растянутой. */
    .bc-go { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: center; gap: 0.75rem;
             padding: 0.6rem 0.7rem; text-decoration: none; color: #fff; font-weight: 600; font-size: 0.92rem;
             background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
             transition: filter 0.2s ease, box-shadow 0.2s ease; }
    .bc-go:hover { color: #fff; filter: brightness(1.09); box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.04); }
    .bc-sum { font-weight: 700; white-space: nowrap;
              font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
    /* Не хватает остатка - подсвечиваем сумму, а не отдельной строкой ниже. */
    .buy-control.over .bc-sum { color: #fde68a; }
    [data-stock="0"] .buy-control { border-color: var(--border-color); }
    [data-stock="0"] .buy-control .bc-go { justify-content: center; opacity: 0.55; pointer-events: none; }
    /* У распроданного на кнопке не бывает ни суммы, ни поля количества: платить
       не за что, а заблокированное поле - это приглашение, которое тут же
       отнимают.

       Прячем именно стилями, а не пропуском в разметке. Страница собирается
       заранее, живые остатки приезжают позже (live_stock.js), и товар может
       поменять состояние в любую сторону уже после сборки. Пока разметка у
       состояний была разная, скрипт умел только одно из двух: у кончившегося
       после сборки он менял подпись, а сумма оставалась рядом
       ("Out of stock $0.50"), а у приехавшего после сборки поля количества
       просто неоткуда было взяться - кнопка покупала ровно одну штуку. */
    [data-stock="0"] .buy-control .bc-sum,
    [data-stock="0"] .buy-control .bc-qty { display: none; }
    .product-price { font-size: 1.4rem; font-weight: 700; color: #fff; margin: 0.75rem 0; }
    .product-stock { display: flex; align-items: center; justify-content: center; gap: 0.4rem; color: #22c55e; font-size: 0.85rem; margin-bottom: 1rem; }
    [data-stock="0"] .product-stock { color: #ef4444; }
    .stock-dot { width: 6px; height: 6px; background: currentColor; border-radius: 50%; }
    .product-divider { border: none; border-top: 1px solid rgba(102, 126, 234, 0.2); margin: 1rem 0; }
    .btn-buy { display: block; width: 100%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.65rem 1rem; border: none; border-radius: 10px; font-weight: 600; font-size: 0.95rem; margin-top: auto;
               transition: box-shadow 0.22s ease, filter 0.22s ease; }
    .product-card:hover .btn-buy { box-shadow: 0 6px 16px rgba(102, 126, 234, 0.42); filter: brightness(1.08); }
    .btn-buy.disabled { opacity: 0.6; }
    .product-card:hover .btn-buy.disabled { box-shadow: none; filter: none; }

    /* Флаг чуть подаётся вперёд вместе с карточкой - от этого движение читается
       как один жест, а не как сдвиг прямоугольника. */
    .flag-badge { transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1); }
    .product-card:hover .flag-badge { transform: scale(1.06); }

    /* Кому анимация мешает - тому её нет. Системная настройка, не наша догадка. */
    @media (prefers-reduced-motion: reduce) {
        .product-card, .btn-buy, .flag-badge { transition: none; }
        .product-card:hover { transform: none; }
        .product-card:hover .flag-badge { transform: none; }
    }

    /* Распроданное гасим слегка - сильнее нельзя: название должно оставаться
       читаемым, товар всё ещё интересен. */
    .product-card[data-stock="0"] { border-style: dashed; border-color: rgba(102, 126, 234, 0.18); }
    .product-card[data-stock="0"] .product-header,
    .product-card[data-stock="0"] .product-price { opacity: 0.75; }

    .flag-badge { display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 32px; border-radius: 6px; font-weight: 700; font-size: 0.9rem; letter-spacing: 1px; overflow: hidden; }
    .flag-badge img { width: 100%; height: 100%; object-fit: cover; }

    /* Значок бренда: логотип на светлой плашке плюс флажок страны углом.
       Плашка не украшение. Все четыре логотипа нарисованы тёмным - GMX синим
       #1c449b, Seznam и T-Online почти чёрным, - и на нашем тёмном фоне они
       просто пропадали бы. Светлый квадрат, как у иконки приложения, делает
       читаемым любой логотип, включая те, что появятся позже.

       Флажок обязателен: у пяти товаров GMX и у трёх чешских логотип один и тот
       же, и без страны карточки снова стали бы неразличимы. */
    .brand-mark { position: relative; display: inline-flex; align-items: center; justify-content: center;
                  width: 68px; height: 68px; border-radius: 14px; background: #f8fafc; padding: 10px; }
    .brand-mark img { width: 100%; height: 100%; object-fit: contain; }
    .product-card:hover .brand-mark { transform: scale(1.04); transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1); }
    @media (prefers-reduced-motion: reduce) { .product-card:hover .brand-mark { transform: none; } }
    .flag-de { background: linear-gradient(180deg, #000 33%, #DD0000 33%, #DD0000 66%, #FFCC00 66%); color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.5); }
    .flag-at { background: linear-gradient(180deg, #ED2939 33%, #fff 33%, #fff 66%, #ED2939 66%); color: #ED2939; text-shadow: 0 0 2px rgba(255,255,255,0.8); }
    .flag-net { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
    .flag-custom { background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); color: white; }
    .flag-com { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
    .flag-us { background: linear-gradient(180deg, #002868 40%, #fff 40%, #fff 60%, #BF0A30 60%); color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.5); }
    .flag-tonline { background: linear-gradient(135deg, #E20074 0%, #B0005E 100%); color: white; }
    .flag-firemail { background: linear-gradient(135deg, #FF6B00 0%, #CC4400 100%); color: white; }
    .flag-freenet { background: linear-gradient(135deg, #E30613 0%, #9c0410 100%); color: white; }
    .flag-kolabnow { background: linear-gradient(135deg, #16a34a 0%, #166534 100%); color: white; }
    .flag-seznam { background: linear-gradient(135deg, #d90429 0%, #8d0b1f 100%); color: white; }
    /* Фирменный градиент Instagram: жёлтый -> розовый -> фиолетовый. Тот же,
       что на боевой странице /GiveMeAccounts/. */
    .flag-instagram { background: linear-gradient(135deg, #FEDA75 0%, #FA7E1E 25%, #D62976 60%, #962FBF 90%); color: white; }
    .flag-post { background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); color: white; }
    .flag-email { background: linear-gradient(135deg, #059669 0%, #047857 100%); color: white; }

    /* Сколько уже взяли - единственный факт на карточке, который у товаров
       разный и что-то говорит. Приглушён намеренно: это подпись, а не заголовок. */
    .product-sold { margin: 0.75rem 0; font-size: 0.78rem; color: var(--text-muted); }

    /* Список особенностей больше не рисуется (см. _product_card.html), но стили
       нужны странице товара и старым шаблонам. */
    .product-features { list-style: none; padding: 0; margin: 0.75rem 0; text-align: left; }
    .product-features li { color: #a8b3f0; font-size: 0.8rem; padding: 0.25rem 0 0.25rem 1.2rem; position: relative; }
    .product-features li::before { content: "\2022"; position: absolute; left: 0; color: #22c55e; }

    /* Чипы протоколов. Зелёный - есть, красный - нет; оба состояния перенесены
       с боевой страницы /GiveMeAccounts/, где у Firemail SMTP помечен красным.
       Убирать чип вместо пометки нельзя: карточка тогда молчит о протоколе, и
       покупатель дочитывает молчание в свою пользу - а потом просит возврат.

       Зелёный - у базового чипа, а не у класса `.available`. Разметка страниц
       и их стили расходятся во времени: категория подгружается запросом, и
       браузер отдаёт её из кэша - на боевой это неделя (Hostinger). Пока едет
       старая разметка без классов, бесцветный базовый чип выглядел бы поломкой.
       Зелёный по умолчанию = ровно прежний вид, а красным метит только явный
       `.unavailable`, которого в старой разметке и не было. */
    .protocol-statuses { display: flex; gap: 0.4rem; margin: 0.7rem 0 0.5rem; justify-content: center; flex-wrap: wrap; }
    .protocol-chip, .protocol-chip.available { display: inline-flex; align-items: center; gap: 0.32rem; padding: 0.24rem 0.55rem; border-radius: 999px; font-size: 0.7rem; font-weight: 700; border: 1px solid rgba(34, 197, 94, 0.35); background: rgba(34, 197, 94, 0.14); color: #86efac; }
    .protocol-chip.unavailable { border-color: rgba(239, 68, 68, 0.3); background: rgba(239, 68, 68, 0.12); color: #fca5a5; }
    /* Гарантия - не протокол, поэтому и цвет другой: синий вместо зелёного,
       чтобы глаз не считал её третьим протоколом. Отсутствие гарантии написано
       словами и приглушено - это факт, а не предупреждение. */
    /* Метка-значение остаётся только на странице товара (формат выдачи рядом с
       протоколами). На карточке этих чипов больше нет - см. ниже. */
    .protocol-chip.meta { border-color: rgba(255, 255, 255, 0.14); background: rgba(255, 255, 255, 0.05); color: var(--text-secondary); font-weight: 600; }
    .protocol-chip[title] { cursor: help; }

    /* Формат и популярность - строкой, а не чипами. В рамках они читались как
       ещё две возможности почты рядом с IMAP и SMTP, хотя это не состояния, а
       значения; четыре ярлыка подряд превращали карточку в кучу, где глазу не
       за что зацепиться. Строка ниже, мельче и приглушённее - это подпись. */
    .product-meta { display: flex; align-items: center; justify-content: center; gap: 0.6rem; margin: 0.5rem 0 0.9rem;
                    font-size: 0.75rem; color: var(--text-muted); }
    .meta-format { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: -0.01em; cursor: help; }
    /* Описание показывает только строчный вид - см. marketplace.html. */
    .meta-descr { display: none; }
    /* Звёзд всегда три - меняется только заливка. Поэтому элемент одной ширины у
       всех товаров, и в строчном списке столбец не рвётся, как рвался на
       "116k sold" против "under 100 sold". */
    .meta-stars { display: inline-flex; gap: 0.1rem; cursor: help; }
    .meta-stars .star { color: rgba(255, 255, 255, 0.16); font-size: 0.85rem; line-height: 1; }
    .meta-stars .star.on { color: #fbbf24; }

    .protocol-chip.warranty { border-color: rgba(99, 102, 241, 0.4); background: rgba(99, 102, 241, 0.14); color: #c7d2fe; }
    .protocol-chip.warranty.none { border-color: rgba(255, 255, 255, 0.12); background: rgba(255, 255, 255, 0.04); color: var(--text-muted); }
    .protocol-dot { width: 0.4rem; height: 0.4rem; border-radius: 50%; background: currentColor; box-shadow: 0 0 8px currentColor; }
