/* ============================================================================
   ОБЩИЙ ПРОСМОТРЩИК СОДЕРЖИМОГО — ОБЛИК (2026-09-13)
   ============================================================================
   ⚠️⚠️ ОДИН ОБЛИК НА ВСЕ ПОВЕРХНОСТИ. До этого просмотрщиков было ТРИ, и они
   разъехались: подложка 0.85 против 0.92, крестик кегля 24 против 18, у одного
   счётчик есть, у другого нет, «скачать» то в потоке под фото, то прижат к
   низу. Замер и разбор — spec 2026-09-13-unified-viewer-design.md.

   ⚠️⚠️ СТИЛИ ЖИВУТ ЗДЕСЬ, А НЕ В `cssText` РАЗМЕТКИ. Прежний просмотрщик
   строился инлайном, и перебить его из таблицы без `!important` было
   невозможно — именно поэтому облик и нельзя было привести к общему.
   Инлайном остаётся ровно одно: `touch-action` на оверлее (браузер защёлкивает
   его в момент касания, из обработчика ставить поздно).

   ⚠️ ГЕЙТ ПО ОБОЛОЧКЕ (`body.v2-active`) — решение Вадима: новый облик только
   в v2, старой оболочке остаётся легаси-просмотрщик.                        */

.media-viewer-overlay {
    background: #000;
    padding: 0;
}

/* ── шапка ───────────────────────────────────────────────────────────────── */
/* ⚠️⚠️ ОБВЕС ПРИВЯЗАН К КАДРУ, А НЕ К ЭКРАНУ. Замер на 3840×2160 до правки:
   шапка, лента и полоса действий растягивались на все 3840 при кадре 1278 —
   на 32" имя файла улетало в левый край, счётчик в правый, а между ними метр
   пустоты. Ширину кадра меряет JS и кладёт в `--mv-scene`; пока её нет (первый
   кадр, нет содержимого) запасное значение — вся ширина, то есть прежнее
   поведение. Потолок нужен и сам по себе: на 21:9 кадр может быть шире, чем
   комфортно читать строку заголовка. */
