/* compare-widget.css — общие стили для drag-шторки (.ba-*) и fullscreen
   landscape-оверлея (.cmp-fs*). Подключается и compare.html, и generate.html,
   разметку .cmp-fs создаёт JS (ensureFsDom в compare-widget.js). */

.ba-slider { position:absolute; inset:0; overflow:hidden; cursor:ew-resize; user-select:none; -webkit-user-select:none; --ba-pct:50%; }
.ba-img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
.ba-img-before { z-index:1; }
.ba-img-after { z-index:2; clip-path: inset(0 calc(100% - var(--ba-pct)) 0 0); -webkit-clip-path: inset(0 calc(100% - var(--ba-pct)) 0 0); }
.ba-handle { position:absolute; top:0; bottom:0; left:var(--ba-pct,50%); width:3px; background:linear-gradient(180deg,transparent,#fff 25%,#fff 75%,transparent); z-index:5; transform:translateX(-50%); pointer-events:none; box-shadow:0 0 14px rgba(255,255,255,.4); }
.ba-handle::after { content:''; position:absolute; bottom:-5px; left:50%; width:10px; height:10px; background:#8b5cff; border-radius:50%; transform:translateX(-50%); box-shadow:0 0 10px rgba(139,92,255,.7), 0 0 0 3px rgba(255,255,255,.9); }
/* [СЛАЙДЕР1 э3 п.4/9] Стиль ручки — копия обычного (не-zoom) слайдера
   (правила .ba-handle / .ba-handle::after выше), увеличенная под масштаб
   зум-модалки: линия 3 → 4px, точка 10 → 14px. Никакого своего оформления:
   прежний вариант с символом ⇄ и полупрозрачной линией убран.
   Перетаскивание не затронуто — обработчики на .ba-slider, у .ba-handle
   как был, так и остаётся pointer-events:none. */
/* [СЛАЙДЕР1 э7 п.2] Линия шторки ПОЛНОСТЬЮ белая. Была
   linear-gradient(180deg,transparent,#fff 25%,#fff 75%,transparent) —
   концы уходили в прозрачность, отсюда «неоднородный цвет»: середина белая,
   верх и низ полупрозрачные поверх фото. Теперь сплошной #fff по всей высоте.
   Круглая ручка тоже была цветным вкраплением (#8b5cff с фиолетовым свечением)
   — стала белой. box-shadow оставлен, но чисто белый: без него белая линия
   пропадала бы на светлых кузовах. Это свечение, а не цвет самой линии. */
.cmp-fs .ba-handle {
  width:4px;
  background:#fff;
  box-shadow:0 0 16px rgba(255,255,255,.45);
}
.cmp-fs .ba-handle::after {
  content:''; top:auto; bottom:-7px; left:50%; transform:translateX(-50%);
  width:14px; height:14px; border-radius:50%;
  /* [СЛАЙДЕР1 э8 п.2] Круглый хват возвращён к виду ДО этапа 7. Требование
     «сделать однотонной» относилось только к САМОЙ линии-разделителю
     (background:#fff выше — остаётся), а не к ручке. */
  background:#8b5cff; color:transparent;
  box-shadow:0 0 12px rgba(139,92,255,.75), 0 0 0 4px rgba(255,255,255,.9);
}

/* В fullscreen фото не обрезается (contain, а не cover) — целиком видно всю
   машину; портретный инлайн-вид (.cmp-stage .ba-img) это правило не задевает,
   там остаётся cover как было. */
.cmp-fs__stage .ba-img { object-fit:contain; }

/* ════════════════════════════════════════════════════════════════
   Fullscreen — landscape-композиция.
   ВСЯ раскладка (flex-direction:row, рейлы-колонки миниатюр, stage
   по центру с aspect-ratio) — ОДИН набор правил, применяется ВСЕГДА,
   независимо от физической ориентации экрана. Ничего не дублируется
   между "portrait" и "landscape" веткой — они отличаются ТОЛЬКО тем,
   как .cmp-fs__rotator позиционируется/масштабируется:

   - Физически portrait (базовое правило, без media query):
     .cmp-fs__rotator получает width:100vh; height:100vw;
     transform:rotate(-90deg) — CSS-разворот содержимого (той же самой
     row-раскладки) в landscape-композицию, физическая ориентация
     экрана/viewport при этом не меняется (без screen orientation lock,
     без requestFullscreen — тот отдельно, см. compare-widget.js).
   - Физически landscape (@media (orientation: landscape)) — юзер сам
     повернул телефон: transform убирается, .cmp-fs__rotator просто
     занимает 100%/100% реального (уже широкого) viewport — ТА ЖЕ
     row-раскладка отображается без какого-либо поворота.

   .cmp-fs__stage — flex:1 1 0 + align-self:center + aspect-ratio через
   --stage-ratio (реальные natural width/height загруженного фото, ставит
   JS в renderFsStage) — контейнер точно повторяет форму конкретного фото,
   никакого пустого поля вокруг независимо от цвета фона.
   ════════════════════════════════════════════════════════════════ */
/* [СЛАЙДЕР1 п.5] Две ОСОЗНАННО раздельные ветки, а не одна адаптивная
   раскладка: на телефоне зум должен занимать весь экран, на десктопе — быть
   компактным окном поверх затемнения. Общее только скрытие по умолчанию.
   Раньше базовым (без media query) был мобильный CSS-разворот, а десктоп
   попадал под @media (orientation: landscape) случайно — просто потому что
   окно браузера обычно шире, чем выше. */
.cmp-fs { position:fixed; z-index:1100; display:none; }

/* ── МОБИЛЬНАЯ ВЕТКА: любое устройство с тачскрином ─────────────
   [СЛАЙДЕР1 э2] Критерий — ТИП ВВОДА, а не ширина экрана.
   (hover:none) and (pointer:coarse) = палец, нет мыши. Ширина сюда не
   входит намеренно: телефон в альбомной ориентации часто шире 600px и
   раньше проваливался в десктопную ветку — модалка «схлопывалась» в
   компактную панель прямо при повороте телефона. Тип ввода от поворота
   не зависит, поэтому телефон остаётся мобильным в любой ориентации.

   ПЛАНШЕТЫ попадают СЮДА же (у них тоже hover:none + pointer:coarse) —
   получат крупный полноэкранный вид. Отдельная третья ветка под планшет
   не делается: это вопрос адаптации всего сайта под три класса устройств,
   а не одного слайдера. Наблюдение зафиксировано, решение отложено. */
@media (hover: none) and (pointer: coarse) {
  /* [СЛАЙДЕР1 э8 п.3] Почему скругление этапа 7 не было видно: радиус стоял
     на .cmp-fs, а этот элемент на телефоне уходит в НАТИВНЫЙ fullscreen
     (requestNativeFullscreen в compare-widget.js, только для max-width:600px).
     UA-стили :fullscreen прибивают его к физическому экрану через !important
     (width/height/inset), поэтому скругление попадало на углы САМОГО экрана —
     туда, где у телефона и так скруглённый дисплей и рамка корпуса. Увидеть
     его было негде. Теперь панель — это .cmp-fs__rotator: он светлый,
     скруглённый и утоплен на 6px от края, а .cmp-fs стал тёмной подложкой,
     на фоне которой скругление читается. Цена — 12px экрана по каждой оси. */
  .cmp-fs { inset:0; height:100vh; height:100dvh; background:#0b0713; overflow:hidden; }
  .cmp-fs.open { display:block; }

  /* Физически portrait: содержимое разворачивается на -90°, чтобы широкая
     landscape-композиция поместилась на вертикальном экране. */
  .cmp-fs__rotator {
    position:absolute; top:50%; left:50%;
    /* -12px по обеим осям = по 6px отступа с каждой стороны. Размер задаётся
       через calc, а не padding у .cmp-fs: ротатор абсолютный, а абсолютное
       позиционирование считается от padding-box родителя — padding его бы
       не утопил. */
    /* [РОТАТОР-100VH] Ротатор повёрнут на -90°, поэтому его ШИРИНА ложится на
       высоту экрана — отсюда vh в width. На iOS/Safari в портретном режиме
       адресная строка съедает часть 100vh, и миниатюры обрезались по верхнему
       и нижнему краю. dvh считается от динамического вьюпорта и учитывает
       свёрнутую/развёрнутую панель.
       Две строки подряд, а не @supports: браузер без поддержки dvh просто не
       поймёт второе объявление и оставит первое. Фолбэк без лишней вложенности.
       height остаётся на vw: по горизонтали адресная строка ничего не отнимает. */
    width:calc(100vh - 12px); height:calc(100vw - 12px);
    width:calc(100dvh - 12px);
    background:#eef0f3; border-radius:12px;
    transform: translate(-50%,-50%) rotate(-90deg);
    transform-origin:center center;
    display:flex; flex-direction:row; align-items:stretch; justify-content:center;
    /* [СЛАЙДЕР1 э5 п.1] gap 6 → 4px: миниатюры придвинуты ближе к краям
       слайдера. Значение читает fitStageToImage из computed style, дублировать
       его числом в JS больше не нужно. */
    gap:4px; padding:6px; box-sizing:border-box;
  }
}
/* Телефон физически повернули — CSS-разворот не нужен, та же row-раскладка
   уже помещается. Ветка осталась внутри тач-условия: раньше она ловила и
   десктоп (окно браузера почти всегда landscape). */
@media (hover: none) and (pointer: coarse) and (orientation: landscape) {
  /* Тот же отступ 6px, что и в portrait, но через margin — здесь ротатор
     static, и calc от 100% считается от родителя уже корректно. Фон и
     border-radius наследуются из правила выше: селектор тот же. */
  .cmp-fs__rotator {
    position:static; width:calc(100% - 12px); height:calc(100% - 12px);
    margin:6px; transform:none;
  }
}

/* ── ДЕСКТОПНАЯ ВЕТКА: устройства с мышью ──────────────────────
   Строгое дополнение мобильной (not all and ...), поэтому третьего
   состояния не возникает: любое устройство попадает ровно в одну ветку. */
@media not all and (hover: none) and (pointer: coarse) {
  .cmp-fs {
    --cmp-panel-w: min(1080px, 90vw);
    --cmp-panel-h: min(660px, 84vh);
    inset:0; background:rgba(12,8,22,.72); overflow:auto;
  }
  /* Клик по затемнению закрывает окно — обработчик сверяет e.target.id,
     поэтому подложкой остаётся сам .cmp-fs, а не псевдоэлемент. */
  .cmp-fs.open { display:flex; align-items:center; justify-content:center; }

  .cmp-fs__rotator {
    position:relative; top:auto; left:auto; transform:none;
    width:var(--cmp-panel-w); height:var(--cmp-panel-h);
    /* [СЛАЙДЕР1 э7 п.3] 20 → 10px: скругление должно быть лёгким. */
    /* [СЛАЙДЕР1 э8 п.3] 10 → 12px — один радиус с мобильной веткой. Контент
       не режется: padding ротатора 10px, точка (10,10) совпадает с центром
       дуги радиуса 12px, то есть лежит глубоко внутри неё. */
    background:#eef0f3; border-radius:12px; overflow:hidden;
    box-shadow:0 30px 80px rgba(6,4,14,.55);
    display:flex; flex-direction:row; align-items:stretch; justify-content:center;
    gap:8px; padding:10px; box-sizing:border-box;
  }

  .cmp-fs__close { top:12px; right:12px; }

  /* [СЛАЙДЕР1 э2 п.4] Миниатюры переполняют столбец — переносятся ВБОК,
     сеткой, а не вертикальной прокруткой как на телефоне. */
  .cmp-fs__rail {
    flex-direction:row; flex-wrap:wrap; align-content:flex-start; justify-content:flex-start;
    min-width:0; max-width:242px; gap:8px;
  }
  /* [СЛАЙДЕР1 э3 п.2] Десктопный размер миниатюр: 84 → 109px (+30% от
     исходного). Число отдельное от мобильного (там +20%). */
  .cmp-fs__rail-thumb, .cmp-fs__rail-add { width:109px; border-radius:5px; }

  /* [СЛАЙДЕР1 э3 п.1] Кнопки инструментов в зуме крупнее, чем в обычном
     режиме: 27 → 32px (+19%). Стиль при этом тот же самый. */
  .cmp-fs__tool { width:32px; height:32px; font-size:1rem; border-radius:6px; }
}

.cmp-fs__stage {
  /* Никакой карточки-обёртки — ни фона, ни border-radius, ни тени, ни
     border. Только сам контейнер под <img>, точно её же формы. */
  /* flex:0 0 auto (не 1 1 0!) — размер целиком задаёт JS (fitStageToImage
     в compare-widget.js) явным width/height в px от natural-размеров
     фото. При flex-grow:1 браузер сам пересчитывал бы главную ось,
     игнорируя заданный размер — aspect-ratio держал только СООТНОШЕНИЕ
     сторон, а не абсолютные пиксели, отсюда и зазор вокруг фото. */
  position:relative; flex:0 0 auto; align-self:center;
  box-sizing:border-box;
}
.cmp-fs__stage .ba-slider { position:absolute; inset:0; cursor:ew-resize; }

/* [СЛАЙДЕР1 э5] Колонка «сцена + подпись» и сама подпись.
   Раскладка и структура повторяют .cmp-names обычного режима
   (compare.html:46-53). Отличие одно и вынужденное: там страница тёмная,
   а фон модалки светлый (#eef0f3), поэтому цвета текста инвертированы —
   лаймовый #ccff33 на светлом был бы нечитаем. */
/* [СЛАЙДЕР1 э5 п.1] flex:1 1 auto → 0 1 auto. Колонка растягивалась на всю
   свободную ширину ротатора, а сцена внутри стояла по центру (align-self) —
   из-за этого рельсы миниатюр прижимались к краям экрана, а не к краям
   слайдера, и левый край подписи уходил левее фото. Теперь ширину колонки
   задаёт её содержимое: сцена (её px-ширину ставит fitStageToImage) и
   подпись, которой та же функция проставляет РОВНО ширину сцены. */
.cmp-fs__center {
  flex:0 1 auto; min-width:0; min-height:0;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px;
}
/* width ставит JS (= ширина сцены), поэтому здесь ни 100%, ни max-width:720px:
   оба тянули подпись шире фото. padding убран — название должно начинаться
   от той же левой границы, что и сам слайдер. */
.cmp-fs__names {
  flex:0 0 auto; width:auto; max-width:100%;
  display:flex; align-items:center; justify-content:space-between; gap:10px; padding:0;
}
/* [СЛАЙДЕР1 э7] Кружок виден на ОБЕИХ версиях. История: этап 5 убрал его
   глобально (вырезав и спаны, и правила) ради мобильной компактности — из-за
   чего он пропал и на десктопе; этап 6 вернул разметку и погасил кружок только
   на телефоне; этап 7 отменил и это — владельцу он нужен везде. Сейчас правила
   общие и ни одного display:none под них нет.
   На высоту мобильной подписи это не влияет: кружок 9px по align-self:center
   ниже строки текста, а компактность держат line-height и размеры шрифта в
   мобильном медиа-запросе. Цена — ~13px ширины на каждую сторону подписи. */
.cmp-fs__names-side { display:flex; align-items:baseline; gap:6px; min-width:0; max-width:48%; overflow:hidden; }
.cmp-fs__names-side--right { flex-direction:row-reverse; text-align:right; }
/* align-self:center — сам кружок круглый, по baseline он бы «тонул» под
   строку; остальные дети подписи выравниваются по baseline друг с другом. */
.cmp-fs__names-dot { width:9px; height:9px; border-radius:50%; flex-shrink:0; align-self:center; }
.cmp-fs__names-side--left  .cmp-fs__names-dot { background:var(--cmp-left, #a78bff); }
.cmp-fs__names-side--right .cmp-fs__names-dot { background:var(--cmp-right, #ff3ec9); }
.cmp-fs__names-code {
  font-family:'JetBrains Mono', monospace; font-size:.82rem; font-weight:800; letter-spacing:.03em;
  color:#6d28d9; flex-shrink:0;
}
.cmp-fs__names-title {
  font-size:.92rem; font-weight:700; color:#1a1620;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
@media (hover: none) and (pointer: coarse) {
  .cmp-fs__names-code  { font-size:.72rem; }
  .cmp-fs__names-title { font-size:.8rem; }
  /* [СЛАЙДЕР1 э5 п.1] Подпись максимально компактная по высоте: на телефоне
     каждый её пиксель отнимается у блока сравнения (fitStageToImage вычитает
     offsetHeight подписи из доступной высоты). line-height:1.1 вместо
     наследуемого и зазор колонки 6 → 2px. */
  .cmp-fs__center { gap:2px; }
  .cmp-fs__names { line-height:1.1; }
  .cmp-fs__names-side { gap:4px; }
  /* [СЛАЙДЕР1 э7] Здесь было .cmp-fs__names-dot{display:none} — снято, кружок
     нужен и на мобильной версии. */
}

/* Рейлы миниатюр — вертикальные колонки слева/справа от stage (одна и та
   же раскладка что в CSS-развороте, что в реальном landscape). */
.cmp-fs__rail {
  flex:0 0 auto; display:flex; flex-direction:column; align-items:center; gap:8px; padding:6px 0;
  overflow-x:hidden; overflow-y:auto; scrollbar-width:none;
  min-width:117px; max-width:135px;
  -webkit-overflow-scrolling:touch; scroll-behavior:smooth; scroll-snap-type:y proximity;
  touch-action:pan-y;
}
.cmp-fs__rail::-webkit-scrollbar { display:none; }
/* [РЕЛЬСА-СВАЙП] ОТКРЫТЫЙ БАГ и история попыток — чтобы не пробовали заново.
   Симптом: в ПОРТРЕТЕ рельса не прокручивается свайпом. Правило выше сейчас в
   точности в том состоянии, в каком было до всех попыток.

   Что перепробовали и откатили:
   1. Значение touch-action меняли на auto — портрет не починило, а в LANDSCAPE
      сломало независимую прокрутку двух рельс двумя пальцами. Механизм: auto
      оставляет в наборе разрешённых жестов pinch-zoom, и второе одновременное
      касание уходит браузеру как кандидат на масштабирование страницы, вместо
      того чтобы вести свой скролл-контейнер. pan-y это исключает. Возвращено.
   2. Снятие -webkit-overflow-scrolling у этого правила — не помогло, откачено.
   3. Снятие scroll-snap-type и scroll-behavior в тач-ветке — не помогло, откачено.
   4. Портретная раскладка без поворота (сцена сверху, рельсы горизонтальными
      полосами) — визуально развалилась на устройстве, откачена целиком.

   Что установлено точно: дело не в кэше, не в JS-перехвате жеста (все слушатели
   passive и к рельсе не привязаны), не в накоплении обработчиков, не в
   сохранённом scrollTop и не в знаке угла (в коде только -90deg). Симптом
   строго коррелирует с тем, применён ли transform: когда поворота нет — свайп
   работает. Остаётся сама связка «нативная прокрутка внутри повёрнутого
   предка»; лечится она отказом от нативной прокрутки в пользу JS либо отказом
   от показа зума в портрете. Оба варианта дорогие и не начаты.

   Имена свойств здесь повторяются только там, где это не мешает проверкам. */

/* [СЛАЙДЕР1 э7 п.1] Крестик лежит в углу самой .cmp-fs и не участвует в
   flex-раскладке, поэтому развести его с кнопкой «+» можно только отступом
   рельсы. Сторона отступа зависит от того, повёрнут ли контент:
   — не повёрнут (десктоп и телефон в landscape): правая рельса идёт сверху
     вниз у правого края, под крестиком оказывается её НАЧАЛО → padding-top;
   — повёрнут на -90° (телефон в portrait): локальная ось X уходит вверх
     экрана, правая рельса ложится горизонтальной полосой по ВЕРХУ, её начало
     оказывается слева, а под крестиком справа — её КОНЕЦ → padding-bottom.
   52px = 10px отступ крестика + 32px его высота + 10px зазор.

   [СЛАЙДЕР1 э9 п.2] Безусловное правило #cmpRailRight{padding-top:52px}
   отсюда УБРАНО — оно было ошибкой этапа 7. На десктопе .cmp-fs__close
   позиционируется относительно .cmp-fs, а .cmp-fs там — полноэкранная
   ПОДЛОЖКА (inset:0), тогда как сама панель (.cmp-fs__rotator) отцентрована
   внутри неё. То есть крестик висит в углу ЭКРАНА, а не панели, и с «+» он
   на десктопе не пересекался никогда — зато отступ честно опускал «+» на
   52px ниже верхнего края кадра. Клиренс нужен только там, где .cmp-fs и
   есть панель, то есть в мобильных ветках ниже. */
/* [РЕЛЬСА-ВЫРАВНИВАНИЕ] Клиренс под крестик теперь на ОБЕИХ рельсах, а не
   только на правой. Замер на устройстве (ландшафт): padT левой 6px, правой 52px —
   колонки начинались на разной высоте. На длинных списках это не читалось, а
   после удаления миниатюры разъезд бросался в глаза, и его дважды принимали за
   баг перерисовки (лечили синхронным рендером обеих рельс и кэшем пропорций —
   обе правки полезны, но не про это).

   Эффект правки этапа 7 сохранён: 52px по-прежнему разводят крестик шторки с
   кнопкой «+», которая рендерится первой в рельсе (renderFsRail). Цена симметрии
   — 46px видимой области в той рельсе, где клиренс не нужен по геометрии. Это
   осознанно: рельса прокручивается, потеря мягкая, а разъезд колонок — визуальный
   дефект, который виден всегда.

   Сторона отступа по-прежнему зависит от поворота (портрет — снизу, ландшафт —
   сверху), логика этапа 7 не тронута; симметризован только выбор рельсы.

   Замечено при разборе, не менял: 52px = 10 + 32 + 10 отсчитаны от .cmp-fs, но
   рельса и так начинается на 12px ниже (margin 6 у ротатора + его padding 6),
   поэтому фактически хватило бы ~40px. Проверять это надо на устройстве. */
@media (hover: none) and (pointer: coarse) {
  #cmpRailLeft, #cmpRailRight { padding-top:6px; padding-bottom:52px; }
}
@media (hover: none) and (pointer: coarse) and (orientation: landscape) {
  #cmpRailLeft, #cmpRailRight { padding-top:52px; padding-bottom:6px; }
}

.cmp-fs__rail-thumb {
  /* [СЛАЙДЕР1 э2 п.2] 84 → 92px (+10%).
     [п.3] aspect-ratio 3/2 вместо квадрата: при background-size:contain
     квадрат оставлял пустые поля сверху и снизу, и рамка выглядела крупнее
     самой миниатюры. Пропорция кадра совпадает с фото — рамка облегает. */
  /* [СЛАЙДЕР1 э3 п.7] Мобильный размер: 84 → 101px (+20% от исходного).
     [п.8] box-sizing:border-box — рамка входит В размер, а не прибавляется
     к нему: без него border:2px делал внешний прямоугольник на 4px больше
     самой миниатюры, отсюда и «рамка не совпадает».
     [п.8] Скругление 14 → 7px (вдвое) — сильное скругление срезало углы
     изображения. */
  /* [СЛАЙДЕР1 э4 п.7] 84 → 109px (+30%) — одинаково для обеих веток,
     прежнее разделение 30/20 отменено.
     [п.3/8] Скругление 7 → 8px (+20% от текущего).
     aspect-ratio здесь только ЗАПАСНОЙ: настоящую пропорцию ставит JS из
     natural-размеров фото (renderFsRail), иначе contain оставлял поля. */
  /* [СЛАЙДЕР1 э5 п.2] Диагноз зазора между фото и внешней границей: рамка
     здесь БЫЛА всегда (border:2px solid transparent), одинаковой толщины в
     обоих состояниях, и box-sizing её уже учитывал — скачка размера не было.
     Зазор давало другое: aspect-ratio при box-sizing:border-box соблюдается
     на ВНЕШНЕМ прямоугольнике (109 x 109/R), а background-size:contain по
     умолчанию вписывает кадр в padding-box (105 x 109/R-4) — у него пропорция
     уже не R, и картинка летербоксится внутри собственной рамки. Лечится
     background-origin:border-box: область фона совпадает с тем боксом, чью
     пропорцию поставил JS, contain заполняет её целиком, полей нет.
     Второе: background-clip по умолчанию border-box, поэтому фон-заглушка
     красилась ПОД прозрачной рамкой — неактивная миниатюра выглядела как
     просвет, а не как рамка. Теперь рамка в неактивном состоянии имеет свой
     видимый нейтральный цвет, и при выборе меняется только ЦВЕТ. */
  position:relative; box-sizing:border-box; flex:0 0 auto;
  width:109px; height:auto; aspect-ratio:3 / 2; border-radius:5px; overflow:hidden;
  /* [СЛАЙДЕР1 п.2/10] contain вместо cover: миниатюра показывает машину
     целиком, а не обрезанный центр кадра. Поля добираются фоном. */
  background-color:rgba(20,15,28,.08); background-size:contain; background-position:center; background-repeat:no-repeat;
  background-origin:border-box; background-clip:border-box;
  /* [СЛАЙДЕР1 э6 п.3] Нейтральная рамка неактивной миниатюры: .22 → .38.
     Прозрачной она перестала быть ещё на этапе 5, но 22% на светлой панели
     #eef0f3 давали настолько бледную линию, что читались как «рамки нет».
     .38 поверх #eef0f3 даёт примерно #9b9aa1 — рамка видна всегда.
     Цвет намеренно ДЕсатурированный серый, а не тонированный: он не спорит
     ни с --cmp-left (#a78bff, фиолетовый), ни с --cmp-right (#ff3ec9,
     маджента) — активная миниатюра остаётся однозначно выделенной. */
  /* [СЛАЙДЕР1 э9 п.1] Толщина неактивной — 2px, активной — 4px (ниже).
     box-sizing:border-box, поэтому внешний прямоугольник 109px не меняется
     ни при какой толщине: рамка растёт внутрь, рельса при смене выбора не
     дёргается. Это объявление — ЗАПАСНОЕ, для браузеров без color-mix;
     нейтральный серый поднят с .38 до .45, чтобы и в этом случае рамка
     читалась. */
  border:2px solid rgba(20,15,28,.45); cursor:pointer; scroll-snap-align:start;
  /* [РЕЛЬСА-СВАЙП-УДАЛЕНИЕ] При свайпе мышью на десктопе без этого выделяется
     окружающий текст и жест выглядит сломанным. Тач не затронут. */
  user-select:none; -webkit-user-select:none;
}
/* Геометрия и скругление те же, что выше; меняются цвет и толщина. */
.cmp-fs__rail-thumb.active--left  { border-width:4px; border-color: var(--cmp-left, #a78bff); }
.cmp-fs__rail-thumb.active--right { border-width:4px; border-color: var(--cmp-right, #ff3ec9); }
/* [СЛАЙДЕР1 э7] Рамка НЕАКТИВНОЙ миниатюры тоже цветная — тон своей стороны,
   разбавленный белым. Прежний нейтральный rgba(20,15,28,.38) владелец читал
   как «рамки нет». Активная остаётся чистым цветом стороны, поэтому разница
   между состояниями сохраняется — теперь это насыщенность, а не наличие.
   Селекторы через id рельсы: сама миниатюра о своей стороне не знает, её
   задаёт контейнер (#cmpRailLeft/#cmpRailRight из ensureFsDom). Активные
   правила продублированы с тем же id — иначе (1 id + 1 класс) перебивало бы
   .active--left (2 класса) и выбранная миниатюра осталась бы блёклой.
   Объявление rgba в базовом правиле работает запасным: браузер без color-mix
   отбросит эти четыре строки и покажет прежнюю нейтральную рамку. */
/* [СЛАЙДЕР1 э9 п.1] Внешнее кольцо box-shadow убрано целиком: вместе с
   border оно давало у активной миниатюры два концентрических контура —
   эффект «двойной рамки». Остался ОДИН border.

   Числа и почему именно они. Панель светлая (#eef0f3), поэтому подмешивание
   белого работает против видимости: чем выше доля белого, тем ближе рамка к
   фону. На 28% (этап 8) неактивная почти сливалась. Поднято до 70%:
     левая  70% #a78bff + 30% #fff = #c1aeff
     правая 70% #ff3ec9 + 30% #fff = #ff78d9
   Оба тона однозначно цветные на фоне #eef0f3 — факт наличия рамки виден
   сразу.

   Разницу с активным состоянием несут СРАЗУ ДВЕ переменные, поэтому её не
   приходится добирать свечением:
     насыщенность  70%  → 100% (чистый цвет стороны)
     толщина       2px  → 4px  (ровно вдвое)
   Удвоение толщины — самый сильный сигнал и, в отличие от двух близких
   оттенков одного цвета, не требует сравнивать миниатюры между собой. */
#cmpRailLeft  .cmp-fs__rail-thumb { border-color:color-mix(in srgb, var(--cmp-left,#a78bff) 70%, #fff); }
#cmpRailRight .cmp-fs__rail-thumb { border-color:color-mix(in srgb, var(--cmp-right,#ff3ec9) 70%, #fff); }
#cmpRailLeft  .cmp-fs__rail-thumb.active--left  { border-width:4px; border-color:var(--cmp-left,#a78bff); }
#cmpRailRight .cmp-fs__rail-thumb.active--right { border-width:4px; border-color:var(--cmp-right,#ff3ec9); }
.cmp-fs__rail-thumb__del {
  position:absolute; top:3px; right:3px; width:22px; height:22px; border-radius:50%;
  background:rgba(7,5,13,.85); color:#fff; border:1px solid rgba(255,255,255,.25); font-size:.72rem;
  display:flex; align-items:center; justify-content:center; cursor:pointer; z-index:2;
}
.cmp-fs__rail-thumb__del:hover { background:rgba(255,62,80,.8); }
/* [УДАЛЕНИЕ-БЕЗ-ПОДТВЕРЖДЕНИЯ] На тач-устройствах крестик убран.
   Гейта на показ у него не было никогда — :hover выше только подкрашивает фон,
   поэтому кнопка висела поверх миниатюры всегда, и промах пальцем по рельсе
   убирал кадр без единого вопроса.
   Замены жестом (долгий тап, свайп) сознательно нет: действие обратимое и
   локальное — onRailDelete в режиме каталога не делает ничего, в режиме
   генераций убирает элемент только из списка текущей сессии, и перезагрузка
   страницы возвращает миниатюру. Новый жест ради этого не окупается.
   Детект — тот же, что во всех мобильных ветках файла (Ф-СЛАЙДЕР1): тип ввода,
   а не ширина экрана. */
@media (hover: none) and (pointer: coarse) {
  .cmp-fs__rail-thumb__del { display:none; }
}
.cmp-fs__rail-add {
  /* [СЛАЙДЕР1 э2 п.3] Тот же масштаб и пропорции, что у миниатюр. */
  /* Тот же масштаб, пропорции, box-sizing и скругление, что у миниатюр. */
  box-sizing:border-box; flex:0 0 auto;
  width:109px; height:auto; aspect-ratio:3 / 2; border-radius:5px; border:2px dashed rgba(20,15,28,.25);
  background:rgba(20,15,28,.04); color:rgba(20,15,28,.55); display:flex; flex-direction:column;
  align-items:center; justify-content:center; font-size:1.1rem; font-weight:700; gap:2px; cursor:pointer;
  scroll-snap-align:start;
}
.cmp-fs__rail-add:hover { border-color:var(--cmp-left,#a78bff); color:#1a1620; }
.cmp-fs__rail-empty {
  flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
  text-align:center; padding:8px 14px; color:rgba(20,15,28,.62); font-size:.72rem; font-weight:600;
}
/* [ЗУМ-ПУСТОЙ-НАБОР] Тот же язык, что у пустой рельсы выше: та же типографика
   и тот же приглушённый цвет по светлой панели. Отличие одно — собственные
   min-размеры и паддинг. У .cmp-fs__stage flex:0 0 auto, а его width/height в
   пикселях ставит JS от natural-размеров фото; фото здесь нет, инлайновые
   размеры сброшены, и без своих габаритов контейнер схлопнулся бы в точку.
   Три элемента в колонке: заголовок, поясняющая строка, кнопка. gap у заголовка
   меньше — он и текст читаются одним абзацем, кнопка отделена. */
.cmp-fs__stage-empty {
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px;
  min-width:200px; min-height:130px; padding:24px 28px;
  text-align:center; color:rgba(20,15,28,.62); font-size:.78rem; font-weight:600;
}
.cmp-fs__stage-empty__title {
  font-size:.96rem; font-weight:800; color:rgba(20,15,28,.86);
}
.cmp-fs__stage-empty .btn-primary {
  margin-top:8px; padding:9px 18px; font-size:.78rem;
}

/* Кнопки закрыть / поменять стороны — фиксированы относительно реального
   (не повёрнутого) экрана, чтобы оставаться в предсказуемом углу. */
/* [СЛАЙДЕР1 п.4/9] Правила .cmp-fs__swap удалены вместе с кнопкой. */
/* [СЛАЙДЕР1 э7 п.1] Крестик приведён к стилю кластера инструментов: те же
   32px, скругление 5px, фон #8b5cff, та же тень и тот же ховер, что у
   .cmp-fs__tool. Был белый круг 38px с собственным градиентным ховером и
   вращением на 90° — единственный элемент модалки со своим оформлением.
   Наложение на кнопку «+» снимают ОБЕ правки сразу: размер 38 → 32px и
   отступ рельсы под кнопкой (#cmpRailRight ниже). */
.cmp-fs__close {
  position:absolute; top:10px; right:10px; z-index:1200;
  width:32px; height:32px; padding:0; border-radius:5px;
  background:#8b5cff; border:none; color:#fff; font-size:1rem; line-height:1; cursor:pointer;
  box-shadow:0 4px 14px rgba(0,0,0,.35); transition:all .2s; display:inline-flex; align-items:center; justify-content:center;
}
.cmp-fs__close:hover { background:#ff3ec9; transform:scale(1.08); }

/* [СЛАЙДЕР1 п.6/12] Кластер инструментов — скачать / поделиться / избранное.
   Слева сверху, чтобы не спорить с крестиком справа. */
/* [СЛАЙДЕР1 э2 п.1] Кластер лежит ВНУТРИ .cmp-fs__stage (его туда переносит
   renderFsStage), поэтому координаты считаются от самого фото, а не от окна.
   Раньше он был сиблингом панели и позиционировался calc(50% - ...) от
   вьюпорта — из-за этого «уезжал» в угол страницы. Вид и размеры — те же,
   что у .cmp-tools/.cmp-tool-btn в обычном режиме на этой же странице. */
.cmp-fs__tools { position:absolute; bottom:6px; right:6px; top:auto; left:auto; z-index:6; display:flex; gap:5px; }
/* [СЛАЙДЕР1 э4 п.1/5] Один в один .cmp-tool-btn обычного режима, только
   крупнее: 27 → 32px (+19%) и иконка 16 → 19px. Форма, цвет, тень, ховер —
   те же. Правило общее для обеих веток: размер одинаков и там, и там. */
.cmp-fs__tool {
  height:32px; width:32px; padding:0; border-radius:5px;
  background:#8b5cff; border:none; color:#fff; line-height:1;
  display:inline-flex; align-items:center; justify-content:center; cursor:pointer;
  box-shadow:0 4px 14px rgba(0,0,0,.35); transition:all .2s;
}
/* [СЛАЙДЕР1 э6 п.4] stroke-linecap/linejoin:round — иконки Feather
   нарисованы под скруглённые концы; без них линии «поделиться» и стрелка
   «скачать» обрывались тупыми торцами. Правило общее, поэтому все три
   кнопки кластера выглядят единообразно. */
.cmp-fs__tool svg { width:19px; height:19px; stroke:currentColor; fill:none; stroke-width:2.2;
  stroke-linecap:round; stroke-linejoin:round; }
.cmp-fs__tool.is-on svg { fill:currentColor; }
.cmp-fs__tool:hover { background:#ff3ec9; transform:scale(1.08); }
.cmp-fs__tool.is-on { background:#ff3ec9; }

/* [СЛАЙДЕР1] Поповер выбора стороны для инструментов зум-модалки. */
.cmp-fs__toolpick {
  position:absolute; bottom:38px; right:6px; top:auto; left:auto; z-index:7; display:none;
  flex-direction:column; gap:2px; padding:8px; min-width:170px; max-width:70vw;
  background:#fff; border:1px solid rgba(20,15,28,.12); border-radius:14px;
  box-shadow:0 12px 30px rgba(20,15,28,.24);
}
.cmp-fs__toolpick.visible { display:flex; }
.cmp-fs__toolpick-title { font-size:.66rem; font-weight:800; letter-spacing:.02em;
  color:rgba(20,15,28,.5); padding:2px 6px 6px; }
.cmp-fs__toolpick button {
  display:flex; align-items:center; gap:8px; width:100%; padding:9px 10px;
  border:none; background:transparent; border-radius:9px; cursor:pointer;
  font-size:.8rem; font-weight:600; color:#1a1620; text-align:left;
  overflow:hidden; white-space:nowrap; text-overflow:ellipsis;
}
.cmp-fs__toolpick button:hover { background:rgba(139,92,255,.1); }
.cmp-fs__toolpick-dot { width:9px; height:9px; border-radius:50%; flex-shrink:0; }
.cmp-fs__toolpick-dot--l { background:var(--cmp-left,#a78bff); }
.cmp-fs__toolpick-dot--r { background:var(--cmp-right,#ff3ec9); }


/* ════════════════════════════════════════════════════════════
   Модалка выбора цвета (#pickModal/#pickGrid/#pickTitle) — общая для
   compare.html (статическая разметка в HTML) и generate.html (создаётся на
   лету через ensurePickModalDom() в compare-widget.js при первом открытии
   попапа из fullscreen). Раньше эти правила жили только в локальном <style>
   compare.html — из-за этого на generate.html попап открывался (JS отрабатывал
   без ошибок), но был полностью невидим: без position/z-index/display он
   рендерился позади .cmp-fs (z-index:1100).
   ════════════════════════════════════════════════════════════ */
/* z-index:1300 — выше .cmp-fs (1100) и .cmp-fs__close/.cmp-fs__swap (1200):
   на generate.html попап теперь открывается ПОВЕРХ уже открытого fullscreen
   (на compare.html конфликта не было — там попап открывается только в
   портретном режиме, до входа в fullscreen, но более высокий z-index
   безвреден и там, конкурировать там не с чем). */
.cmp-modal { position:fixed; inset:0; z-index:1300; display:none; background:rgba(5,3,12,.85); backdrop-filter:blur(6px); }
.cmp-modal.open { display:flex; align-items:flex-end; justify-content:center; }
.cmp-modal__panel { width:100%; max-width:720px; max-height:88vh; background:linear-gradient(160deg,#150c2c,#0a0614); border-top-left-radius:22px; border-top-right-radius:22px; border:1px solid rgba(167,139,255,.2); overflow:hidden; display:flex; flex-direction:column; animation:cmpUp .25s ease; }
@keyframes cmpUp { from{transform:translateY(40px);opacity:0;} to{transform:translateY(0);opacity:1;} }
.cmp-modal__head { padding:16px 18px; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid rgba(167,139,255,.15); }
.cmp-modal__title { font-size:1rem; font-weight:800; }
.cmp-modal__close { width:32px; height:32px; border-radius:50%; background:rgba(255,255,255,.08); border:none; color:#fff; font-size:1.1rem; cursor:pointer; }
/* [ПИКЕР-СЕТКА] Треки ФИКСИРОВАННОЙ ширины, а не minmax(...,1fr).
   Причина «при переносе на второй ряд вёрстка плывёт» — не auto-fit (его убрали
   в [ПИКЕР-ЛАНДШАФТ] ниже), а связка «1fr + появление скроллбара»:

   пока карточки влезают в один ряд, грид ниже панели и полосы прокрутки нет;
   второй ряд упирает панель в max-height:88vh, у грида включается overflow-y,
   классический скроллбар съедает ~15px инлайновой ширины — и все треки 1fr
   ПЕРЕСЧИТЫВАЮТСЯ. Разъезжается не новый ряд, а сразу все карточки.

   Фиксированный максимум это снимает: ширина карточки не зависит ни от числа
   рядов, ни от наличия скроллбара. Верхняя граница подобрана так, чтобы число
   колонок осталось прежним (портрет ~328px → 3 колонки по 100px).
   justify-content:center раскладывает остаток по краям, а не растягивает треки.
   scrollbar-gutter:stable убирает саму причину — место под полосу резервируется
   заранее, поэтому её появление больше ничего не сдвигает. */
.cmp-modal__grid {
  /* [ПИКЕР-СЕТКА-2] ГЛАВНОЕ здесь — min-height:0, и добавлено оно последним.
     Грид лежит flex-элементом в .cmp-modal__panel (flex-колонка, overflow:hidden,
     max-height:88vh). У flex-элемента min-height по умолчанию auto, то есть он
     ОТКАЗЫВАЕТСЯ сжиматься ниже высоты своего содержимого. Пока карточки в один
     ряд, это незаметно. Со второго ряда грид требует полную высоту, панель
     упирается в 88vh и обрезает лишнее своим overflow:hidden — а собственный
     overflow-y:auto грида НЕ включается, потому что его бокс и так равен
     содержимому. Внешне: второй ряд срезан, прокрутки нет, «вёрстка поплыла».
     min-height:0 снимает этот отказ, грид становится настоящим скролл-контейнером.
     Тот же приём уже стоит у .cmp-modal__info — сюда его просто не перенесли. */
  min-height:0;
  padding:14px 16px 24px; overflow-y:auto; display:grid;
  grid-template-columns:repeat(auto-fill, minmax(96px, 100px));
  /* [ПИКЕР-РЯДЫ] Ряды здесь неявные — задан только grid-template-columns, —
     поэтому их размер определяет grid-auto-rows, по умолчанию auto, то есть
     minmax(min-content, max-content).

     Минимум оказался нулевым: у .cmp-pick стоит overflow:hidden, а автоминимум
     грид-элемента равен контенту ТОЛЬКО при overflow:visible. Карточка тем самым
     разрешает сжать себя до нуля. Второе условие дал min-height:0 выше
     ([ПИКЕР-СЕТКА-2]): грид перестал переполнять панель и получил определённую
     высоту. Дальше алгоритм разделил её между треками ПОРОВНУ — замер с
     устройства показал два ряда по 68.4545px при потребных 122px, и
     68.4545*2 + 10 = 146.909 в точности равно content-box грида. Прокрутка при
     этом не включалась: переполнения не возникало, треки ужимались под
     контейнер. Симптомы — плотный кроп фото и срезанная подпись под ним.

     max-content убирает нижнюю границу из уравнения: ряд всегда равен своему
     содержимому независимо от высоты контейнера, а лишнее уходит в уже
     включённый overflow-y:auto. Работает одинаково при 1, 6 и 200 плёнках и не
     зависит от того, сколько места занимает панель фильтра.

     align-content:start — на случай обратной ситуации, когда рядов мало и место
     остаётся: значение по умолчанию (normal, ведёт себя как stretch) растянуло
     бы auto-треки. На max-content оно не действует, но пишем явно, чтобы
     поведение не зависело от того, каким останется grid-auto-rows. */
  grid-auto-rows:max-content;
  align-content:start;
  justify-content:center;
  scrollbar-gutter:stable;
  gap:10px;
}
.cmp-pick { position:relative; border-radius:12px; overflow:hidden; cursor:pointer; border:1px solid rgba(167,139,255,.15); background:rgba(20,12,44,.5); transition:transform .15s,border-color .15s; }
.cmp-pick:hover { transform:translateY(-3px); border-color:var(--cmp-left,#a78bff); }
.cmp-pick img { width:100%; aspect-ratio:3/2; object-fit:cover; display:block; }
/* [ПИКЕР-СЕРДЕЧКО] Полоса под фото: противовес | номер | сердечко.
   Боковые колонки одинаковой ширины — только так номер встаёт по центру
   карточки; при обычном flex он прижался бы к краю, а при space-between уехал
   бы влево, потому что слева соседа нет.

   Числа СВОИ, не перенесённые с плиток каталога. Карточка тут 96-100px против
   168px, и от 94px внутренней ширины после padding 6+6 остаётся 82px; минус
   две колонки по 16 и два зазора по 4 — под номер около 42px. Пятизначный
   артикул при .62rem занимает примерно 30px, то есть влезает с запасом, а
   длинный уедет в многоточие, не выталкивая кнопку.

   Одинаковые размеры на всех ширинах, без media-запроса: карточка тесная и на
   десктопе, и на телефоне, а разводить два набора чисел ради 46px разницы
   значит завести второй источник правды на ровном месте.

   Тач-цель 16px — меньше принятых в проекте 28px, и это осознанно: на 96px
   карточке 28px кнопка не оставила бы места номеру. Цена промаха здесь ниже,
   чем была у снятого крестика (см. комментарий в appendCards): мимо сердечка
   попадаешь в карточку, а она открывает попап ВЫБОРА действия, а не применяет
   цвет сразу. */
.cmp-pick__bar { display:grid; grid-template-columns:16px 1fr 16px; align-items:center; gap:4px; padding:5px 6px; }
.cmp-pick__name { font-size:.62rem; font-weight:700; text-align:center; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Голая иконка без подложки — как в полосе плиток каталога и карточек ряда. */
.cmp-pick__fav { width:16px; height:16px; padding:0; background:none; border:none; cursor:pointer;
  display:flex; align-items:center; justify-content:center; }
.cmp-pick__fav .cmp-heart { width:14px; height:14px; }
.cmp-pick__fav:hover .cmp-heart { stroke:#ff3b5c; }
/* [ПИКЕР-УДАЛЕНИЕ] Правила .cmp-pick__del убраны вместе с самой кнопкой:
   удаление переехало третьим пунктом попапа выбора действия (askPickAction).
   position:relative у .cmp-pick оставлен — он ни на что не мешает и пригодится
   следующему абсолютно позиционированному элементу карточки. */
.cmp-modal__empty { padding:50px 20px; text-align:center; color:var(--muted,#9990bf); }
.cmp-modal__empty .btn-primary { margin-top:14px; display:inline-block; }

/* [ПИКЕР-ЛАНДШАФТ] Карточки в окне выбора были в ландшафте МЕЛЬЧЕ, чем в портрете
   (106x93 против 113x99) — при том, что места на экране больше. Две причины,
   обе снимаются здесь и только здесь; портрет и десктоп не затронуты.

   1. Панель ограничена max-width:720px. На телефоне в ландшафте (~844px) это
      оставляло по ~62px мёртвых полей с каждой стороны — ширина, которую окно
      просто не использовало.
      Ставим 960px, а не «снять совсем»: 960 выше самых широких телефонов в
      ландшафте (у актуальных моделей до ~956pt), поэтому ЛЮБОЙ телефон получает
      полную ширину и поля исчезают. При этом планшет — а он тоже подпадает под
      тач-детект — не растягивает нижнюю шторку на все 1180-1366px, где она
      читалась бы хуже.

   2. Сетка использовала minmax(96px, 1fr): при росте доступной ширины она
      плодила НОВЫЕ колонки вместо того, чтобы расширять имеющиеся, — отсюда
      парадокс «шире экран, мельче карточки». Лечит это подъём МИНИМУМА до
      140px: колонок становится меньше, каждая шире.

   [ПИКЕР-ВКЛАДКИ] Здесь же стоял auto-fit — возвращён на auto-fill, и менять
   обратно НЕ НАДО. auto-fit схлопывает пустые треки, а уцелевшие делят между
   собой всю ширину панели по 1fr, поэтому ширина карточки начинает зависеть от
   ДЛИНЫ СПИСКА: на вкладке «Избранное» с парой плёнок карточки выходили в
   несколько раз крупнее, чем на «Каталоге» в том же окне. auto-fill пустые
   треки сохраняет, и карточка держит одну ширину при любой длине списка.
   Выигрыш п.2 при этом не теряется: его давал подъём минимума 96 → 140, а не
   auto-fit. */
@media (hover: none) and (pointer: coarse) and (orientation: landscape) {
  .cmp-modal__panel { max-width:960px; }

  /* [ПИКЕР-ПОЛНЫЙ-ЭКРАН] В ландшафте окно занимает ВСЮ высоту экрана в любом
     состоянии, а не только под карточкой характеристик (.cmp-modal.is-info).

     Основание — замеры с устройства (856x392, зум в нативном фуллскрине):
     при max-height:88vh панель получала 345.6px, из них шапка, вкладки и панель
     фильтра забирали ~206px, и гриду доставалось 138px. Одному ряду карточек
     нужно ~159px (картинка 146x97.3 при aspect-ratio 3/2 + подпись ~22 + рамки 2,
     плюс собственные падинги грида 14 и 24). То есть не помещался даже первый
     ряд. Полная высота возвращает эти ~49px.

     Ширину здесь НЕ снимаем. max-width:960px выше поставлен осознанно, чтобы на
     планшете шторка не растягивалась на 1180-1366px; проблема была в высоте, и
     трогать горизонталь незачем. Тем и отличается от .cmp-modal.is-info, где
     max-width:none — там карточка одна и ей полезна вся ширина.

     Панель фильтра намеренно не ужимается: её компоновка вынесена в отдельный
     этап дизайна. */
  .cmp-modal.open { align-items:stretch; }
  .cmp-modal__panel {
    height:100%; max-height:none;
    /* Скругление сверху теряет смысл, когда панель прижата к верхней кромке. */
    border-radius:0;
  }
  /* [ПИКЕР-СЕТКА] Тот же фиксированный максимум, что и в базовом правиле.
     146px подобраны под панель 960px: 6 колонок по 146 + 5 зазоров по 10 = 926
     при доступных 928 — то есть внешний вид ряда остаётся прежним, но перестаёт
     зависеть от появления полосы прокрутки. */
  .cmp-modal__grid  { grid-template-columns:repeat(auto-fill, minmax(140px, 146px)); }
}

/* ────────────────────────────────────────────────────────────
   [Ф-СЛАЙДЕР2] Подпись под миниатюрой рельсы и кнопка подгрузки
   истории в окне-пикере. Оба правила применяются только к узлам,
   которые создаются НОВЫМ кодом: на compare.html подписи не
   передаются (railCaption не задан), а разметка модалки там
   статическая и кнопки подгрузки в ней нет. Раскладка страницы
   сравнения этими правилами не затрагивается.
   ──────────────────────────────────────────────────────────── */

/* [п.1] Дата под карточкой. Контейнер .cmp-fs__rail-thumb уже имеет
   position:relative и overflow:hidden, поэтому подпись прижимается к низу
   миниатюры и обрезается по её скруглению. pointer-events:none — чтобы
   подпись не перехватывала клик выбора стороны. */
/* [БЕЙДЖ-ТОП] Верх-лево, симметрично крестику .cmp-fs__rail-thumb__del в
   верх-право. Подложка и pointer-events:none — как у __cap ниже, размер и вес —
   как у служебных подписей страницы (uppercase, 800, letter-spacing).
   Ловушка обрезки решается так же, как у чипа: у .cmp-fs__rail-thumb
   overflow:hidden при border-radius:5px и border:2px с box-sizing:border-box,
   то есть внутренний радиус 3px; точка (3,3) от внутреннего угла лежит ровно в
   центре этой дуги и под неё не попадает.

   [ЯРКОСТЬ] Фон — янтарный #FFB45A, уже живущий в палитре проекта
   (.catalog-status.warning, generate.html). Фирменный CTA-градиент
   #8b5cff→#ff3ec9 сюда НЕ взят намеренно: в этом виджете фиолетовый и
   маджента заняты семантикой сторон (--cmp-left/--cmp-right — рамки
   миниатюр, точки подписей, активные состояния), и бейдж теми же цветами
   читался бы как «эта карточка выбрана», а не «популярная». Янтарь здесь
   ни с чем не спорит и виден на любом кадре. Текст тёмный #1a1030 — на
   янтаре его контраст выше, чем у белого. box-shadow отрывает плашку от
   фото, чтобы она не «лежала» на светлом капоте.

   [ЦЕНТРОВКА] Убраны три независимых источника перекоса:
   - padding симметричен по обеим осям (3px по вертикали, 6px по
     горизонтали), а не подобран на глаз;
   - display:flex + align-items/justify-content:center вместо надежды на
     line-height: вертикаль больше не зависит от метрик шрифта.
     line-height:1 убирает лишний half-leading, габарит плашки при этом
     прежний (было 9.6+4 ≈ 14px, стало 8+6 = 14px), позиция не поехала;
   - text-indent равен letter-spacing. letter-spacing добавляет отступ и
     ПОСЛЕ последней буквы, и этот отступ входит в ширину строки — без
     компенсации «ТОП» стоит левее центра своей подложки. При ширине по
     содержимому indent тоже входит в ширину, поэтому точная компенсация —
     сдвиг вправо ровно на letter-spacing, не на половину. */
.cmp-fs__rail-thumb__badge {
  position:absolute; top:3px; left:3px; z-index:2;
  display:flex; align-items:center; justify-content:center;
  padding:3px 6px; border-radius:4px;
  background:#ffb45a;
  color:#1a1030;
  font-size:.5rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
  text-indent:.06em;
  line-height:1; pointer-events:none;
  box-shadow:0 1px 4px rgba(7,5,13,.45);
}

.cmp-fs__rail-thumb__cap {
  position:absolute; left:0; right:0; bottom:0;
  padding:2px 4px;
  font-family:'JetBrains Mono', monospace; font-size:.55rem; line-height:1.2;
  color:rgba(244,241,255,.9); text-align:center;
  background:linear-gradient(to top, rgba(7,5,13,.92), rgba(7,5,13,0));
  pointer-events:none;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* [п.5] «Показать ещё» — подгрузка следующей порции истории.
   .cmp-modal__panel это flex-колонка с overflow:hidden, а .cmp-modal__grid
   внутри скроллится (overflow-y:auto), поэтому кнопка остаётся видимой внизу
   панели и не уезжает вместе с содержимым грида.
   flex-shrink:0 — иначе при длинном списке flex сожмёт кнопку по высоте. */
.cmp-modal__more {
  flex-shrink:0;
  display:block; margin:0 auto 18px; padding:10px 22px;
  background:rgba(139,92,255,.14); border:1px solid rgba(167,139,255,.3);
  border-radius:10px; color:#c9b6ff;
  font-family:inherit; font-size:.8rem; font-weight:700; cursor:pointer;
}
.cmp-modal__more:hover {
  background:linear-gradient(135deg,#8b5cff,#ff3ec9);
  color:#fff; border-color:transparent;
}

/* [Ф-СЛАЙДЕР2 п.11] Вкладки источника в окне-пикере.
   .cmp-modal__panel — flex-колонка с overflow:hidden, содержимое скроллит
   .cmp-modal__grid. flex-shrink:0 удерживает полосу вкладок на месте, иначе
   при длинном списке flex сожмёт её по высоте вместе с гридом.
   [ПИКЕР-ВКЛАДКИ] Раньше здесь стояло «на compare.html этих узлов нет» — это
   больше не так: #pickTabs добавлен в статическую разметку compare.html, иначе
   renderTabs() выходил бы на !tabsEl и вкладки не появились бы. */
/* [ПИКЕР-4-ВКЛАДКИ] Полоса обязана вместить ЧЕТЫРЕ подписи в ОДНУ строку без
   горизонтальной прокрутки — требование владельца. Ни overflow-x, ни flex-wrap
   не рассматривались.

   Откуда числа. Панель окна на телефоне занимает всю ширину экрана
   (.cmp-modal__panel: width:100%), минус 2px её рамок. При прежних 0.76rem и
   padding 7px 14px четыре вкладки требовали около 436px при доступных 358 на
   экране 360px — не хватало примерно 78px. Их набрали тремя мелкими правками:

     кегль    .76 -> .70rem   (12.16 -> 11.2px)   ~ -18px на четырёх подписях
     padding  14 -> 9px по горизонтали           ~ -40px
     gap 8 -> 6, поля полосы 16 -> 12            ~ -14px

   плюс подпись «Просмотренные» сокращена до «Просмотр» (см. pickSources) — ещё
   около -34px. Итого потребность ~331px против 358 доступных: запас порядка
   27px, то есть больше погрешности расчёта по метрикам шрифта (±5%).

   flex-shrink:0 у самой полосы — чтобы при длинном списке карточек flex не
   сжал её по высоте вместе с гридом. У кнопок shrink оставлен по умолчанию
   намеренно: если подписи когда-нибудь всё же не влезут, они ужмутся, а не
   вылезут за край панели (у неё overflow:hidden, и обрезка была бы молчаливой).

   Вкладок бывает ТРИ или ЧЕТЫРЕ: «Генерации» условны. Три при этих же числах
   требуют ~255px — запас там кратный. */
.cmp-modal__tabs {
  flex-shrink:0;
  display:flex; gap:6px; padding:0 12px 12px;
  border-bottom:1px solid rgba(167,139,255,.12);
  margin-bottom:2px;
}
.cmp-modal__tab {
  padding:6px 9px; border-radius:99px;
  background:transparent; border:1px solid rgba(167,139,255,.25);
  color:var(--muted,#9990bf);
  font-family:inherit; font-size:.70rem; font-weight:700; cursor:pointer;
  white-space:nowrap;
}
.cmp-modal__tab:hover { border-color:var(--cmp-left,#a78bff); color:#f4f1ff; }
.cmp-modal__tab.active {
  background:linear-gradient(135deg,#8b5cff,#ff3ec9);
  border-color:transparent; color:#fff;
}

/* [ПИКЕР-ФИЛЬТР] Панель фильтра вкладки «Каталог» в окне «Добавить».
   Классы .filter-chip / .filter-chip.active намеренно те же, что на
   catalog.html, — но там они объявлены ИНЛАЙНОМ в самой странице, поэтому сюда
   их приходится повторить. Селекторы уложены внутрь .cmp-modal__filter, чтобы
   не заводить глобальное имя, которое столкнулось бы со страничным, если
   когда-нибудь оба CSS окажутся на одной странице.

   Размеры УМЕНЬШЕНЫ против каталога (чип 4x10 против 7x14, шрифт .7 против .82,
   поле поиска 6x12 против 9x16): окно пикера — нижняя шторка с max-height:88vh,
   и открывают её чаще всего в ландшафте, где по высоте всего ~390-430px. Панель
   каталожного размера съела бы там половину места под карточки.
   flex-shrink:0 — по той же причине, что у .cmp-modal__tabs: иначе flex сожмёт
   панель, когда список длинный. */
.cmp-modal__filter {
  flex-shrink:0;
  display:flex; flex-direction:column; gap:6px;
  padding:0 16px 10px;
  border-bottom:1px solid rgba(167,139,255,.12);
}
.cmp-modal__filter .cmp-fx__row { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.cmp-modal__filter .cmp-fx__label {
  font-family:'JetBrains Mono', monospace;
  font-size:.58rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:rgba(244,241,255,.4); margin-right:2px;
}
.cmp-modal__filter .filter-chip {
  padding:4px 10px; border-radius:99px;
  background:rgba(244,241,255,.04); border:1px solid rgba(167,139,255,.18);
  color:rgba(244,241,255,.7);
  font-family:inherit; font-size:.7rem; font-weight:600;
  cursor:pointer; white-space:nowrap; transition:background .15s, border-color .15s, color .15s;
}
.cmp-modal__filter .filter-chip:hover { background:rgba(167,139,255,.12); color:#fff; }
.cmp-modal__filter .filter-chip.active {
  background:linear-gradient(135deg,#8b5cff,#ff3ec9);
  border-color:transparent; color:#fff;
}
.cmp-modal__filter .cmp-fx__search {
  width:100%; box-sizing:border-box;
  padding:6px 12px; border-radius:8px;
  background:rgba(244,241,255,.04); border:1px solid rgba(167,139,255,.2);
  color:#fff; font-family:inherit; font-size:.78rem; outline:none;
  transition:border-color .15s, background .15s;
}
.cmp-modal__filter .cmp-fx__search:focus { border-color:rgba(167,139,255,.5); background:rgba(167,139,255,.08); }
.cmp-modal__filter .cmp-fx__search::placeholder { color:rgba(244,241,255,.35); }

/* [ПИКЕР-ИНФО] Карточка одной плёнки внутри окна «Добавить» и развилка
   «добавить / инфо» поверх него.

   Классы .modal-row / .modal-row-label / .modal-row-value повторяют каталожные
   имена, но у catalog.html они объявлены ИНЛАЙНОМ в самой странице — переиспользовать
   нечего, приходится дублировать. Обязательно внутри .cmp-modal__info, иначе
   заведём глобальные имена, которые столкнутся со страничными.

   Раскладка ГОРИЗОНТАЛЬНАЯ, в отличие от каталога: там сверху крупный блок фото,
   под ним строки. Здесь окно открывают в ландшафте, где по высоте ~390-430px, и
   вертикальная раскладка не помещается. Фото слева фиксированной колонкой,
   строки компактнее оригинала (6px против 12px, .78rem против .9rem). */
/* [ПИКЕР-ХАРАКТЕРИСТИКИ] Карточка не должна растягиваться на всю ширину шторки.
   В ландшафте панель — 960px, и без ограничения строки «label ... value» с их
   justify-content:space-between расползались на всю эту ширину: подпись у левого
   края, значение у правого, между ними полметра пустоты. Отсюда max-width на
   .cmp-info и центрирование.
   flex здесь НЕ задаём: у .cmp-modal__grid его тоже нет, а стоявший тут
   flex:1 1 auto был единственным отличием от рабочего образца. min-height:0
   нужен, чтобы overflow-y работал внутри flex-колонки панели. */
.cmp-modal__info { min-height:0; overflow-y:auto; padding:10px 16px 16px; }
.cmp-info { max-width:520px; margin:0 auto; }

/* [ПИКЕР-ХАРАКТЕРИСТИКИ] Пока открыта карточка, окно занимает весь экран, чтобы
   характеристики читались целиком без прокрутки. Ставится классом на #pickModal
   из showInfo и снимается в showList/closePick, поэтому список карточек и
   развилка выбора действия остаются нижней шторкой, как были.

   Специфичность (0,3,0) намеренно выше, чем у .cmp-modal__panel в ландшафтной
   ветке [ПИКЕР-ЛАНДШАФТ] (0,1,0), — иначе max-width:960px оттуда пересилил бы.
   Ширину самой .cmp-info при этом НЕ снимаем: строки «label … value» и на
   полном экране должны оставаться читаемой колонкой, а не растягиваться. */
.cmp-modal.is-info { align-items:stretch; justify-content:stretch; }
.cmp-modal.is-info .cmp-modal__panel {
  width:100%; max-width:none;
  height:100%; max-height:none;
  border-radius:0; border:none;
}
.cmp-info__back {
  display:inline-block; margin-bottom:10px; padding:4px 10px;
  background:transparent; border:1px solid rgba(167,139,255,.25); border-radius:99px;
  color:var(--muted,#9990bf); font-family:inherit; font-size:.72rem; font-weight:700; cursor:pointer;
}
.cmp-info__back:hover { border-color:var(--cmp-left,#a78bff); color:#f4f1ff; }
.cmp-info__main { display:flex; gap:12px; align-items:flex-start; }
.cmp-info__media { flex:0 0 104px; }
.cmp-info__media.is-zoomable { cursor:zoom-in; }
.cmp-info__media img {
  width:100%; aspect-ratio:3/2; object-fit:cover; display:block;
  border-radius:10px; border:1px solid rgba(167,139,255,.15);
}
.cmp-info__head { min-width:0; }
.cmp-info__brow {
  font-family:'JetBrains Mono', monospace;
  font-size:.6rem; font-weight:800; letter-spacing:.16em; text-transform:uppercase;
  color:#a78bff; margin-bottom:4px;
}
.cmp-info__title { font-size:.95rem; font-weight:800; line-height:1.2; }
.cmp-info__rows { margin-top:10px; }
.cmp-modal__info .modal-row {
  display:flex; justify-content:space-between; gap:12px;
  padding:6px 0; border-bottom:1px solid rgba(167,139,255,.1); font-size:.78rem;
}
.cmp-modal__info .modal-row:last-of-type { border-bottom:none; }
.cmp-modal__info .modal-row-label { color:rgba(244,241,255,.55); }
.cmp-modal__info .modal-row-value { color:#fff; font-weight:600; text-align:right; }
.cmp-modal__info .modal-row-value.price {
  color:#b4ff39; font-family:'JetBrains Mono', monospace; font-weight:800;
}
.cmp-info__actions { display:flex; gap:8px; flex-wrap:wrap; margin-top:12px; }
.cmp-info__btn {
  flex:1 1 auto; padding:8px 12px; border-radius:10px;
  background:transparent; border:1px solid rgba(167,139,255,.3);
  color:#c9b6ff; font-family:inherit; font-size:.78rem; font-weight:700;
  cursor:pointer; white-space:nowrap;
}
.cmp-info__btn--primary {
  background:linear-gradient(135deg,#8b5cff,#ff3ec9); border-color:transparent; color:#fff;
}
/* [СЕРДЦЕ] ЕДИНЫЙ стиль иконки избранного для всех трёх мест: полоса карточки
   в рядах (.cmp-chip__fav, стили самой карточки инлайном в compare.html),
   кнопка карточки характеристик (.cmp-info__btn--fav) и кластер инструментов
   зум-панели (.cmp-fs__tool). Три копии стиля здесь были бы той же ошибкой,
   что три копии логики избранного до user-lists.js.

   Состояние несёт САМА ФОРМА: контур — не в избранном, заливка — в избранном.
   Подложки/плашки вокруг нет сознательно: фон дублировал бы то, что уже сказано
   заливкой, и на трёх разных поверхностях выглядел бы по-разному.

   Контур светлый, а не чёрный: все три поверхности тёмные (полоса карточки
   rgba(7,5,13,.82), панель характеристик, фиолетовая кнопка кластера), на них
   чёрный контур не читался бы вовсе.

   Размер задаёт КОНТЕКСТ, а не этот класс: иконка живёт в кнопках трёх разных
   калибров. Правило для кластера записано как .cmp-fs__tool .cmp-heart, потому
   что его специфичность (0,2,0) обязана перебить существующее
   .cmp-fs__tool svg (0,1,1) — иначе размер и обводка пришли бы оттуда. */
/* Базовые .cmp-heart / .cmp-heart.is-on переехали в design-system.css: та же
   иконка теперь и на плитках каталога, а compare-widget.css там не подключён.
   Здесь остаются только КОНТЕКСТНЫЕ размеры — они про кнопки виджета. */
.cmp-fs__tool .cmp-heart { width:19px; height:19px; }
.cmp-info__btn--fav .cmp-heart { width:14px; height:14px; }

/* display:inline-flex + gap — иконка и подпись теперь ОТДЕЛЬНЫЕ узлы, раньше
   кнопка несла одну текстовую строку «♥ В избранном». */
.cmp-info__btn--fav { border-color:rgba(255,105,180,.35); background:rgba(255,105,180,.1); color:#ff9ec9;
  display:inline-flex; align-items:center; justify-content:center; gap:6px; }
.cmp-info__btn--fav.is-on { background:rgba(255,105,180,.22); color:#fff; }

/* Развилка действия. position:absolute считается от .cmp-modal (fixed, inset:0),
   потому что узел лежит внутри него — рядом с .cmp-modal__panel, а не в нём:
   панель с overflow:hidden обрезала бы оверлей по своей высоте. */
.cmp-modal__ask {
  position:absolute; inset:0; z-index:5;
  display:flex; align-items:center; justify-content:center; padding:16px;
  background:rgba(5,3,12,.6);
}
.cmp-ask__card {
  width:100%; max-width:320px;
  display:flex; flex-direction:column; gap:8px;
  padding:16px; border-radius:14px;
  background:linear-gradient(160deg,#150c2c,#0a0614);
  border:1px solid rgba(167,139,255,.25);
}
.cmp-ask__title {
  font-size:.85rem; font-weight:800; text-align:center;
  margin-bottom:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.cmp-ask__btn {
  display:flex; align-items:center; justify-content:center; gap:8px;
  padding:10px 14px; border-radius:10px;
  background:transparent; border:1px solid rgba(167,139,255,.3);
  color:#c9b6ff; font-family:inherit; font-size:.82rem; font-weight:700; cursor:pointer;
}
/* [ПИКЕР-ИЗБРАННОЕ] Сердечко на кнопке добавления: подсказка, что плёнка попадёт
   заодно и в избранное. Розовое — тот же цвет, что у кнопки избранного в
   карточке характеристик (.cmp-info__btn--fav), чтобы связь читалась. */
/* [СЕРДЦЕ] Пометка действия, а не переключатель: заливка ПОСТОЯННАЯ и берётся
   из currentColor кнопки, поэтому прежние цвета (#ff9ec9 и #ffd9ec на основной
   кнопке) сохраняются один-в-один — сменилась только форма, с глифа на вектор.
   Специфичность (0,2,0) перебивает базовый .cmp-heart (0,1,0). */
.cmp-ask__heart { color:#ff9ec9; font-size:1em; line-height:1; display:inline-flex; align-items:center; }
.cmp-ask__heart .cmp-heart { width:13px; height:13px; stroke:currentColor; fill:currentColor; }
.cmp-ask__btn--primary .cmp-ask__heart { color:#ffd9ec; }
.cmp-ask__btn--primary {
  background:linear-gradient(135deg,#8b5cff,#ff3ec9); border-color:transparent; color:#fff;
}

/* [ПИКЕР-ХАРАКТЕРИСТИКИ] Увеличенное фото. Отдельный лёгкий слой, а не
   зум-шторка #cmpFs: та несёт рельсы, сцену сравнения и нативный fullscreen —
   для одной картинки это несоразмерно. z-index выше развилки: фото открывают
   уже из карточки, то есть оно всегда верхний слой. */
.cmp-modal__zoom {
  position:absolute; inset:0; z-index:6;
  display:flex; align-items:center; justify-content:center; padding:12px;
  background:rgba(5,3,12,.92); cursor:zoom-out;
}
.cmp-zoom__img {
  max-width:100%; max-height:100%;
  width:auto; height:auto; object-fit:contain; display:block;
  border-radius:10px;
}
