/*
 * mod_articart_search — стили выпадашки.
 *
 * Все настраиваемые значения вынесены в CSS-переменные с префиксом
 * --articart-search-*: переопределяются в шаблоне сайта без правки
 * этого файла (он перезаписывается при обновлении модуля).
 */
.articart-search {
    --articart-search-radius: 10px;
    --articart-search-border: #d9dde1;
    --articart-search-bg: #fff;
    --articart-search-text: #1a1d21;
    --articart-search-muted: #6b7280;
    --articart-search-accent: #2563eb;
    /* Обводка поля при фокусе с клавиатуры. ОТДЕЛЬНАЯ переменная, а не
       --articart-search-accent: акцент участвует ещё в семи местах
       (подсветка совпадения, полоска активной строки, чип «під замовлення»,
       спиннер), и перекраска его ради одной обводки утащила бы за собой
       всю выдачу. Здесь янтарный — он заметнее синего на белом поле и не
       спорит с синими ссылками шаблона. */
    --articart-search-focus: #f0a500;
    --articart-search-hover: #f3f5f7;
    --articart-search-shadow: 0 12px 28px rgba(20, 23, 28, 0.14);
    --articart-search-price: #1a1d21;
    --articart-search-price-old: #9ca3af;
    --articart-search-in-stock: #17825a;
    --articart-search-out-of-stock: #b91c1c;
    --articart-search-awaiting-delivery: #b7791f;
    --articart-search-made-to-order: #2563eb;
    --articart-search-discontinued: #9ca3af;
    --articart-search-other: #b7791f;
    /* Приглушённые подложки для чипа статуса наличия (row3) — тот же
       цвет, что и текст/иконка чипа, но по факту прозрачности, чтобы
       чип визуально отделялся от цены, а не просто стоял рядом текстом. */
    --articart-search-in-stock-bg: rgba(23, 130, 90, 0.12);
    --articart-search-out-of-stock-bg: rgba(185, 28, 28, 0.1);
    --articart-search-awaiting-delivery-bg: rgba(183, 121, 31, 0.12);
    --articart-search-made-to-order-bg: rgba(37, 99, 235, 0.12);
    --articart-search-discontinued-bg: rgba(156, 163, 175, 0.16);
    --articart-search-other-bg: rgba(183, 121, 31, 0.12);
    --articart-search-accent-soft: rgba(37, 99, 235, 0.14);
    --articart-search-accent-stripe: rgba(37, 99, 235, 0.45);
    /* Длительность fade/slide-перехода панели. Меняется на сайте без
       правки этого файла; JS (openPanel/closePanel) ориентируется на
       событие transitionend, а не на конкретное число — так что менять
       это значение здесь безопасно. */
    --articart-search-anim-duration: 220ms;
    /* Закрытие заметно быстрее открытия: появление хочется рассмотреть,
       исчезновение — не хочется ждать. Разные длительности задаются не
       скриптом, а двумя transition: в базовом состоянии (играет при
       уходе в закрытое) и в .is-open (играет при открытии). */
    --articart-search-anim-duration-out: 130ms;
    --articart-search-anim-offset: 10px;
    /* «Мягкая» кривая с сильным замедлением к концу — та же, что у
       расширения поля, чтобы обе анимации ощущались одним движением. */
    --articart-search-anim-ease: cubic-bezier(0.16, 1, 0.3, 1);
    /* Появление пунктов лесенкой: шаг между соседними карточками и
       длительность одной. Шаг намеренно мелкий — при шести пунктах вся
       лесенка укладывается в ~100 мс и читается как одно движение, а не
       как последовательность. */
    --articart-search-item-stagger: 22ms;
    --articart-search-item-duration: 200ms;
    /* Расширение поля (см. .is-expanded) живёт отдельным таймингом от
       fade-панели: чуть дольше и на "мягкой" ease-out-кривой (сильное
       замедление к концу, без резкого рывка в конце), поэтому смена
       ширины ощущается плавной, а не дёрганой линейной анимацией. */
    --articart-search-anim-duration-width: 260ms;
    --articart-search-anim-ease-width: cubic-bezier(0.16, 1, 0.3, 1);
    /*
     * Ширина поля в свёрнутом виде и при фокусе. Панель выдачи не имеет
     * собственной ширины — она растянута left:0/right:0 относительно
     * этого блока (см. .articart-search__panel), поэтому расширяя сам
     * .articart-search мы одновременно расширяем и поле, и выпадашку.
     *
     * ПОЧЕМУ МИНИМУМ, А НЕ ПРОСТО ШИРИНА. Карточка выдачи содержит название,
     * бренд, упаковку, две цены и чип наличия. На 380px этот набор
     * укладывался только за счёт многоточий, то есть покупатель видел
     * обрезанное название вместо полного — ради чего подсказка и нужна.
     * Поэтому ширина задаётся снизу: не уже 450px, а на экране уже этого
     * значения виджет занимает всю доступную ширину (см. min() ниже и
     * медиазапрос в конце файла).
     *
     * Три числа приезжают из настроек модуля инлайновыми переменными на
     * корне виджета (tmpl/default.php), значения здесь — запасные, на случай
     * старого шаблона.
     */
    --articart-search-min-width: 450px;
    --articart-search-width: 450px;
    --articart-search-width-expanded: 560px;

    /*
     * ТИПОГРАФИКА ВИДЖЕТА. Вынесена в переменные не ради симметрии с
     * остальными настройками: в разделе «Броня» (в конце файла) эти же
     * значения переобъявляются с !important, а !important на литерале
     * («font-size: 14px !important») закрыл бы владельцу сайта путь к
     * настройке навсегда. На переменной не закрывает: подстановка значения
     * происходит ДО каскада, поэтому переопределение
     * `--articart-search-fs-title` в шаблоне работает и через !important.
     * То есть броня защищает от чужого CSS, но не от хозяина сайта.
     */
    --articart-search-fs-base: 14px;
    --articart-search-fs-input: 15px;
    --articart-search-fs-title: 14px;
    --articart-search-fs-meta: 12px;
    --articart-search-fs-price: 13px;
    --articart-search-fs-badge: 11px;
    --articart-search-line-height: 1.3;

    position: relative;
    width: var(--articart-search-width);
    /* Первое объявление — для браузеров без min(); второе перекрывает его
       там, где min() поддерживается, и не даёт виджету вылезти за пределы
       узкого контейнера (колонка шаблона, мобильная шапка). */
    min-width: var(--articart-search-min-width);
    min-width: min(var(--articart-search-min-width), 100%);
    max-width: 100%;
    font-family: inherit;
    color: var(--articart-search-text);
    box-sizing: border-box;
    transition: width var(--articart-search-anim-duration-width) var(--articart-search-anim-ease-width);
}