.media-viewer-overlay {
    --mv-scene: 100%;
    /* ⚠️ 2400, А НЕ 1760: на 21:9 (3440×1440) кадр 16:9 упирается в высоту и
       даёт ширину 2156 — потолок 1760 СРЕЗАЛ бы его там, где ограничивать
       нечего. 2400 страхует только 4К+, где иначе строка заголовка
       растянулась бы на метр. Таблица вывода — в спеке. */
    --mv-scene-max: 2400px;
}
.mv-top, .mv-bottom {
    width: min(var(--mv-scene), var(--mv-scene-max), 100%);
    margin-inline: auto;
}
.mv-top {
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 64px;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 12px;
    z-index: 11;
    /* ⚠️ Градиент, а не сплошная полоса: сплошная отнимает у кадра 64px
       постоянно, а на 844px это 8 % экрана. Замер вариантов — в спеке. */
    background: linear-gradient(rgba(0, 0, 0, 0.6), transparent);
}
.mv-title {
    flex: 1;
    min-width: 0;
    font: 500 15px/1.2 var(--font-mono, ui-monospace, monospace);
    color: #fff;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── круглые кнопки: одна форма на все ───────────────────────────────────── */
/* ⚠️ РАЗМЕР КНОПКИ — ПАНДУС, А НЕ КОНСТАНТА. На 32"/3440 при dpr=1 сорок
   пикселей это ~7 мм — мелко для мыши на таком расстоянии; на 390px, наоборот,
   крупнее 36 занимает лишнее. `vmin` растёт и по ширине, и по высоте, поэтому
   ультраширокий 21:9 (высота всего 1440) не раздувает кнопки. */
.media-viewer-overlay { --mv-btn: clamp(34px, 1.4vmin + 30px, 48px); }
.mv-close, .mv-jump, .mv-forward, .mv-more {
    position: static;
    width: var(--mv-btn); height: var(--mv-btn);
    flex: none;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 0;
    transform: none;
}
.mv-close svg, .mv-jump svg, .mv-forward svg, .mv-more svg { width: 18px; height: 18px; }
.mv-close:hover, .mv-jump:hover, .mv-forward:hover, .mv-more:hover {
    background: rgba(255, 255, 255, 0.12);
}

.mv-counter {
    position: static;
    transform: none;
    flex: none;
    font: 13px/1 var(--font-mono, ui-monospace, monospace);
    color: rgba(255, 255, 255, 0.75);
    font-variant-numeric: tabular-nums;
}

/* ── низ: лента + действия ───────────────────────────────────────────────── */
.mv-bottom {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 10px 0 16px;
    z-index: 11;
    background: linear-gradient(transparent, rgba(0, 0, 0, 0.65));
}

/* ⚠️ ЛЕНТА ПОКАЗЫВАЕТСЯ, КОГДА ЭЛЕМЕНТОВ БОЛЬШЕ ОДНОГО (решение Вадима).
   Пустая полоса под одиночным снимком — отнятое место без смысла. */
.mv-strip {
    display: flex;
    gap: 6px;
    justify-content: center;
    padding: 0 12px;
    overflow-x: auto;
    scrollbar-width: none;
}
.mv-strip::-webkit-scrollbar { display: none; }
.mv-strip:empty { display: none; }
.mv-strip i {
    flex: none;
    width: 44px; height: 44px;
    border-radius: 8px;
    background-size: cover;
    background-position: center;
    opacity: .45;
    cursor: pointer;
    border: 2px solid transparent;
    transition: opacity .15s ease;
}
.mv-strip i.is-current { opacity: 1; border-color: #fff; }
/* ⚠️ Тайл несёт `<img crossorigin>`, а не CSS-фон: фон тянется запросом
   `no-cors`, ответ приходит непрозрачным и травит медиа-кэш service worker'а
   под тем же `mcid` — после чего основной кадр падает по CORS и остаётся
   чёрным. Замер поймал это на альбоме. */
.mv-strip i > img {
    width: 100%; height: 100%;
    object-fit: cover;
    border-radius: 6px;
    display: block;
}
.mv-strip i { position: relative; }
.mv-strip i.is-video::after {
    position: absolute;
    inset: 0;
    content: '';
    background: rgba(0, 0, 0, .35) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23fff'%3E%3Cpath d='M8 5v14l11-7z'/%3E%3C/svg%3E") center / 16px no-repeat;
    border-radius: 6px;
}

.mv-actions {
    position: static;
    transform: none;
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
    padding: 0 12px;
}
.mv-actions button {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 9px 15px;
    border-radius: 22px;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(255, 255, 255, 0.06);
    color: rgba(255, 255, 255, 0.92);
    font-size: 13px;
    cursor: pointer;
}
.mv-actions button:hover { background: rgba(255, 255, 255, 0.12); }
.mv-actions button.is-danger {
    background: rgba(220, 60, 60, 0.18);
    color: #ff9b9b;
    border-color: rgba(220, 60, 60, 0.3);
}

/* ── боковые стрелки: только там, где есть мышь ──────────────────────────── */
/* ⚠️ На телефоне листают жестом и лентой; стрелка 48×48 у края кадра там
   только мешает — замер показал её в 27px от кластера перемотки плеера. */
@media (hover: none) {
    .mv-prev, .mv-next { display: none !important; }
}
.mv-prev, .mv-next {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.14);
}

/* ── содержимое ──────────────────────────────────────────────────────────── */
.mv-content { padding: 64px 0 96px; box-sizing: border-box; }

/* ============================================================================
   ВИДЕО: ОБВЕСОМ ВЛАДЕЕТ ПЛЕЕР (Ф2, 2026-09-13)
   ============================================================================
   ⚠️⚠️ У ПЛЕЕРА УЖЕ ЕСТЬ ВСЁ: своя шапка, кластер, полоса управления и своя
   строка действий с четырьмя кнопками. Рисуя поверх свой обвес, просмотрщик
   давал ДУБЛИ — замер на 390×844 показал две шапки одна под другой и стрелку
   просмотрщика в 27px от кластера перемотки.

   Поэтому при видео свой обвес просмотрщик прячет, а счётчик и ленту
   ПЕРЕНОСИТ в обвес плеера — там они гаснут вместе с ним.                   */
.media-viewer-overlay.is-video > .mv-top,
.media-viewer-overlay.is-video > .mv-bottom { display: none; }
.media-viewer-overlay.is-video > .mv-content { padding: 0; }

/* счётчик, переехавший в шапку плеера */
.vp-top-bar > .mv-counter { margin-left: auto; padding-right: 4px; }

/* лента, переехавшая в окно плеера — над его полосой управления */
.video-player > .mv-strip {
    position: absolute;
    left: 0; right: 0;
    z-index: 6;
    transition: opacity .18s ease;
}
/* ⚠️ Гаснет вместе с остальным обвесом плеера: иначе на погашенном кадре
   висит одинокая полоса миниатюр. */
.vp-viewport.is-idle ~ .mv-strip { opacity: 0; pointer-events: none; }

/* ============================================================================
   ОБЩИЙ ВИЗУАЛЬНЫЙ ЯЗЫК С ЭКРАНОМ ОТПРАВКИ (Ф3, 2026-09-13)
   ============================================================================
   ⚠️⚠️ СВОДИТСЯ ЯЗЫК, А НЕ РАСКЛАДКА. У экрана отправки (`v2/edit/viewer.js`)
   своя трёхрядная сетка, выстраданная замерами: там внутрь ПЕРЕЕЗЖАЕТ настоящий
   узел `.chat-input`, а содержимое — локальный черновик, а не серверный
   элемент. Перекроить его под градиентный обвес значило бы сломать работающую
   поверхность ради единообразия, которого человек всё равно не увидит.
   Одинаковым человек видит ФОРМУ: круглые кнопки, ту же типографику имени и
   счётчика, те же плитки.

   ⚠️ ПРАВИЛА ОДНИ НА ДВОИХ — сгруппированными селекторами, а не копией. Копия
   разошлась бы на первой правке; этот проект на таких парах горел трижды.

   ⚠️ ЗАГРУЖАЕТСЯ ЭТА ТАБЛИЦА РАНЬШЕ `v2.css`, поэтому расходящиеся объявления
   из `v2/css/viewer.css` УДАЛЕНЫ, а не перекрыты: перекрытие держалось бы на
   порядке тегов и молча сломалось бы при его смене.                         */
.mv-close, .mv-jump, .mv-forward, .mv-more,
.v2-vw-x, .v2-vw-ico, .v2-ed-ico {
    width: 40px;
    height: 40px;
    flex: none;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(255, 255, 255, 0.06);
    color: var(--tx, #fff);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    cursor: pointer;
}
.mv-close:hover, .mv-jump:hover, .mv-forward:hover, .mv-more:hover,
.v2-vw-x:hover, .v2-vw-ico:hover, .v2-ed-ico:hover { background: rgba(255, 255, 255, 0.12); }


}

.mv-title, .v2-vw-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font: 500 15px/1.2 var(--font-mono, ui-monospace, monospace);
}

