/* site-media.css — медиа-компоненты публичной части, перенесены из style.css 1:1 (редизайн 20.10.2026).
   Завязаны на main.js (галерея, просмотрщик, спойлеры, эмодзи). Цвета — через алиасы в site.css :root. */

/* ---- style.css:509-564 ---- */
/* Мини-лента медиа — уменьшенная копия .news-gallery (страница поста), НО намеренно БЕЗ кастомного
   mouse-drag JS (см. скепsис-ревью 2026-08-10): обычный overflow-x:auto + scroll-snap уже даёт
   плавную прокрутку мышью/тачем/колесом без риска конфликта клика с переходом на пост и без
   размножения сложного отлаженного-с-трудом JS-компонента на сотни карточек одновременно. */
.news-mini-strip {
  display: flex;
  gap: 4px;
  margin-top: 8px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  padding-bottom: 2px;
}
.news-mini-strip::-webkit-scrollbar { height: 4px; }
.news-mini-strip::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
.news-mini-tile {
  position: relative;
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: 4px;
  overflow: hidden;
  border: none;
  padding: 0;
  cursor: pointer;
  background: var(--bg-panel-2);
  scroll-snap-align: start;
}
.news-mini-tile img,
.news-mini-tile video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
/* Аудио — своей превьюшки-картинки в принципе нет, заглушка цветным квадратом с иконкой по центру,
   а не пустой тайл или бейдж поверх ничего (реальная находка скепsис-ревью 2026-08-10). */