/*
 * Раскрытие управляется классом .is-expanded из JS (см. expand()/collapse()
 * в articart-search.js), а не голым :focus-within. Раньше ширина была
 * завязана на :focus-within — а фокус слетает с input при клике на ЛЮБОЙ
 * нефокусируемый элемент внутри виджета (li/img/span в панели результатов
 * не фокусируемы, и клик по ним уводит фокус на <body>), из-за чего поле
 * сворачивалось от любого касания, а не только от выбора пункта или
 * закрытия меню. Теперь JS сам решает, когда сворачивать: при клике по
 * пункту выдачи (navigateTo) или при закрытии панели (closePanel) —
 * см. комментарий там же.
 */
.articart-search.is-expanded {
    width: var(--articart-search-width-expanded);
}

/*
 * Мобильный экран. Медиазапрос дублирует то, что уже делает min(…, 100%), и
 * это не избыточность: min() спасает от узкого КОНТЕЙНЕРА, а здесь речь про
 * узкий ЭКРАН — виджет часто лежит в шапке, у которой ширины как таковой нет
 * (inline-flex, shrink-to-fit), и процент там считать не от чего. Порог на
 * пиксель шире базовой ширины: ровно на 450px виджет ещё помещается целиком.
 */
@media (max-width: 480px) {
    .articart-search,
    .articart-search.is-expanded {
        width: 100%;
        min-width: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .articart-search {
        transition: none;
    }
}

.articart-search * {
    box-sizing: border-box;
}

.articart-search__field {
    position: relative;
    display: flex;
    align-items: center;
}

.articart-search__icon {
    position: absolute;
    left: 12px;
    color: var(--articart-search-muted);
    pointer-events: none;
}

.articart-search__input {
    width: 100%;
    padding: 10px 36px 10px 36px;
    border: 1px solid var(--articart-search-border);
    border-radius: var(--articart-search-radius);
    background: var(--articart-search-bg);
    color: var(--articart-search-text);
    font-size: 15px;
    line-height: 1.3;
}

.articart-search__input::placeholder {
    color: var(--articart-search-muted);
}

.articart-search__input:focus-visible {
    outline: 2px solid var(--articart-search-focus);
    outline-offset: 1px;
}

.articart-search__clear,
.articart-search__spinner {
    position: absolute;
    right: 8px;
}

.articart-search__clear {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--articart-search-muted);
    cursor: pointer;
}

.articart-search__clear:hover {
    background: var(--articart-search-hover);
}