.mv-counter, .v2-vw-count {
    flex: 0 0 auto;
    font: 13px/1 var(--font-mono, ui-monospace, monospace);
    font-variant-numeric: tabular-nums;
}

/* Плитка ленты — один размер на обе поверхности. */
.mv-strip i, .v2-vw-th {
    width: 48px;
    height: 48px;
    border-radius: 8px;
    border: 2px solid transparent;
    overflow: hidden;
}

/* ⚠️⚠️ НА УЗКОМ ЭКРАНЕ КНОПКИ УМЕНЬШАЮТСЯ У ВСЕХ ТРЁХ СРАЗУ, И ЭТО ИНВАРИАНТ,
   А НЕ КОСМЕТИКА. Высота шапки просмотра обязана совпадать с высотой шапки
   редактора: разойдутся — картинка ПРЫГНЕТ при переходе между просмотром и
   правкой (записано в `v2/css/edit.css` с 2026-09-05). Пока размеры жили в
   двух файлах, их держали в согласии руками; теперь правило одно. */
/* ── Переходы кадра живут В ТАБЛИЦЕ, а не инлайном ─────────────────────────
 *
 * ⚠️⚠️ ИНЛАЙНОВЫЙ ПЕРЕХОД СТИРАЛСЯ НАСОВСЕМ. Разметка просмотрщика строится
 * строкой и ставит `transition: opacity 200ms` прямо в `style` картинки, а
 * `_применитьКартинку` делает `img.style.transition = ''` — то есть снимает
 * его. Правила `.mv-image` в таблицах не было НИ ОДНОГО, поэтому после первого
 * же обновления переход исчезал навсегда.
 *
 * Замер в живом просмотрщике (десктоп, один флип): `transitionDuration` даёт
 * `0s` и до перехода, и после. Значит показ картинки и снятие заглушки — не
 * плавные, а ЖЁСТКИЕ СРЕЗЫ. Это и описывалось словом «мелькает».
 *
 * ⚠️ Мгновенный показ прогретого снимка по-прежнему возможен: там код ставит
 * `transition: none` ИНЛАЙНОМ, а инлайн сильнее таблицы. То есть быстрый путь
 * остаётся мгновенным, а медленный перестаёт моргать.
 */