.news-mini-tile--audio { display: flex; align-items: center; justify-content: center; }
.news-mini-badge {
  position: absolute;
  right: 3px;
  bottom: 3px;
  font-size: 11px;
  line-height: 1;
  background: rgba(0,0,0,.65);
  border-radius: 50%;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.news-mini-badge--standalone { position: static; font-size: 20px; background: none; width: auto; height: auto; }
.news-mini-badge .ui-icon { width: 9px; height: 9px; color: #fff; }
.news-mini-badge--standalone .ui-icon { width: 22px; height: 22px; color: var(--gray); }
.news-mini-tile:hover { outline: 1px solid var(--red); outline-offset: -1px; }

/* ---- style.css:1751-2285 ---- */
/* ---------- Галерея новости: миниатюры + лайтбокс по клику ---------- */
.news-gallery-wrap {
  position: relative;
  /* margin уменьшен с 20px — .news-gallery теперь сама добавляет 30px вертикального padding
     (см. комментарий там, про coverflow-scale и обрезку полей), 20+30 давало бы уже избыточный
     отступ от заголовка/даты сверху. */
  margin: 8px 0;
}
/* Фейд по краям — перенесено 1:1 с ШРС Сервис (страница "Контакты" → "Наш интерьер и
   оборудование", .coverflow-track) по прямой повторной просьбе пользователя. Прошлые три версии
   (см. историю правок этого файла в git) городили дуговой фейд через отдельную нескроллящуюся
   обёртку с PNG/radial-градиентами — и по пути ловили то провал альфы до нуля в середине, то
   "линия вместо дуги" от суммирования градиентов, то flex-gap между карточками, попадающий в
   зону фейда. ШРС решает всё это одним mask-image ПРЯМО на самом скролл-контейнере: mask-image,
   в отличие от background-image, не "уезжает" вместе со скроллящимся содержимым (это свойство
   самого элемента, не его фона) — отдельная нескроллящаяся обёртка ради фейда была не нужна
   вовсе. Простой 2-стоповый linear-градиент через mask не даёт ни одного из трёх найденных багов. */
.news-gallery {
  -webkit-mask-image: linear-gradient(to right, transparent 0%, black 14%, black 86%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0%, black 14%, black 86%, transparent 100%);
}
/* Если реально скроллить нечего (1-2 фото, переполнение <=60px) — маску снимаем целиком:
   обрезанные по краям края единственного/двух статичных фото выглядели бы как недоделка, а не
   приём (дизайн-спека 2026-08-09, раздел "Если фото 1-2"). Центрируем содержимое в этом случае. */
.news-gallery-wrap.no-scroll .news-gallery {
  -webkit-mask-image: none;
  mask-image: none;
  justify-content: center;
}
.news-gallery {
  display: flex;
  /* Реальная жалоба пользователя 2026-08-09: неровные "рваные" края документов в ленте — разная
     высота карточек торчала вразнобой. Причина: display:flex без явного align-items растягивал
     (дефолт stretch) каждую карточку до высоты САМОЙ высокой в ряду, а object-fit:contain внутри
     центрировал фото в этой чужой, стретченной высоте — у альбомных сканов оставалось много
     "воздуха" сверху/снизу разного размера. flex-start — каждая карточка своей естественной
     высоты (тот же принцип, что и в остальных карточных сетках сайта, см. ВСЕ БАГИ/памятка
     "Grid-карточки: обжимать, не центрировать"). */
  align-items: flex-start;
  /* Зазор — 10px → 22px → 14px → 10px (эта правка). Раньше "расстояние" дополнительно стягивалось
     отрицательным margin в JS (updateCoverflow), но это оказалось источником реальных дёрганий —
     margin меняет раскладку, а .news-gallery одновременно живёт под scroll-snap-type:x proximity,
     JS-правка margin на каждый 'scroll' и нативное доседание snap воевали друг с другом (см.
     подробный разбор в main.js). Margin-трюк убран целиком, зазор — статичное CSS-число, никогда
     не меняется на лету, ни с чем не конфликтует. 10px — исходное дораскруточное значение,
     достаточно тесно, разворот (rotateY, см. main.js) сам по себе визуально сужает угловые
     карточки перспективой, переплетение возле центра не возвращается (там разворот минимальный). */
  gap: 10px;
  overflow-x: auto;
  overflow-y: hidden;
  margin: 0;
  /* Левый/правый padding — ДВЕ разные задачи сразу, оба решает одно значение:
     1) должен быть НЕ МЕНЬШЕ ширины фейда (70px, .news-gallery-wrap::before/after) — иначе
        крайний элемент никогда не выйдет из-под градиента полностью (баг уже ловился на карусели
        "Нам доверяют", см. ВСЕ БАГИ 2026-08-08);
     2) реальная просьба пользователя 2026-08-09 "крайние слева/справа фото должны оказаться по
        центру" — scroll-snap-align:center (ниже) физически не может докрутить ПЕРВЫЙ/ПОСЛЕДНИЙ
        элемент до центра видимой области, если слева/справа не хватает пустого места для скролла:
        нужно РОВНО (ширина_контейнера − ширина_фото)/2. calc((100% - 220px)/2) считает это
        динамически под РЕАЛЬНУЮ ширину контейнера (у него нет вырезанного паддинга, поэтому 100%
        внутри calc — это уже полная ширина .news-gallery). max() подстраховывает — если контейнер
        совсем узкий (мобильный) и вычисленное значение вышло бы МЕНЬШЕ ширины фейда, берём фейд —
        крайний элемент никогда не должен прятаться под градиентом.
     Верхний/нижний padding подняты с 2px/6px до 30px — реальная находка пользователя по скринам
     5/6: coverflow при hover может увеличить карточку до scale(1.16) (см. updateCoverflow() в
     main.js), у 320px-высокой карточки это +51px по высоте, ±25.6px сверху/снизу от исходных
     границ — при overflow-y:hidden и старом крошечном паддинге верх/низ карточки попросту
     обрезались. 30px с запасом перекрывает это (25.6px + 4px на подъём translateY при hover). */
  padding: 30px max(70px, calc((100% - 220px) / 2));
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  /* Скрытие НАТИВНОГО скроллбара браузера — реальная жалоба пользователя 2026-08-08: "колхозный
     скролл", виден стандартный серый браузерный скроллбар со стрелками, без него уже выглядит
     на порядок аккуратнее. Тот же приём, что и в .trust-viewport (карусель "Нам доверяют" на
     главной) — переиспользован, а не изобретён заново. */
  scrollbar-width: none;
}
.news-gallery::-webkit-scrollbar { display: none; }
/* "Схватить и потащить" курсор — только на устройствах с настоящей мышью (pointer:fine),
   на touch он бессмысленен и там всё равно нативный scroll-snap пальцем. */
@media (pointer: fine) {
  .news-gallery { cursor: grab; }
  .news-gallery.is-dragging { cursor: grabbing; scroll-snap-type: none; }
}
/* КРИТИЧНО (подтверждено живым тестом 2026-08-09, не только диагнозом): scroll-snap-type активно
   конфликтует с покадровой прямой записью scrollLeft из JS — браузер синхронно "доворачивает"
   позицию к ближайшей snap-точке при каждой записи, из-за чего анимация автопрокрутки вообще не
   двигалась (проверено: 60 кадров подряд scrollLeft+=1 с snap включённым — итог 0px движения;
   тот же тест со scroll-snap-type:none — движение проходит и держится). Пока автопрокрутку ведёт
   JS (класс ниже), snap выключаем; как только ведёт пользователь (ручной скролл/drag/touch),
   класс снимается и нативный scroll-snap возвращается — там он не мешает, а помогает. */
.news-gallery.is-auto-scrolling {
  scroll-snap-type: none;
}
/* Тот же класс конфликта snap-против-JS-записи, что и у автопрокрутки выше — только для колеса
   мыши (реальный баг, найден пользователем 2026-08-09: "нету переключения СКМ" — на самом деле
   колесо честно двигало scrollLeft, но snap тут же откатывал позицию обратно). Отдельный класс
   от is-dragging — тот ещё меняет курсор на grabbing, колесо не должно выглядеть как ручной драг. */
.news-gallery.is-wheel-scrolling {
  scroll-snap-type: none;
}
.news-gallery-item {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: 220px;
  /* center, не start — реальная просьба пользователя 2026-08-09: "крайние слева и справа должна
     оказаться по центру тоже при прокрутке". С align:center браузер сам центрирует ЛЮБОЙ снапнутый
     элемент, включая первый/последний, в пределах доступного скролла (padding по бокам, см. выше,
     уже даёт достаточно места для этого). */
  scroll-snap-align: center;
  border-radius: var(--radius);
  /* overflow:hidden УБРАН (было тут) — реальная просьба пользователя "3D-утолщение, как будто
     телевизор": боковые "рёбра" (::before/::after ниже) физически ТОРЧАТ за пределы плоскости
     карточки в 3D (это и создаёт иллюзию толщины корпуса) — overflow:hidden обрубал бы их под
     корень. Скруглённые углы карточки без него держит border-radius:inherit прямо на <img>/
     <video> (см. правило ниже) — оно клипует их СОБСТВЕННЫЙ прямоугольник тем же радиусом, тот
     же визуальный результат, что раньше давало overflow родителя. */
  background: var(--bg-panel);
  border: 1px solid var(--border);
  max-height: 320px;
  /* preserve-3d — ::before/::after (боковые рёбра "телевизора") должны разворачиваться ВМЕСТЕ
     с самой карточкой в едином 3D-пространстве, а не расплющиваться в её плоскость. */
  transform-style: preserve-3d;
  /* transform/opacity ЗДЕСЬ — на самой карточке (рамка+фото), не на <img> внутри неё — их
     считает и пишет updateCoverflow() в main.js (scale по расстоянию от центра галереи + "прыжок"
     на hover). Раньше scale стоял на <img>, а рамка (border/фон/border-radius, вот этот самый
     элемент) оставалась неподвижной — растущее фото обрезалось краем рамки, сжимающееся оставляло
     пустой зазор внутри нетронутой рамки: реальная жалоба пользователя "рамка и фотка — разница,
     должны вместе масштабироваться". transform-origin:center — масштаб от центра карточки, не от
     угла. CSS-переход :hover С TRANSFORM убран отсюда намеренно — JS каждый кадр перезаписывает
     inline style.transform, он всегда бьёт это CSS-правило по специфичности, эффект бы просто
     никогда не показался; сам "прыжок" на hover теперь считается в JS вместе с coverflow-масштабом. */
  transform-origin: center center;
  will-change: transform, opacity;
  transition: transform .2s ease-out, box-shadow .2s ease-out, border-color .2s ease-out;
}
/* "Утолщение, как телевизор" — прямая просьба пользователя. Классический CSS-приём для боковой
   грани 3D-коробки: тонкая полоса, прижатая своим ВНУТРЕННИМ краем (transform-origin) точно к
   левому/правому ребру фронтальной грани (карточки) и развёрнутая на 90° — при preserve-3d на
   родителе (выше) она уходит вглубь ровно там, где у настоящего корпуса был бы боковой торец, и
   поворачивается вместе с самой карточкой при coverflow-развороте (main.js), а не отдельно от
   неё. Тонировка темнее лицевой стороны (имитация тени в грани, не плоский повтор фото) —
   thickness = 10px, тот же порядок величины, что рамка/бордер, не "кирпич". */
.news-gallery-item::before,
.news-gallery-item::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 10px;
  background: linear-gradient(to bottom, #060606, #000 45%, #060606);
  pointer-events: none;
}
.news-gallery-item::before {
  right: 100%;
  transform-origin: right center;
  transform: rotateY(-90deg);
}
.news-gallery-item::after {
  left: 100%;
  transform-origin: left center;
  transform: rotateY(90deg);
}
.news-gallery-item:hover {
  z-index: 2;
  /* Просьба пользователя 2026-08-09: одного подъёма мало, добавить лёгкую подсветку — тонкая
     акцентная рамка + слабое красное свечение поверх обычной чёрной тени, чтобы навéдённый
     элемент (фото/видео/документ) читался явно, но без "магазинного" неонового эффекта. Сам
     подъём/увеличение ("прыжок") — transform, теперь считается в JS (см. комментарий выше). */
  border-color: var(--red);
  box-shadow: 0 10px 22px rgba(0,0,0,.35), 0 0 0 1px rgba(216,31,38,.4), 0 0 14px 1px rgba(216,31,38,.25);
}
/* Фото-элементы — <button data-doc-zoom> (просмотрщик с зумом), не <a> — сброс дефолтного вида
   кнопки браузера, чтобы выглядело идентично видео-элементам (<a>) рядом в той же сетке.
   ВАЖНО: ширину здесь больше НЕ переопределяем — "button.news-gallery-item{width:100%}" раньше
   перебивал ".news-gallery-item{width:220px}" по специфичности (0,1,1 против 0,1,0) независимо от
   порядка правил в файле, из-за чего каждое фото раздувалось почти на всю ширину галереи вместо
   220px-миниатюры (диагноз скептика 2026-08-09, style.css:1553 в старой нумерации). */
button.news-gallery-item {
  padding: 0;
  font: inherit;
  cursor: pointer;
}
/* Раньше жёсткий aspect-ratio:1/1 + object-fit:cover обрезал любое фото под квадрат — реальная
   жалоба пользователя 2026-08-02: снимок ~1000×700 (альбомный) показывался обрезанным квадратом.
   Теперь рамка подстраивается под реальные пропорции конкретного фото (object-fit:contain —
   ничего не обрезается), max-height ограничивает габарит примерно как раньше у одного элемента
   сетки, чтобы вертикальное фото не «распирало» сетку по высоте. Клик всё так же открывает
   лайтбокс (data-lightbox) с полным приближением. */
.news-gallery-item img,
.news-gallery-item video {
  width: 100%;
  height: auto;
  max-height: 320px;
  object-fit: contain;
  display: block;
  /* Держит скруглённые углы теперь, когда overflow:hidden убран у родителя (см. комментарий там,
     "3D-утолщение как телевизор") — border-radius на самом <img>/<video> клипует его СОБСТВЕННОЕ
     содержимое тем же радиусом, что и у карточки. */
  border-radius: inherit;
}
/* Аудио — не картинка, полосе плеера нужна ширина побольше стандартной 220px карточки, но всё
   равно flex-элемент внутри той же горизонтальной прокрутки, не на всю ширину секции. */
.news-gallery-item--audio {
  width: 320px;
  max-height: none;
  display: flex;
  align-items: center;
  padding: 0 10px;
}
.news-gallery-play {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 40px; height: 40px;
  border-radius: 50%;
  background: rgba(0,0,0,.6);
  color: var(--white);
  display: flex; align-items: center; justify-content: center;
  font-size: .9rem;
  pointer-events: none;
}
/* Точки-пагинация под галереей — вставляются JS-ом, только от 3 фото и выше (дизайн-спека
   2026-08-09). Оформление подчёркнуто спокойное — "подпись к слайду в презентации", не игровой
   индикатор: маленькие кружки, активный просто заливается акцентным цветом фонда. */
.news-gallery-dots {
  display: flex;
  justify-content: center;
  gap: 12px;
  margin-top: 4px;
}
.news-gallery-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--white);
  opacity: .3;
  transition: opacity .25s, background .25s, transform .15s;
  /* Точки теперь кликабельны (переключают на конкретное фото, см. main.js buildDots()) — реальная
     просьба пользователя 2026-08-09. */
  cursor: pointer;
}
.news-gallery-dot:hover { opacity: .6; transform: scale(1.3); }
.news-gallery-dot:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.news-gallery-dot.is-active {
  opacity: 1;
  background: var(--red);
}