.articart-search__spinner {
    width: 16px;
    height: 16px;
    border: 2px solid var(--articart-search-border);
    border-top-color: var(--articart-search-accent);
    border-radius: 50%;
    animation: articart-search-spin 0.7s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    .articart-search__spinner {
        animation-duration: 1.6s;
    }
}

@keyframes articart-search-spin {
    to {
        transform: rotate(360deg);
    }
}

.articart-search__panel {
    position: absolute;
    z-index: 60;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    max-height: 70vh;
    overflow-y: auto;
    background: var(--articart-search-bg);
    border: 1px solid var(--articart-search-border);
    border-radius: var(--articart-search-radius);
    box-shadow: var(--articart-search-shadow);
    padding: 6px;
    /*
     * Выравнивание задаётся ЯВНО, а не наследуется. Виджет живёт в шапке
     * сайта, а шапка почти в любом шаблоне отцентрована (text-align: center
     * на строке-контейнере) — и выдача наследовала это выравнивание: название,
     * бренд и цена стояли по центру карточки, хотя миниатюра прижата к левому
     * краю. Читать такой список глазом невозможно: у строк нет общей левой
     * кромки, за которую цепляется взгляд. Собственный text-align в панели
     * стоит именно потому, что источник центрирования — ЧУЖОЙ css, которого
     * мы не видим; наследование здесь обязано обрываться.
     * Явный `center` у .articart-search__empty и .articart-search__show-all
     * ниже это правило не задевает: прямое объявление всегда сильнее
     * унаследованного значения.
     */
    text-align: left;
    /*
     * Fade + лёгкий slide вниз. Панель управляется классом .is-open,
     * который JS добавляет/снимает поверх [hidden] (см. articart-search.js:
     * openPanel/closePanel) — атрибут hidden всё ещё убирает элемент из
     * доступности и раскладки, а класс отвечает только за transition.
     * Старт-состояние (opacity:0, translateY) должно совпадать с тем,
     * что задано ниже для закрытой панели, иначе переход "прыгнет".
     */
    opacity: 0;
    transform: translateY(calc(-1 * var(--articart-search-anim-offset))) scale(0.985);
    /* Панель растёт от поля, а не из своего центра. */
    transform-origin: top center;
    transition: opacity var(--articart-search-anim-duration-out) ease-in,
                transform var(--articart-search-anim-duration-out) ease-in;
}

.articart-search__panel.is-open {
    opacity: 1;
    transform: translateY(0) scale(1);
    transition: opacity var(--articart-search-anim-duration) var(--articart-search-anim-ease),
                transform var(--articart-search-anim-duration) var(--articart-search-anim-ease);
}

/*
 * Морфинг высоты при подмене результатов в уже открытой панели (класс
 * вешает morphHeight() в articart-search.js). Переход по height включается
 * ТОЛЬКО на время анимации: постоянный transition на height мешал бы
 * панели свободно растягиваться под содержимое и ломал бы прокрутку при
 * достижении max-height.
 */
.articart-search__panel.is-morphing {
    overflow: hidden;
    transition: opacity var(--articart-search-anim-duration) var(--articart-search-anim-ease),
                transform var(--articart-search-anim-duration) var(--articart-search-anim-ease),
                height var(--articart-search-anim-duration) var(--articart-search-anim-ease);
}

/*
 * Появление пунктов лесенкой. Задержка считается от --i, который ставит
 * renderItem: чистый CSS, поэтому при prefers-reduced-motion (ниже)
 * анимация просто выключается, а не «проигрывается быстро».
 */