.media-viewer-overlay .mv-image { transition: opacity 200ms ease; }
.media-viewer-overlay .mv-blur-bg { transition: opacity 200ms ease; }

/* ⚠️ Кольцо загрузки НЕ КРУТИЛОСЬ. Разметка просит `animation: lv-spin`, а
 * сами кадры объявлены ВНУТРИ функции в `pages/library-page.js` — то есть в
 * переписке их нет, и человек видел застывший артефакт вместо признака
 * загрузки. Замер: `getAnimations()` на `.mv-spinner` возвращает 0.
 */
@keyframes lv-spin { to { transform: rotate(360deg); } }


@media (max-width: 620px) {
    .mv-close, .mv-jump, .mv-forward, .mv-more,
    .v2-vw-x, .v2-vw-ico, .v2-ed-ico { width: 34px; height: 34px; }
    .mv-title, .v2-vw-name { font-size: 14px; }
}
/* ⚠️⚠️ ЭТА СКОБКА БЫЛА ПОТЕРЯНА, И ЭТО СТОИЛО ОТЛАДКИ. Блок
   `@media (max-width: 620px)` оставался открытым, и ВСЁ дописанное ниже
   попадало внутрь него — то есть на десктопе не применялось вовсе. Подсчёт
   скобок скриптом соврал (сошёлся), правду сказал браузер: `cssRules` показал
   у правил путь `@(max-width: 620px)`. Проверять принадлежность правила надо
   через `document.styleSheets`, а не глазами и не счётчиком. */


/* ══ Единый облик управления у видео (2026-09-14) ═════════════════════════
   Спека: docs/superpowers/specs/2026-09-14-viewer-player-chrome-design.md */

/* ⚠️ Пустой ярус действий убираем: кнопки уехали в полосу управления, а сам
   контейнер продолжал отнимать 65px под кадром. Скрываем ПО ПУСТОТЕ, а не по
   признаку видео: если перенос не состоялся (нет полосы управления), ярус
   обязан остаться — иначе действия пропадут совсем. */
.media-viewer-overlay .vp-now:not(:has(.vp-action)) { display: none; }