/* height:100dvh — тот же фикс мобильного тулбара, что и у .doc-zoom-overlay ниже (см. подробный
   комментарий там); здесь же max-height у img/video переведён на dvh по той же причине. */
.lightbox-overlay {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100dvh;
  background: rgba(0,0,0,.9);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 30px;
  padding-top: max(30px, env(safe-area-inset-top));
  cursor: zoom-out;
}
.lightbox-overlay[hidden] { display: none; }
.lightbox-overlay img,
.lightbox-overlay video {
  display: block;
  max-width: 90vw;
  max-height: 90vh;
  max-height: 90dvh;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius);
  cursor: default;
}
.lightbox-close {
  position: absolute;
  top: max(16px, env(safe-area-inset-top));
  right: 20px;
  background: none;
  border: none;
  color: var(--white);
  font-size: 2rem;
  line-height: 0;
  padding: 4px;
  cursor: pointer;
}
.lightbox-close:hover { color: var(--red); }

/* Миниатюра свидетельства о регистрации на странице «Документы» — клик открывает
   zoom-просмотрщик (см. .doc-zoom-* ниже), не просто увеличенную картинку. */
.doc-cert-thumb-btn {
  display: block;
  width: 100%;
  max-width: 220px;
  margin: 10px auto 0;
  padding: 0;
  border: none;
  background: none;
  cursor: zoom-in;
  position: relative;
  transition: transform .15s ease, filter .15s ease;
}
.doc-cert-thumb-btn:hover,
.doc-cert-thumb-btn:focus-visible {
  transform: translateY(-2px);
  filter: brightness(1.08);
}
.doc-cert-thumb {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10px;
}
.doc-cert-thumb-hint {
  display: block;
  padding: 6px 4px 0;
  font-size: .78rem;
  color: var(--gray);
  text-align: center;
}