@keyframes articart-search-item-in {
    from {
        opacity: 0;
        transform: translateY(4px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.articart-search__panel.is-open .articart-search__item {
    animation: articart-search-item-in var(--articart-search-item-duration)
               var(--articart-search-anim-ease) backwards;
    animation-delay: calc(var(--i, 0) * var(--articart-search-item-stagger));
}

@media (prefers-reduced-motion: reduce) {
    .articart-search__panel,
    .articart-search__panel.is-open,
    .articart-search__panel.is-morphing {
        transition: none;
    }

    .articart-search__panel.is-open .articart-search__item {
        animation: none;
    }
}

.articart-search__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/*
 * align-items: stretch — миниатюра занимает ВСЮ высоту строки, а не стоит
 * маленьким квадратом у её верхнего края.
 *
 * История такая. Пока текст резался многоточием, строка всегда была одной
 * высоты с гнездом, и вопрос не стоял. После отказа от многоточий (см.
 * __title ниже) название переносится на две-три строки, и текстовый блок
 * стал выше картинки: сначала стояло center — миниатюра уезжала в середину
 * карточки, потом flex-start — она держала левую колонку сверху, но под ней
 * оставалась пустая дыра в полкарточки. Ни то ни другое не читается как одна
 * строка списка. Растянутое гнездо задаёт карточке левую границу целиком, и
 * заодно товар в подсказке становится крупнее — картинка вписывается
 * (object-fit: contain) в более высокое гнездо, а не обрезается.
 *
 * text-align: left продублирован здесь намеренно: центрирование прилетает из
 * CSS шаблона сайта, и правило шаблона может оказаться специфичнее нашего
 * правила на панели (например `#header * { text-align: center }`). Два
 * объявления стоят ничего, а промах — это снова центрованная выдача.
 */
.articart-search__item {
    display: flex;
    align-items: stretch;
    gap: 10px;
    padding: 8px;
    border-radius: 8px;
    cursor: pointer;
    text-align: left;
}

.articart-search__item.is-active,
.articart-search__item:hover {
    background: var(--articart-search-hover);
}

.articart-search__item.is-exact {
    box-shadow: inset 2px 0 0 var(--articart-search-accent);
}

/* Совпадение с началом названия — акцент слабее, чем у точного code/pack
   (is-exact), но всё равно даёт глазу зацепку в списке из 6-7 строк. */
.articart-search__item.is-match-start:not(.is-exact) {
    box-shadow: inset 2px 0 0 var(--articart-search-accent-stripe);
}

.articart-search__hl {
    background: var(--articart-search-accent-soft);
    color: var(--articart-search-accent);
    border-radius: 2px;
    font-weight: 600;
}

/*
 * Размер гнезда приезжает из настроек компонента через inline-переменные на
 * корне виджета (tmpl/default.php). Значения в fallback — то же, что стоит
 * по умолчанию в ArtiCart, чтобы вёрстка не поехала, если модуль обновили,
 * а компонент нет.
 *
 * ШИРИНА ФИКСИРОВАНА, ВЫСОТА — НЕТ. Ширина держит колонку картинок ровной по
 * всему списку (иначе названия начнут «ступеньками» разъезжаться от строки к
 * строке), а высота приходит от align-self: stretch, то есть равна высоте
 * текста рядом. --articart-search-thumb-h из настройки превращается в
 * min-height: это по-прежнему тот размер, под который ThumbMaker режет файл,
 * и он же не даёт короткой строке (однострочное название без бренда) схлопнуть
 * гнездо в полоску.
 */
.articart-search__thumb-wrap {
    position: relative;
    flex: 0 0 auto;
    align-self: stretch;
    width: var(--articart-search-thumb-w, 60px);
    height: auto;
    min-height: var(--articart-search-thumb-h, 60px);
    border-radius: 6px;
    overflow: hidden;
    background: var(--articart-search-hover);
}

.articart-search__thumb-skeleton {
    position: absolute;
    inset: 0;
    background-image: linear-gradient(
        100deg,
        var(--articart-search-hover) 30%,
        #eef1f4 45%,
        var(--articart-search-hover) 60%
    );
    background-size: 250% 100%;
    animation: articart-search-shimmer 1.4s ease-in-out infinite;
}

@keyframes articart-search-shimmer {
    0% {
        background-position: 100% 0;
    }
    100% {
        background-position: -50% 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .articart-search__thumb-skeleton {
        animation: none;
    }
}

/*
 * object-fit: contain, а НЕ cover — это не косметика. Файл превью компонент
 * режет под витрину (170×220, пропорция 1:1.29), а гнездо в выпадашке
 * квадратное. При cover браузер обрезал бы верх и низ упаковки, и товар в
 * подсказке переставал быть похож на товар на витрине — а именно по картинке
 * покупатель и узнаёт нужную строку. С contain по бокам появляются белые
 * поля: ровно то же, что делает ThumbMaker, вписывая фото в холст.
 *
 * Подложка белая (а не серая, как у гнезда): поля вокруг вписанной картинки
 * обязаны сливаться с её собственным белым фоном, иначе у каждой миниатюры
 * появляется серая рамка по бокам.
 */
/*
 * Картинка позиционируется АБСОЛЮТНО, а не потоком. Это не стилизация, а
 * единственный способ дотянуть её до низа растянутого гнезда: у гнезда теперь
 * height: auto (высоту ему даёт stretch), а `height: 100%` у обычного потомка
 * от auto-родителя не считается — процент разрешать не от чего, и браузер
 * молча берёт натуральную высоту файла. У абсолютно позиционированного
 * элемента содержащий блок — padding-box ближайшего позиционированного
 * предка, и его высота к моменту раскладки уже известна, поэтому inset: 0
 * работает всегда. Тот же приём уже используется скелетоном ниже.
 *
 * Атрибуты width/height у <img> при этом остаются (см. renderItem): для
 * раскладки они больше не нужны — от прыжков списка страхует min-height
 * гнезда, — но они по-прежнему сообщают браузеру пропорции файла.
 */
.articart-search__thumb {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: var(--articart-search-bg);
}

/* Реальная миниатюра: невидима, пока не отработал onload — тогда
   скелетон под ней просто перестаёт быть виден глазу (плавно). */
.articart-search__thumb-wrap:not(.articart-search__thumb-wrap--static) .articart-search__thumb {
    opacity: 0;
    transition: opacity 0.25s ease;
}

.articart-search__thumb-wrap:not(.articart-search__thumb-wrap--static) .articart-search__thumb.is-loaded {
    opacity: 1;
}

/* Категорийная SVG-заглушка (нет фото в принципе — не грузим, не мигаем) */
.articart-search__thumb-wrap--static {
    background: var(--articart-search-hover);
}

.articart-search__thumb--placeholder {
    padding: 12px;
    box-sizing: border-box;
    object-fit: contain;
    opacity: 0.6;
}

/* Настоящая миниатюра, не загрузившаяся по 404 и подменённая на
   заглушку через data-fallback: та же приглушённая подача. */
.articart-search__thumb.articart-search__thumb--fallback {
    padding: 12px;
    box-sizing: border-box;
    object-fit: contain;
    opacity: 0.6 !important;
}

/*
 * Три отдельные строки вместо "название+бренд в одну строку, цена+статус
 * в другую" — раньше при длинном названии/бренде/статусе всё визуально
 * слипалось в одну кучу без ясной иерархии. Теперь:
 *  __row1      — только название (самый важный элемент, ему вся ширина);
 *  __row-meta  — бренд + упаковка (второстепенный, приглушённый текст);
 *  __row2      — цена(и) слева, статус наличия — отдельным "чипом"
 *                справа, чтобы он не подпирал цену вплотную, а читался
 *                как отдельный визуальный блок.
 */
.articart-search__body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.articart-search__row1 {
    display: flex;
    align-items: baseline;
    min-width: 0;
}

/*
 * ТЕКСТ ПЕРЕНОСИТСЯ, А НЕ ОБРЕЗАЕТСЯ МНОГОТОЧИЕМ. Раньше здесь стояли
 * white-space: nowrap + text-overflow: ellipsis, и на длинных названиях
 * («Борошно нутове Добрий Мельник, паперовий пакет 0,5 кг») подсказка
 * показывала первую половину строки. Смысл живого поиска ровно в том, чтобы
 * покупатель узнал товар, не открывая страницу, — обрезанное название этому
 * прямо мешает, а места теперь хватает: виджет не уже 450px.
 *
 * overflow-wrap: anywhere нужен для длинных кодов и артикулов без пробелов:
 * без него одна такая «строка-слово» распирает карточку и возвращает
 * горизонтальное сжатие через заднюю дверь.
 */
.articart-search__title {
    flex: 1 1 auto;
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.3;
}

/* Бренд и упаковка — общая приглушённая мета-строка под названием. Оба
   значения показываются целиком; если вместе не помещаются — переносятся
   на следующую строку, а не режутся. */
.articart-search__row-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 4px;
    min-width: 0;
    font-size: 12px;
    line-height: 1.3;
    color: var(--articart-search-muted);
}

.articart-search__brand,
.articart-search__pack {
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
}

.articart-search__brand {
    flex: 0 1 auto;
    font-weight: 500;
}

.articart-search__pack {
    flex: 0 1 auto;
}

/* Разделитель между брендом и упаковкой рисуем только когда оба
   присутствуют одновременно (соседний с брендом элемент). */
.articart-search__brand + .articart-search__pack::before {
    content: '\00b7';
    margin-right: 4px;
}

/*
 * ЗНАКИ ТОВАРА: ПИЛЮЛИ В СТРОКЕ ПОДСКАЗКИ (1.5.3).
 *
 * ОФОРМЛЕНИЕ ЗДЕСЬ, А НЕ В КОМПОНЕНТЕ. Правила `.articart-badge` лежат в
 * `media/com_articart/css/articart.css`, а его подключает плагин контента —
 * то есть только на странице со статьёй. Виджет живёт в ШАПКЕ и работает на
 * главной, в категории и в корзине, где этого файла может не быть вовсе:
 * пилюля вышла бы голым текстом. Ровно эту грабку проект уже проходил с
 * угловой медалью — новый класс не получает оформления, когда оформление
 * живёт в чужом файле.
 *
 * ЗНАЧЕНИЯ ПОВТОРЯЮТ КОМПОНЕНТ НАМЕРЕННО: пилюля в подсказке обязана быть
 * похожа на пилюлю на витрине, иначе покупатель считает их разными знаками.
 * Величины при этом чуть мельче — строка подсказки высотой в миниатюру.
 *
 * ЦВЕТ НЕ ЗАДАЁТСЯ: он приходит инлайном (--h/--s/--l/--fg и --edge-*),
 * посчитанный BadgeColor на сервере по относительной яркости WCAG. Поэтому
 * смена цвета знака в настройках компонента меняет и подсказку.
 *
 * ЗАПАСНЫЕ ЗНАЧЕНИЯ ОБЯЗАТЕЛЬНЫ: свойство с неразрешимой var() становится
 * недействительным целиком, и пилюля осталась бы без фона и без надписи.
 */
.articart-search__marks {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 3px 4px;
    margin-top: 1px;
    min-width: 0;
}

.articart-search__marks .articart-badge {
    --h: 0;
    --s: 100%;
    --l: 33%;

    display: inline-block;
    max-width: 100%;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    /* Длинная подпись переносится, а не режется многоточием: обрезанный
       «Розпродаж» от «Розпродано» не отличить — то же правило, по которому
       в 1.5.0 из карточки убрали ellipsis. */
    white-space: normal;
    overflow-wrap: anywhere;
    background: hsl(var(--h), var(--s), var(--l));
    border: 1px solid hsl(
        var(--edge-h, var(--h)),
        var(--edge-s, var(--s)),
        var(--edge-dark-l, calc(var(--l) - 12%)));
    color: var(--fg, #ffeaa7);
}

/* Цена и чип наличия: если вдвоём в строку не помещаются (две цены плюс
   длинная подпись статуса), чип уезжает на свою строку целиком. Сжимать его
   нельзя — «Изготовление под заказ» в обрезанном виде читается как другой
   срок, а не как длинная надпись. */
.articart-search__row2 {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    font-size: 13px;
    margin-top: 1px;
}

.articart-search__price-group {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
}

.articart-search__price {
    font-weight: 600;
    color: var(--articart-search-price);
    white-space: nowrap;
}

.articart-search__price-old {
    color: var(--articart-search-price-old);
    text-decoration: line-through;
    white-space: nowrap;
}

/*
 * Иконка статуса наличия — не точка, а свой символ на статус:
 * галочка (в наличии) / крестик (нет в наличии) / часы (под заказ,
 * любой прочий статус). Технически это не <img> и не инлайновый SVG в
 * HTML, а один цвет через background-color + CSS mask-image: значит
 * иконка ровно того же цвета, что и текст бейджа (currentColor тут
 * работать не может — берём background-color вместо заливки), меняется
 * вместе с --articart-search-available/discontinued/other без второго
 * набора файлов, и не даёт лишнего запроса к серверу (все три — один
 * встроенный CSS-файл, кэшируется вместе с ним).
 */
.articart-search__badge {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    gap: 4px;
    margin-left: auto;
    padding: 3px 8px 3px 6px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 500;
    line-height: 1.3;
    white-space: nowrap;
}

.articart-search__badge::before {
    content: '';
    display: block;
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    background-color: currentColor;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
}

.articart-search__badge.is-in-stock {
    color: var(--articart-search-in-stock);
    background: var(--articart-search-in-stock-bg);
}

.articart-search__badge.is-in-stock::before {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M13.5 4.5 6.5 12 2.5 8' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M13.5 4.5 6.5 12 2.5 8' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.articart-search__badge.is-out-of-stock {
    color: var(--articart-search-out-of-stock);
    background: var(--articart-search-out-of-stock-bg);
}

.articart-search__badge.is-out-of-stock::before {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 3l10 10M13 3 3 13' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 3l10 10M13 3 3 13' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}

.articart-search__badge.is-awaiting-delivery {
    color: var(--articart-search-awaiting-delivery);
    background: var(--articart-search-awaiting-delivery-bg);
}

.articart-search__badge.is-awaiting-delivery::before {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M8 4.8V8l2.4 1.4' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M8 4.8V8l2.4 1.4' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Made to order: значок часов (тот же, что и awaiting-delivery) — по
   решению: «под заказ» визуально тоже про ожидание, отличается только
   подпись, не иконка. */
.articart-search__badge.is-made-to-order {
    color: var(--articart-search-made-to-order);
    background: var(--articart-search-made-to-order-bg);
}

.articart-search__badge.is-made-to-order::before {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M8 4.8V8l2.4 1.4' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M8 4.8V8l2.4 1.4' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.articart-search__badge.is-discontinued {
    color: var(--articart-search-discontinued);
    background: var(--articart-search-discontinued-bg);
}

.articart-search__badge.is-discontinued::before {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M4.6 4.6l6.8 6.8' stroke='%23000' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M4.6 4.6l6.8 6.8' stroke='%23000' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
}

.articart-search__badge.is-other {
    color: var(--articart-search-other);
    background: var(--articart-search-other-bg);
}

.articart-search__badge.is-other::before {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M8 4.8V8l2.4 1.4' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3Cpath d='M8 4.8V8l2.4 1.4' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.articart-search__empty {
    padding: 14px 10px;
    text-align: center;
    color: var(--articart-search-muted);
    font-size: 14px;
}

.articart-search__empty--error {
    color: #b91c1c;
}

.articart-search__show-all {
    display: block;
    margin-top: 4px;
    padding: 10px 8px;
    text-align: center;
    border-top: 1px solid var(--articart-search-border);
    color: var(--articart-search-accent);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    border-radius: 0 0 6px 6px;
}

.articart-search__show-all:hover,
.articart-search__show-all:focus-visible {
    background: var(--articart-search-hover);
}

/* ==================================================================== *
 * БРОНЯ: изоляция виджета от CSS шаблона                     (1.5.2)
 * ==================================================================== *
 *
 * ЧТО СЛУЧИЛОСЬ. Выдача продолжала стоять по центру и после того, как в
 * 1.5.1 в панель и в карточку был добавлен явный `text-align: left`.
 * Devtools показали, что text-align тут вообще ни при чём — он честно
 * `left`. Центрировало другое правило шаблона:
 *
 *     .container-header .grid-child:nth-child(2) * {
 *         margin: 0 auto;
 *         font-size: clamp(10px, 1.5vw, 20px);
 *         white-space: nowrap;
 *         text-overflow: ellipsis;
 *         max-width: 100%;
 *     }                                        // user.css:88
 *
 * `margin: 0 auto` на КАЖДОМ потомке шапки. Наши строки карточки
 * (__row1, __row-meta, __row2) — flex-элементы, а у flex-элемента
 * автополя съедают всё свободное место по бокам, то есть центрируют его
 * не хуже text-align. Отсюда и картина «миниатюра слева, текст по
 * центру»: гнездо картинки свободного места рядом с собой не имеет, а
 * текстовые строки имеют.
 *
 * Заодно тем же правилом сносится всё остальное: clamp() приравнивает
 * название, бренд, цену и чип к одному кеглю (на широком экране — 20px,
 * то есть иерархия карточки исчезает), а `white-space: nowrap` +
 * `text-overflow: ellipsis` возвращают многоточия, от которых в 1.5.0
 * отказались осознанно.
 *
 * ПОЧЕМУ НЕ ХВАТИЛО ОБЫЧНЫХ ОБЪЯВЛЕНИЙ. Специфичность правила шаблона —
 * (0,3,0): два класса плюс :nth-child. Любое наше одноклассовое правило
 * (0,1,0) проигрывает ему, сколько бы раз мы его ни повторили. Проигрыш
 * молчаливый: в devtools наше объявление просто зачёркнуто.
 *
 * КАК СДЕЛАНО ЗДЕСЬ. Два независимых механизма, потому что каждый по
 * отдельности пробивается:
 *
 *   1. `!important` — он сильнее ЛЮБОГО обычного объявления шаблона,
 *      какой бы специфичности оно ни было. Это основная защита.
 *   2. Учетверённый класс `.articart-search` ×4 — (0,4,0). Это не опечатка
 *      и не копипаста: повтор класса — законный способ поднять
 *      специфичность, не привязываясь к id конкретного экземпляра модуля
 *      (их на странице может быть несколько, и id у каждого свой). Нужен
 *      на случай, когда в шаблоне появится `!important`: между двумя
 *      !important спор решается уже специфичностью, а при РАВНОЙ
 *      специфичности выигрывает тот файл, который подключён позже, —
 *      то есть шаблон, а не мы. Четыре повтора берут с запасом любое
 *      реалистичное правило на классах; правило на id (1,0,0) не побьёт
 *      никакое число классов — если однажды такое появится, лечить его
 *      придётся на стороне шаблона.
 *
 * ЧТО ИМЕННО ГАСИТСЯ. Только наследуемые и «навязываемые звёздочкой»
 * свойства — те, которыми чужой `*`-селектор реально может дотянуться
 * внутрь виджета. Цвета, фоны, отступы-padding, flex и анимации здесь
 * НЕ трогаются: они задаются нашими правилами выше и в поле зрения
 * проблемы не попадали. Если однажды шаблон начнёт навязывать и их —
 * дописывается сюда же тем же приёмом, а не разбрасывается !important
 * по всему файлу.
 *
 * ПРАВИЛО НА БУДУЩЕЕ. Автономный виджет, который вставляют в чужую
 * разметку, обязан обрывать наследование на своём корне ЯВНО. Одного
 * объявления мало: источник — чужой CSS, которого мы не видим.
 */

/* Корень виджета. Отличается от потомков двумя вещами: margin не
   обнуляется (шаблон вправе центрировать сам БЛОК в шапке — это его
   право, и `margin: 0 auto` здесь как раз уместен), а font-size задаётся
   базовым значением, а не inherit: с него начинается цепочка наследования
   всей типографики виджета. */
.articart-search.articart-search.articart-search.articart-search {
    font-size: var(--articart-search-fs-base, 14px) !important;
    line-height: var(--articart-search-line-height, 1.3) !important;
    text-align: left !important;
    text-indent: 0 !important;
    text-transform: none !important;
    letter-spacing: normal !important;
    word-spacing: normal !important;
    font-style: normal !important;
}

/* Все потомки, включая псевдоэлементы. `font-size: inherit` вместо
   конкретного размера — так чужой clamp() снимается со ВСЕХ элементов
   разом, а свой кегль остаётся только у тех шести, которым он
   действительно нужен (перечислены ниже). */
.articart-search.articart-search.articart-search.articart-search *,
.articart-search.articart-search.articart-search.articart-search *::before,
.articart-search.articart-search.articart-search.articart-search *::after {
    margin: 0 !important;
    float: none !important;
    font-size: inherit !important;
    font-style: normal !important;
    line-height: var(--articart-search-line-height, 1.3) !important;
    text-align: left !important;
    text-indent: 0 !important;
    text-transform: none !important;
    text-decoration: none !important;
    letter-spacing: normal !important;
    word-spacing: normal !important;
    white-space: normal !important;
    text-overflow: clip !important;
    list-style: none !important;
}

/*
 * Возврат собственных значений. Специфичность (0,5,0) — на единицу выше
 * брони, поэтому порядок этих правил в файле роли не играет и случайная
 * перестановка блоков ничего не сломает.
 */
.articart-search.articart-search.articart-search.articart-search .articart-search__input {
    font-size: var(--articart-search-fs-input, 15px) !important;
}

.articart-search.articart-search.articart-search.articart-search .articart-search__title {
    font-size: var(--articart-search-fs-title, 14px) !important;
}

.articart-search.articart-search.articart-search.articart-search .articart-search__row-meta {
    font-size: var(--articart-search-fs-meta, 12px) !important;
}

.articart-search.articart-search.articart-search.articart-search .articart-search__row2 {
    font-size: var(--articart-search-fs-price, 13px) !important;
}

/* Цена не переносится никогда: «200 220» с переносом посередине читается
   как два разных числа. Это единственные три места, где nowrap оправдан. */
.articart-search.articart-search.articart-search.articart-search .articart-search__price,
.articart-search.articart-search.articart-search.articart-search .articart-search__price-old {
    white-space: nowrap !important;
}

.articart-search.articart-search.articart-search.articart-search .articart-search__price-old {
    text-decoration: line-through !important;
}

/* margin-left: auto — это НЕ отступ, а способ прижать чип наличия к
   правому краю строки. Обнуление полей бронёй его снимает, поэтому
   возвращается здесь явно. */
.articart-search.articart-search.articart-search.articart-search .articart-search__badge {
    font-size: var(--articart-search-fs-badge, 11px) !important;
    margin-left: auto !important;
    white-space: nowrap !important;
}

/* Пилюли знаков: броня выше приравнивает кегль всех потомков к
   унаследованному (`font-size: inherit`), и без этой строки маркер вышел бы
   ростом с название товара. Своя переменная, а не размер чипа наличия:
   величины разные по смыслу, хотя сегодня совпадают. */
.articart-search.articart-search.articart-search.articart-search .articart-search__marks .articart-badge {
    font-size: var(--articart-search-fs-mark, 11px) !important;
}

.articart-search.articart-search.articart-search.articart-search .articart-search__brand + .articart-search__pack::before {
    margin-right: 4px !important;
}

/* Два блока, которым центр нужен по замыслу, а не по наследству. */
.articart-search.articart-search.articart-search.articart-search .articart-search__empty {
    font-size: var(--articart-search-fs-base, 14px) !important;
    text-align: center !important;
}

.articart-search.articart-search.articart-search.articart-search .articart-search__show-all {
    font-size: var(--articart-search-fs-base, 14px) !important;
    text-align: center !important;
    margin-top: 4px !important;
}

/* --- Настройки в админке: превью пресета заглушки ------------------- */
.articart-iconpreset-wrap {
    display: inline-flex;
    align-items: center;
    gap: 10px;
}

.articart-iconpreset-preview {
    width: 60px;
    height: 60px;
    flex: 0 0 auto;
    padding: 6px;
    border: 1px solid var(--border-color, #ccd4db);
    border-radius: 6px;
    background: #fff;
    object-fit: contain;
}