.vp-controls-row .vp-actions-slot {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

/* ⚠️⚠️ ОДИН РАЗМЕР НА ВСЕ КНОПКИ ОБОИХ ЯРУСОВ. Замер до правки на 957×862:
   44×44 (play), 28×28 (безымянная), 36×36 (шестерёнка/PiP/фуллскрин),
   40×40 (♡ ➤ ⤓ ⋮) — четыре размера в одном экране. */
.media-viewer-overlay .vp-controls-row .vp-ctrl,
.media-viewer-overlay .vp-controls-row button,
.media-viewer-overlay .vp-actions-slot .vp-action {
    width: var(--mv-btn);
    height: var(--mv-btn);
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}
.media-viewer-overlay .vp-controls-row .vp-ctrl svg,
.media-viewer-overlay .vp-controls-row button svg,
.media-viewer-overlay .vp-actions-slot .vp-action svg {
    width: 55%;
    height: 55%;
}

/* ⚠️ НА ТЕЛЕФОНЕ РЯД ОБЯЗАН ОСТАТЬСЯ ОДНОЙ СТРОКОЙ. Замер: 3 кнопки плеера +
   4 действия при 36px давали ДВЕ строки (ряды на y 757 и 793). Поджимаем
   кнопку и зазоры — при 34px арифметика сходится: 7×34 + время + зазоры
   укладываются в 390. */
@media (max-width: 640px) {
    .media-viewer-overlay { --mv-btn: 34px; }
    .media-viewer-overlay .vp-controls-row { gap: 2px; flex-wrap: nowrap; }
    .media-viewer-overlay .vp-actions-slot { gap: 2px; }
    /* Разделитель « / » в таймкоде — первое, чем можно пожертвовать. */
    .media-viewer-overlay .vp-controls-row .vp-time-sep { margin: 0 1px; }
}


/* ── Пустая шапка плеера не должна отъедать верх кадра ────────────────────
   ⚠️⚠️ ЗАМЕР: `.mv-top` 0..64, а сразу под ней `.vp-top-bar` 65..135 — ДВЕ
   шапки друг под другом, причём вторая ПУСТАЯ: `vp-back` невидима (за «назад»
   отвечает наша стрелка), `vp-top-title` без текста (имя файла показывает
   `.mv-title`). Семьдесят пикселей градиента поверх картинки ни за чем —
   именно это читается как «видео вставили в старый кусок дизайна».

   ⚠️ СКРЫВАЕМ ПО ПУСТОТЕ, А НЕ ПО ШИРИНЕ ЭКРАНА: на телефоне в эту шапку
   ПЕРЕЕЗЖАЕТ счётчик (Ф2, 2026-09-13), и там она несёт содержимое. Правило
   с `:has` само различает эти два случая — гейт по ширине пришлось бы держать
   в согласии с логикой переезда руками. */
/* ⚠️ ПРИЗНАК — НАЛИЧИЕ СЧЁТЧИКА, А НЕ `[hidden]` У КНОПКИ «НАЗАД». Первая
   редакция смотрела на `.vp-back:not([hidden])`, а он скрыт нулевой шириной, а
   не атрибутом — правило не срабатывало, шапка оставалась (замер:
   `шапкаПлеераВидна: true`). Счётчик — единственное, что туда реально
   переезжает, и его наличие ровно и означает «шапка занята». */
.media-viewer-overlay .vp-top-bar:not(:has(.mv-counter)) { display: none; }

/* ── Боковые стрелки живут у СЦЕНЫ, а не у краёв монитора ─────────────────
   ⚠️ Замер на 1512: стрелка на x=16, кадр начинается с x=132 — то есть орган
   листания висел в чёрном поле отдельно от картинки. На 3440 разрыв был бы
   в полэкрана. Привязываем к ширине сцены тем же способом, что шапку и ленту. */
/* ⚠️ БАЗОВЫЕ ПРАВИЛА СТРЕЛОК ПЕРЕЕХАЛИ СЮДА ИЗ ИНЛАЙНА. Пока они стояли в
   разметке (`style="position:absolute;left:16px;…"`), привязать их к сцене
   было нечем: инлайн из таблицы без `!important` не перебить — та же грабля,
   что с обликом трёх просмотрщиков (Ф1) и потолком обёртки плеера. */
.media-viewer-overlay .mv-prev,
.media-viewer-overlay .mv-next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px; height: 48px;
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    z-index: 10;
}
@media (hover: hover) {
    .media-viewer-overlay .mv-prev,
    .media-viewer-overlay .mv-next {
        /* ⚠️ ЗНАК: стрелка стоит СНАРУЖИ кадра, а не поверх него. Формула с
           `+ 12px` заводила её ВНУТРЬ на 60px (замер: зазор −60) — то есть
           орган листания накрывал картинку. Отступ считается от края сцены
           влево на ширину кнопки (48) плюс зазор (12). */
        --mv-край: max(12px, calc((100vw - min(var(--mv-scene), var(--mv-scene-max), 100vw)) / 2 - 60px));
    }
    .media-viewer-overlay .mv-prev { left: var(--mv-край); }
    .media-viewer-overlay .mv-next { right: var(--mv-край); }
}