/* Zoom-просмотрщик документа — отдельный от обычного лайтбокса галереи, т.к. тут
   нужен настоящий масштаб (пинч на телефоне, бегунок на ПК), а не просто img в 90vh.
   РЕАЛЬНЫЙ БАГ, найден пользователем на Яндекс.Браузере для Android (скрины 9-10): при появлении
   нижней панели браузера (адресная строка/кнопки — показывается заново при скролле, в отличие от
   Chrome, где она надёжнее прячется) `position:fixed; inset:0` считает высоту от "большого" вьюпорта
   (как будто панели нет), хотя реально видимая область меньше — крестик закрытия у верхнего края
   оверлея визуально уезжает за реальный видимый верх экрана, становится неtogглядным/некликабельным.
   Фикс — `height:100dvh` (динамическая единица, учитывает ТЕКУЩЕЕ состояние панели браузера) поверх
   запасного 100vh для старых браузеров без её поддержки. Это НЕ Яндекс-специфичный фикс и не требует
   определения браузера — `dvh` устраняет весь класс проблемы "мобильный тулбар меняет высоту
   вьюпорта" универсально, в любом браузере, включая Chrome (где раньше просто не проявлялось так
   заметно, но сама категория бага та же). */
.doc-zoom-overlay {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100dvh;
  z-index: 1001;
  background: rgba(0,0,0,.94);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  padding-top: max(20px, env(safe-area-inset-top));
  gap: 14px;
}
.doc-zoom-overlay[hidden] { display: none; }
.doc-zoom-stage-wrap {
  position: relative;
  width: 100%;
  max-width: 900px;
  height: 68vh;
  height: 68dvh;
}
.doc-zoom-stage {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  touch-action: none;
  border-radius: var(--radius);
  background: rgba(255,255,255,.02);
}
.doc-zoom-stage img {
  position: absolute;
  top: 50%; left: 50%;
  max-width: 100%;
  max-height: 100%;
  width: auto; height: auto;
  transform: translate(-50%, -50%);
  user-select: none;
  -webkit-user-drag: none;
}
.doc-zoom-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 420px;
}
.doc-zoom-icon { flex: 0 0 auto; line-height: 1; }
.doc-zoom-icon-sm { font-size: .85rem; opacity: .6; }
.doc-zoom-icon-lg { font-size: 1.5rem; opacity: .9; }
.doc-zoom-slider {
  flex: 1 1 auto;
  accent-color: var(--red);
  cursor: pointer;
}
.doc-zoom-hint {
  color: var(--gray);
  font-size: .8rem;
  text-align: center;
  max-width: 420px;
  margin: 0;
}
.doc-zoom-hint[hidden], .doc-zoom-controls[hidden] { display: none; }

/* Навигация между медиа одного поста (открыто из мини-ленты карточки, main.js openMediaViewer()) —
   вплотную к краю САМОГО ФОТО (кнопки — дети .doc-zoom-stage, не .doc-zoom-overlay, см. layout.php
   за причиной), счётчик "N / M" сверху оверлея. Скрыты по умолчанию, показываются только когда
   у поста больше одного медиа. "Дышащая" белая подсветка — тот же ритм/тайминг, что у пульса
   "МАЯК" на странице входа в админку (.mayak-pulse/@keyframes mayak-heartbeat), только белым
   цветом и через box-shadow (у кнопки есть закрашенный фон-круг, а не только текст) — прямая
   просьба пользователя "дышащий... как при входе в админку, оттуда прям выдерни". */
.doc-zoom-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1002;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.25);
  background: rgba(20,20,20,.55);
  color: var(--white);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.doc-zoom-nav[hidden] { display: none; }
.doc-zoom-nav:hover { border-color: var(--white); }
.doc-zoom-prev { left: 10px; }
.doc-zoom-next { right: 10px; }

/* 🔴 РЕАЛЬНЫЙ БАГ, найден владельцем по скрину 20.09.2026 («градиент НЕ ПОВЕРХ, а внутри какой-то
   рамки»): «дыхание» стрелок раньше делалось анимированным box-shadow (0 0 42px). На машине без
   аппаратного ускорения Chrome рисует страницу процессором и растрирует анимированную тень
   отдельным ПРЯМОУГОЛЬНЫМ слоем — вокруг каждой круглой кнопки появлялся заметный тёмный квадрат
   ~120×120, внутри которого сидело свечение, а текст страницы под ним пропадал. Воспроизведено и
   снято вживую на fond-mayak.ru до/после (см. память reference_chrome_no_gpu_diagnosis).
   Лечение: то же свечение, но обычным фоном — круглый ::after с radial-gradient, мигает
   прозрачностью. Градиент-фон рисуется в общем слое кнопки, отдельного слоя тени не создаётся,
   квадрата нет ни с GPU, ни без него. Ритм/тайминги пульса сохранены 1:1 (тот же «пульс МАЯК»
   со страницы входа в админку, о котором просил пользователь). */
.doc-zoom-nav::after,
.news-gallery-arrow::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 340%;
  height: 340%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  z-index: -1;
  pointer-events: none;
  /* Центр градиента ПУСТОЙ до 30% радиуса — ровно на месте самой кнопки (её диаметр = 100/340
     ширины этого круга). Псевдоэлемент с z-index:-1 внутри кнопки (у неё свой контекст наложения
     из-за position+z-index) рисуется ПОВЕРХ её фона-кружка, а не под ним: сплошной градиент на
     пике засвечивал лицо кнопки и гасил контраст самой стрелки — проверено живьём на
     остановленном кадре. Кольцо ведёт себя как прежняя тень: светится только ВОКРУГ. */
  background: radial-gradient(circle closest-side,
      rgba(255,255,255,0) 28%,
      rgba(255,255,255,.5) 34%,
      rgba(255,255,255,.2) 48%,
      rgba(255,255,255,0) 74%);
  animation: doc-zoom-nav-heartbeat 2.8s infinite;
}
@keyframes doc-zoom-nav-heartbeat {
  0% { opacity: .35; animation-timing-function: cubic-bezier(.2,.6,.35,1); }
  7% { opacity: 1; animation-timing-function: cubic-bezier(.55,0,.65,1); }
  18% { opacity: .28; animation-timing-function: cubic-bezier(.2,.6,.35,1); }
  27% { opacity: .8; animation-timing-function: cubic-bezier(.55,0,.4,1); }
  40% { opacity: .35; animation-timing-function: linear; }
  100% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  .doc-zoom-nav::after, .news-gallery-arrow::after { animation: none; opacity: .35; }
}
/* Прямая просьба пользователя 2026-08-10: на мобильном стрелки просмотрщика фото сначала увеличили
   до 80px, но это оказалось многовато — уменьшено обратно в 1.5 раза (80→53px, свг 40→27px). На ПК
   (38px) всё устраивало с самого начала — там по-прежнему ничего не трогаем. */
@media (max-width: 640px) {
  .doc-zoom-nav { width: 53px; height: 53px; }
  .doc-zoom-nav svg { width: 27px; height: 27px; }
}

/* Дышащие стрелки-кнопки полноразмерной галереи страницы поста (2026-08-10, отдельно от
   .doc-zoom-nav выше — тот же визуальный язык, но дети .news-gallery-wrap, не .doc-zoom-stage-wrap,
   см. main.js buildArrows()). У .news-gallery-wrap нет overflow:hidden (сама .news-gallery со
   скроллом — да, но обёртка вокруг неё — нет), поэтому свечение здесь не обрезается изначально,
   тот же баг с "рамкой" тут не воспроизводится структурно. */
.news-gallery-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.25);
  background: rgba(20,20,20,.55);
  color: var(--white);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Свечение — общий ::after с radial-gradient (см. блок у .doc-zoom-nav выше): анимированный
     box-shadow здесь давал тот же квадрат вокруг круглой кнопки на машине без GPU. */
}
.news-gallery-arrow:hover { border-color: var(--white); }
.news-gallery-arrow-prev { left: 4px; }
.news-gallery-arrow-next { right: 4px; }
@media (max-width: 640px) {
  .news-gallery-arrow { width: 28px; height: 28px; }
  .news-gallery-arrow svg { width: 14px; height: 14px; }
}
.doc-zoom-counter {
  position: absolute;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1002;
  color: var(--gray);
  font-size: .82rem;
  background: rgba(20,20,20,.7);
  padding: 3px 10px;
  border-radius: 999px;
}
.doc-zoom-counter[hidden] { display: none; }
#doc-zoom-media {
  width: 100%;
  height: 100%;
}
#doc-zoom-media video { width: 100%; height: 100%; }
#doc-zoom-media audio { width: 80%; max-width: 360px; }
#doc-zoom-media[hidden] { display: none; }

/* ---- style.css:2890-2917 ---- */
/* Эмодзи-картинки (Twemoji, emoji-render.js) — вписаны в высоту строки текста, не сдвигают
   базовую линию соседних символов.
   14.08 — было <img>, стало <span> с background-image: настоящий unicode-символ остаётся живым
   текстом внутри (color:transparent прячет его ВИЗУАЛЬНО, но не из DOM) — копипаст со страницы
   переносит реальный эмодзи, а не пустоту (баг с "не видит смайлики" при вставке в Telegram). */
.emoji-img {
  display: inline-block;
  color: transparent;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  height: 1.2em;
  width: 1.2em;
  /* overflow:hidden — базовой линией inline-block становится его НИЖНИЙ КРАЙ, а не спрятанный
     символ внутри. Без него базовая линия бралась от невидимого глифа системного emoji-шрифта
     (свои отступы сверху/снизу), и картинку поднимало над строкой — «смайлики выше текста»
     везде на сайте (баг 2026-09-24). Нижний край на 0.25em ниже строки = центр на середине букв. */
  overflow: hidden;
  vertical-align: -0.25em;
  /* 14.08, реальный найденный баг: при ВЫДЕЛЕНИИ текста мышкой браузер красит выделенный текст
     своим цветом ::selection, который перебивает color:transparent выше — под подсветкой вылезал
     настоящий unicode-символ поверх нашей же картинки (в системном emoji-шрифте, не Twemoji —
     визуально "два разных смайлика сразу"). Держим прозрачным и под выделением тоже. */
  margin: 0 .04em;
}
.emoji-img::selection { color: transparent; background-color: rgba(80, 140, 255, .35); }
.emoji-img::-moz-selection { color: transparent; background-color: rgba(80, 140, 255, .35); }
h1 .emoji-img, h2 .emoji-img, h3 .emoji-img { height: 0.85em; width: 0.85em; vertical-align: -0.08em; }

/* ---- style.css:2973-2994 ---- */
/* ========== Спойлер и моно на самой странице новости (views/news/show.php, main.js) ========== */
/* Спойлер — блюр+тёмная плашка до клика, снимается кликом (как в Telegram), .revealed навсегда
   открывает именно этот спойлер (снова не блюрится). */
.content-body .tg-spoiler {
  cursor: pointer; border-radius: 4px; background: #2a2a2a;
  filter: blur(5px); transition: filter .15s;
}
.content-body .tg-spoiler.revealed { filter: none; background: transparent; cursor: text; }
/* Моно — копируется по клику (реальная просьба пользователя), пунктирная рамка + курсор-подсказка
   визуально отличают его от обычного текста ДО клика — иначе непонятно, что кликабельно. */
.content-body code {
  background: var(--bg-panel-2); padding: 1px 6px; border-radius: 4px;
  font-family: 'Courier New', monospace; cursor: pointer; border: 1px dashed var(--border);
}
.content-body code:hover { border-color: var(--red); }
/* Всплывающая подсказка "Скопировано" — короткий тултип у самого клика, не модалка. */
.copy-toast {
  position: fixed; z-index: 9999; background: var(--red); color: #fff;
  font-size: .78rem; padding: 4px 10px; border-radius: 4px; pointer-events: none;
  opacity: 1; transition: opacity .4s ease .5s;
}
.copy-toast.copy-toast-fade { opacity: 0; }

/* ---- style.css:3129-3165 ---- */
/* Пересланный чужой пост: пометка источника.
   Одна и та же пометка печатается в трёх местах — страница новости, карточка в ленте, карточка
   на главной (см. forward_source_html() в src/Router.php) и, тем же смыслом, в Telegram-канале
   (ChannelPublisher::forwardNoteHtml). Визуально приглушена: это служебная строка-атрибуция,
   она не должна спорить с заголовком поста, но и прятать её нельзя — иначе чужой материал
   читается как собственный. */
.news-forward-note {
  margin: 0 0 10px;
  font-size: .82rem;
  color: var(--gray);
  font-style: italic;
}
.news-forward-note a {
  color: var(--gray);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.news-forward-note a:hover { color: var(--red); }
/* Карточка ленты (17.10.2026): та же пометка тихой строкой — без 📤 и курсива, мельче превью. */
.news-forward-note--compact { margin: 0 0 4px; font-size: .74rem; font-style: normal; }

/* ===== HONEYPOT (анти-спам форм, аудит 2026-08-23 #3) =====
   Поле-ловушка для ботов: человек его не видит, бот заполняет всё подряд и палится.
   position:absolute + left:-9999px, а НЕ display:none — специально:
   часть спам-ботов уже умеет пропускать display:none/hidden-поля, а «уехавшее за экран»
   поле для них выглядит обычным. Для человека результат тот же — не видно и не кликается.
   Из Tab-обхода и скринридера поле убрано в разметке (tabindex="-1" + aria-hidden),
   поэтому здесь достаточно визуального увода. */
.hp-field {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}


/* ---- style.css:3297-3367 ---- */
/* ---------- Скрытые вложения (спойлер Telegram), 2026-09-05 ----------
   Автор поста закрыл фото/видео — сайт обязан показывать его так же закрытым.
   Размываем САМ файл (img/video), а не контейнер: у контейнера свои рамки, значок ▶ и подпись,
   их размывать не нужно — иначе подсказку «Показать» не прочитать.
   scale(1.06) — компенсация: blur осветляет края, без увеличения по периметру видна рамка. */
.media-spoiler > img,
.media-spoiler > video {
  filter: blur(18px);
  transform: scale(1.06);
  transition: filter .25s ease, transform .25s ease;
}
.media-spoiler.spoiler-revealed > img,
.media-spoiler.spoiler-revealed > video {
  filter: none;
  transform: none;
}
.media-spoiler-hint {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 18px;
  border-radius: 12px;
  background: rgba(0, 0, 0, .55);
  border: 1px solid rgba(255, 255, 255, .16);
  color: rgba(255, 255, 255, .82);
  font-size: .78rem;
  letter-spacing: .02em;
  text-align: center;
  white-space: nowrap;
  pointer-events: none; /* клик должен доставаться самой плитке, а не подписи */
}
/* «Показать» — вторая строка, ярче и крупнее: это то, что человек ищет глазами. */
.media-spoiler-hint b {
  font-size: .84rem;
  font-weight: 600;
  color: #fff;
  border-bottom: 1px solid rgba(255, 255, 255, .45);
  padding-bottom: 1px;
}
/* Значок «перечёркнутый глаз» — вместо эмодзи, наследует цвет текста. */
.media-spoiler-mark { display: inline-flex; }
.media-spoiler-mark svg { width: 22px; height: 22px; display: block; }
/* На плитке мини-ленты значок один, без подписи — уменьшаем и ставим по центру. */
.news-mini-tile .media-spoiler-mark {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  color: rgba(255, 255, 255, .85);
  pointer-events: none;
}
.news-mini-tile .media-spoiler-mark svg { width: 18px; height: 18px; }
.media-spoiler.spoiler-revealed .media-spoiler-mark { display: none; }
.media-spoiler.spoiler-revealed .media-spoiler-hint { display: none; }
/* Значок «видео» поверх размытия только мешает читать подсказку — прячем до раскрытия.
   Значок мини-ленты НЕ прячем: на плитке 56px подсказка «Показать» не помещается, и единственный
   признак «тут что-то закрыто» — как раз бейдж (в карточке он для спойлера равен 🙈). */
.media-spoiler:not(.spoiler-revealed) .news-gallery-play { display: none; }
/* Плитка мини-ленты всего 56px — размытие в 18px превращает её в чёрный квадрат, неотличимый от
   пустой ячейки. Размываем слабее: содержимое всё равно не разобрать, но видно, что там картинка. */
.news-mini-tile.media-spoiler > img,
.news-mini-tile.media-spoiler > video { filter: blur(6px); }
.news-mini-tile.media-spoiler.spoiler-revealed > img,
.news-mini-tile.media-spoiler.spoiler-revealed > video { filter: none; }

/* Ревью визуала р.3 №12 (21.10.2026): миниатюра свидетельства — по левому краю колонки, подпись в шкале кеглей,
   кривая и длительность общие; без «подъёма с яркостью» из другого языка. */
.doc-cert-thumb-btn { margin: var(--s-3) 0 0; transition: transform var(--t-fast) var(--ease); }
.doc-cert-thumb-btn:hover, .doc-cert-thumb-btn:focus-visible { filter: none; }
.doc-cert-thumb-hint { font-size: var(--fs-1); padding: var(--s-2) 0 0; text-align: left; }
