/* ═══════════════════════════════════════════════════════════════════════════
 * ФУНКЦИОНАЛЬНЫЙ СЛОЙ
 * ───────────────────────────────────────────────────────────────────────────
 * Правила, которые держат не внешний вид, а поведение: разворот обложки,
 * индикаторы загрузки HTMX, скрытие неинициализированного Alpine, прижатие
 * подвала к низу окна, доступность.
 *
 * Выделено из theme.css при переходе на «Кадр». theme.css уходит вместе с
 * последним непеределанным экраном, а это остаётся: в дизайн-системе таких
 * правил нет вовсе, и с её приходом они не отменяются.
 *
 * Подключается ПОСЛЕДНИМ — после kadr.css и compat.css.
 * ═══════════════════════════════════════════════════════════════════════════ */




/* Ссылка «Перейти к содержимому» видна только под фокусом, но текст на ней —
   primary на поверхности карточки, а это на тёмном 3.4:1. Берём светлый тон шкалы */
[data-bs-theme=dark] .skip-link {
  color: var(--cdb-purple-300);
  border-color: var(--cdb-purple-300);
}

/* Прижимаем футер к низу окна: на коротких страницах он не должен всплывать в середину экрана */
body {
  background: var(--cdb-bg);
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;   /* на мобильных 100vh уезжает под панели браузера */
}

.site-content {
  flex: 1 0 auto;
  /* margin-bottom снят: он достался от старой темы и ложился поверх собственного
     нижнего отступа .cdb-main в 64 px — перед подвалом висело 88 px пустоты */
}

.site-header, .site-nav, .site-footer {
  flex: 0 0 auto;
}

.site-footer {
  margin-top: auto;
}

/* Видна только с клавиатуры: мышью её незачем показывать */
.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 1040;
  padding: .5rem .9rem;
  background: var(--cdb-white);
  color: var(--cdb-primary-text);
  border: 1px solid var(--cdb-primary);
  border-radius: 0 0 var(--cdb-radius-sm) 0;
}

.skip-link:focus {
  left: 0;
  top: 0;
}

#siteContent:focus {
  outline: none;
}

table {
  border-collapse: collapse;
  empty-cells: show;
}

td {
  position: relative;
}

/* Просмотр обложки во весь экран (H5). Пришёл на смену jquery-colorbox:
   у нативного <dialog> подложка — это ::backdrop, а Esc и ловушка фокуса встроены */
.cdb-lightbox {
  max-width: 96vw;
  max-height: 96vh;
  padding: 0;
  border: 0;
  border-radius: var(--bs-border-radius);
  background: transparent;
  overflow: visible;
}

.cdb-lightbox::backdrop {
  background: rgba(0, 0, 0, .8);
}

.cdb-lightbox__image {
  display: block;
  max-width: 96vw;
  /* Место под кнопку закрытия сверху, чтобы она не легла на саму обложку */
  max-height: calc(96vh - 2.5rem);
  margin-top: 2.5rem;
  border-radius: var(--bs-border-radius);
}

.cdb-lightbox__close {
  position: absolute;
  top: 0;
  right: 0;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--cdb-white);
  color: var(--cdb-gray-800);
  line-height: 1;
}

.cdb-lightbox__close:hover,
.cdb-lightbox__close:focus {
  background: var(--cdb-primary-light);
  color: var(--cdb-on-color);
}

tr.strikeout td:before {
  content: " ";
  position: absolute;
  top: 50%;
  left: 0;
  border-bottom: 1px solid var(--cdb-gray-900);
  width: 100%;
}

tr.strikeout td:after {
  content: "\00B7";
  font-size: 1px;
}


/* Ожидание ответа HTMX: элемент, отправивший запрос, гаснет и не принимает повторных кликов —
   это заменило CDB_Tools.disableButtons() и общий #bigPreloader на пол-экрана */
.htmx-request.btn,
.htmx-request .btn,
button.htmx-request {
  opacity: .65;
  pointer-events: none;
}

/* Кнопке с подписью спиннер приезжает разметкой (<span class="htmx-indicator">), у кнопки-иконки
   подписи нет — ей крутилку рисует псевдоэлемент, а иконка на это время прячется.
   :has() отделяет одни от других: где он не поддержан, правило просто не применится
   и кнопка останется приглушённой — то же поведение, что было до H2 */
.btn.htmx-request:not(:has(.htmx-indicator)) {
  position: relative;
}

.btn.htmx-request:not(:has(.htmx-indicator)) > i,
.btn.htmx-request:not(:has(.htmx-indicator)) > span:not(.htmx-indicator) {
  visibility: hidden;
}

.btn.htmx-request:not(:has(.htmx-indicator))::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 1em;
  height: 1em;
  margin: auto;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: cdb-spin .6s linear infinite;
}

/* Долгий запрос по старому jQuery-пути: полоса поверх страницы вместо белой заглушки с гифкой */
body.cdb-busy::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 2000;
  background: linear-gradient(90deg, var(--cdb-primary), var(--cdb-accent), var(--cdb-primary));
  background-size: 200% 100%;
  animation: cdb-busy-slide 1.1s linear infinite;
}

/* Alpine скрывает разметку до инициализации только вместе с этим правилом — сам он его
   не добавляет, а модалка живёт в общем шаблоне, то есть мигала бы на каждой странице */
[x-cloak] {
  display: none !important;
}

@media (prefers-reduced-motion: reduce) {
.btn.htmx-request::after { animation: none; }
  body.cdb-busy::before { animation: none; }
}


/* Скелет комментария. HTMX прячет индикаторы прозрачностью, но место в потоке они
   занимают: под вкладками висела пустая полоса в 65 px, из-за которой форма ответа
   уезжала вниз. Правило рядом было написано на класс .comment_skeleton, которого в
   разметке нет вовсе — там id и .cdb-comment, поэтому оно никогда не срабатывало */
#comment-skeleton { display: none; }
#comment-skeleton.htmx-request { display: grid; }
/* ═══════════════════════════════════════════════════════════════════════════
 * ПОСЕЩЁННЫЕ ССЫЛКИ
 * ───────────────────────────────────────────────────────────────────────────
 * «Кадр» посещённые ссылки не различает: токен --cdb-visited в нём объявлен,
 * но не используется ни одним правилом. Сайту различение нужно — на странице
 * серии полсотни выпусков, и для гостя цвет посещённого остаётся единственным
 * способом понять, что он это уже открывал (механизм «прочитано» работает
 * только под входом, а вход — лишь через VK). Основание: вопрос читателя
 * №1534 от 27.08.2026.
 *
 * Красим ТОЛЬКО названия материалов и только там, где ссылка ведёт на
 * страницу, которую читают. Ни одного :visited на кнопке, вкладке, пункте
 * меню, фильтре или пагинации: a:visited — это (0,1,1), а класс компонента
 * (0,1,0), и глобальное правило перекрасило бы их все. Именно на этом
 * четырежды обожглись при переезде на «Кадр».
 *
 * Свойства только цветовые: из соображений приватности браузер к :visited
 * ничего другого не применяет, а getComputedStyle отдаёт непосещённые
 * значения — проверять можно лишь глазами и лишь на посещённых ссылках.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* Названия выпусков и серий: карточка на главной, строка каталога, плитка
   обложки, номер выпуска в таблице серии. Последнему добавлен класс-якорь
   cdb-issue-link: в той же строке идут ссылки на глобалку, команду и двух
   переводчиков, тоже без классов, а помечать посещённым надо только выпуск —
   имя переводчика повторяется в каждой строке и в фиолетовом просто зарябит */
.cdb-issue__title:visited,
.cdb-srow__title:visited,
.cdb-tile:visited,
.cdb-issue-link:visited {
  color: var(--cdb-visited);
  text-decoration-color: var(--cdb-visited);
}

/* Под курсором посещённая ссылка берёт тот же цвет наведения, что и обычная
   (--cdb-brand-text, он темнее). Без этого правила отклика на наведение не было
   бы вовсе: --cdb-link-hover в светлой теме совпадает с --cdb-visited, и
   посещённая ссылка под курсором не менялась бы никак */
.cdb-issue__title:visited:hover,
.cdb-srow__title:visited:hover,
.cdb-tile:visited:hover,
.cdb-issue-link:visited:hover,
.cdb-issue__title:visited:focus-visible,
.cdb-srow__title:visited:focus-visible,
.cdb-issue-link:visited:focus-visible {
  color: var(--cdb-brand-text);
  text-decoration-color: var(--cdb-brand-text);
}

/* ═══════════════════════════════════════════════════════════════════════════
 * ЗАПЛАТКИ К «КАДРУ»
 * ───────────────────────────────────────────────────────────────────────────
 * Правки самой системы живут здесь, а не в kadr.css: тот файл стягивается из
 * Claude Design целиком, и любая правка в нём пропадёт при следующей стяжке.
 * Каждую заплатку нужно продублировать замечанием в дизайн — тогда она
 * когда-нибудь уедет отсюда обратно в систему.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* .pk-row — строка серии в личных списках. На узком экране система объявляет
   сетке две колонки, а прямых детей у строки три: обложка, название, полоса
   прогресса. Третий зажимался в 36 px обложки. Переносим его на всю ширину */
@container shell (max-width: 767px) {
  .pk-row > :last-child { grid-column: 1 / -1; }
}

/* Календарь недель. «Есть обзоры» против «нет» держалось на одном оттенке:
   фиолетовый #632372 против серого #7c7686 — это 2.4:1 между собой, при
   моноширинном шрифте 11.5 px в столбце из полусотни строк они не различаются.
   Добавляем второй признак, форму: точку у недель, куда можно провалиться.
   Пустым оставляем место под неё, иначе столбец съезжает по левому краю */
.cdb-month__list .cdb-week { position: relative; padding-left: 20px; }
.cdb-month__list a.cdb-week::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 50%;
  width: 5px;
  height: 5px;
  margin-top: -2px;
  border-radius: 50%;
  background: var(--cdb-brand);
}
/* У текущей недели фон уже залит брендом — точка на нём должна быть светлой */
.cdb-month__list a.cdb-week--now::before { background: var(--cdb-on-brand); }

/* .cdb-suggest — выпадашка с голыми текстовыми подсказками: подбор жанра в tag_input
   и подбор ника в people_suggest (мастер комиксов, шаг 4, и пакетное добавление).
   В «Кадре» под это нет готового компонента: единственная похожая выпадашка,
   .cdb-livesearch, жёстко зашита на строку с обложкой (первая колонка 34px), и голый
   текст в ней обрезается. Токены и форма — те же, что у .cdb-livesearch__results,
   только без колонки под картинку */
.cdb-suggest { position: relative; }
.cdb-suggest__list {
  position: absolute; z-index: 20; left: 0; right: 0; margin: var(--cdb-s2) 0 0;
  padding: 0; max-height: 220px; overflow-y: auto; list-style: none;
  border: 1px solid var(--cdb-line); border-radius: var(--cdb-r-sm);
  background: var(--cdb-surface); box-shadow: var(--cdb-e2);
}
.cdb-suggest__option {
  display: block; width: 100%; padding: 6px var(--cdb-s3); border: 0; background: none;
  text-align: left; font-size: var(--cdb-size-sm); color: var(--cdb-text); cursor: pointer;
}
.cdb-suggest__option:hover, .cdb-suggest__option:focus {
  background: var(--cdb-brand-soft); color: var(--cdb-brand-text);
}

/* .cdb-tag--warn — у .cdb-tag есть --brand/--signal/--outline/--ok/--alert, но не
   предупреждение, хотя токены --cdb-warn/--cdb-warn-bg в системе уже есть и используются
   в .cdb-callout--warn и .cdb-count--warn. Нужна в пакетном добавлении: пометить выпуск,
   у которого перевод уже есть, — это не ошибка (второй перевод другой командой обычное
   дело), но и не «ок» */
.cdb-tag--warn { background: var(--cdb-warn-bg); color: var(--cdb-warn); }

/* Очередь модерации: в системе у строки шесть колонок, последняя под кнопки
   приёма и отклонения. Построчных действий у нас нет ни у одного типа записи —
   контроллер умеет только пакетный приём, — а единственные кнопки, у потеряшки,
   живут в теле строки: там форма с выбором команды, в 136 px она не помещается.
   Колонка стояла пустой на каждой строке и оставляла дыру во всю таблицу */
.cdb-qrow { grid-template-columns: 32px 120px minmax(0, 1fr) 150px 110px; }


/* Мобильная шапка: только логотип и поиск. Тема и профиль уезжают в шторку «Ещё» —
   там профиль и так дублировался, а в шапке эти две иконки занимали отдельную
   строку под поиском. Меню шапки система прячет сама, вся навигация внизу */
@container page (max-width: 767px) {
  .cdb-topbar__actions { display: none; }
}

/* Поиск на телефоне открывается кнопкой нижней панели, а не занимает место в шапке
   постоянно. Шапка при этом остаётся из одного логотипа, и страницу не нужно
   прокручивать к началу, чтобы что-то найти */
@container page (max-width: 767px) {
  .cdb-topbar .cdb-search { display: none; }
  .cdb-topbar--searching .cdb-search { display: flex; }
}

/* Кнопка в мобильной шторке выглядит как пункт списка: в старой теме оформлены
   только ссылки, и нативная кнопка рядом с ними торчала рамкой и своим шрифтом.
   Кнопкой она осталась потому, что ничего не открывает — переключает тему на месте */
.nav-sheet__list button {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  min-height: 2.9rem;
  padding: .7rem .25rem;
  border: 0;
  border-bottom: 1px solid var(--cdb-line);
  background: none;
  color: var(--cdb-text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.nav-sheet__list button:hover { color: var(--cdb-brand-text); }

/* Поиск в шапке. Закрытый убран из потока совсем: шапка на телефоне переносит
   элементы строками, и поле нулевой высоты всё равно держало под собой пустую
   полосу. Плавность даёт анимация появления — переход с display не работает */
@container page (max-width: 767px) {
  .cdb-topbar .cdb-search { display: none; }
  .cdb-topbar--searching .cdb-search {
    display: flex;
    animation: cdb-search-in .18s var(--cdb-ease);
  }
}

@keyframes cdb-search-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* Боковая колонка, которая на телефоне важнее основной. На сводке панели это
   быстрые действия: нажать «Внести перевод» нужно чаще, чем читать таблицу
   последних переводов, а в исходном порядке она уезжала под неё */
@container page (max-width: 767px) {
  .cdb-cols > .cdb-stack--first-narrow { order: -1; }
}

/* Мост под выпадающим меню. В системе оно отстоит от кнопки на 4 px, и мышь,
   идущая к пунктам, проваливается в этот зазор — меню успевает закрыться.
   Отступ остаётся тем же на вид, но теперь он внутри самого меню, и курсор
   всё время держится на элементе */
.cdb-nav details > .cdb-nav__menu {
  top: 100%;
  padding-top: 8px;
}

/* Мобильная шторка занимала лишь треть экрана, хотя разделов в ней семнадцать:
   высота по содержимому упиралась в собственный предел offcanvas. Отдаём ей почти
   весь экран — прокрутка остаётся, но короткая, а не на четыре экрана */
.nav-sheet { --bs-offcanvas-height: auto; max-height: 88vh; }
.nav-sheet .offcanvas-body { overflow-y: auto; }

/* Бейдж в нижней панели: пункт — колонка «иконка, подпись», и число вставало
   третьей строкой, разъезжая панель по высоте. Прижимаем его к иконке, как в
   мобильных приложениях */
.cdb-bottomnav a, .cdb-bottomnav button { position: relative; }
.cdb-bottomnav .badge {
  position: absolute;
  top: 4px;
  left: 50%;
  margin-left: 2px;
  padding: 1px 5px;
  font-size: 9px;
  line-height: 1.4;
}

/* Карточка баннера: сведения и управление рядом, а не одной колонкой. У «Кадра»
   .cdb-banner__body — простой столбец, и на широком экране справа от коротких строк
   («Команда», «Ссылка») оставалась половина карточки пустой, а кнопки уезжали вниз.
   Ниже 900px — обратно в столбец: две колонки там уже не помещаются.
   Рамка превью в форме краснеет, когда размер файла не сошёлся с форматом */
@container page (min-width: 900px) {
  .cdb-banner__body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 320px); gap: var(--cdb-s3) var(--cdb-s5); align-content: start; }
  .cdb-banner__body > .cdb-row { grid-column: 1 / -1; }
  .cdb-banner__body > .cdb-facts { margin: 0; }
  .cdb-banner__body > .cdb-filterbar,
  .cdb-banner__body > .cdb-banner__actions { grid-column: 2; }
  .cdb-banner__body > .cdb-banner__actions { margin-top: 0; align-self: start; }
}
.cdb-banner__frame.is-alert { border-color: var(--cdb-alert); color: var(--cdb-alert); }

/* Форма загрузки баннера: поля и макет места рядом. Инлайновой сеткой это не задать —
   на узком экране колонка превью отбирала ширину у полей, и «Начните вводить название»
   сжималось до 86 px. Ниже 700px превью уходит под поля */
.banner-upload { display: grid; gap: var(--cdb-s4); align-items: start; }
@container page (min-width: 700px) {
  .banner-upload { grid-template-columns: minmax(0, 1fr) 260px; }
}

/* Ширина поля в ряду фильтров. .cdb-field в системе — только колонка «подпись + поле»,
   своей доли строки у неё нет, а ряд фильтров раскладывается флексом: без базы поля
   сжимались по содержимому и подписи ломались посреди слова. Раньше эту ширину давал
   filter-bar__field из старой темы */
.cdb-listbar > .cdb-field { flex: 1 1 13rem; min-width: 0; }
.cdb-listbar > .cdb-field--narrow { flex-basis: 10rem; }

/* Выбор команды поверх .cdb-livesearch. Компонент системы рассчитан на результаты
   поиска серий — список стоит в потоке под полем, а строка сложена сеткой
   «обложка | название | мета». Выбору команды нужно другое, и это три заплатки,
   а не свой виджет: обложек у команд нет, список должен накрывать содержимое
   (иначе форма прыгает на каждой букве), а подсветка ведётся классом, а не :hover —
   строку выбирают и стрелками с клавиатуры, когда мышь стоит совсем не там */
.cdb-livesearch--grow { flex: 1 1 auto; width: auto; }
.cdb-livesearch__results--drop { position: absolute; z-index: 20; left: 0; right: 0; }
.cdb-livesearch__item--plain { grid-template-columns: minmax(0, 1fr); }
.cdb-livesearch__item.is-active { background: var(--cdb-brand-soft); }

/* Ритм колонки: промежутки между блоками раздаёт .cdb-stack, и своих внешних
   отступов у блоков внутри быть не должно. Два таких оставались: у ряда баннеров
   (собственный margin старой темы ложился поверх зазора — 40 px вместо 24) и у
   последнего абзаца секции, чей хвост давал 36 там, где соседняя секция давала 24.
   В подвале ряд баннеров стоит вне стека, и там его отступ нужен — правило адресное */
.cdb-stack > .banners { margin-bottom: 0; }
.cdb-stack > section > :last-child { margin-bottom: 0; }

/* Шапка внутри ряда: свой отступ снизу ей не нужен — промежутки раздаёт сам ряд.
   На широком экране это незаметно (шапка, логотип и счётчик стоят в одну строку),
   а на узком счётчик переносится под шапку, и два отступа складывались в 40 px
   там, где по общему правилу должно быть 24 */
.cdb-row > .cdb-pagehead { margin-bottom: 0; }

/* Ритм вкладок: одна величина на весь сайт. Каждый экран носил свой инлайновый
   отступ — 16 у серии, журнала и команды, 24 у профиля и чтения, — и на «Читай также»
   выходило 16 сверху против 32 снизу: список выглядел прижатым к вкладкам и оторванным
   от формы. Инлайны сняты, зазор задаётся здесь и от списка вниз тем же токеном */
.cdb-pills { margin-bottom: var(--cdb-s5); }

/* Отступ под списком — только когда в списке что-то есть. Пустой контейнер
   оставлял под вкладками полосу воздуха, и форма под ним выглядела оторванной.
   Правило общее: списки комментариев и рекомендаций бывают пустыми одинаково,
   и раньше каждый носил свой инлайновый margin, который в пустом виде не снимался */
.cdb-readmore--spaced,
.cdb-list--spaced { margin-bottom: var(--cdb-s5); }
.cdb-readmore--spaced:not(:has(> *)),
.cdb-list--spaced:not(:has(> *)) { margin-bottom: 0; }

/* Форма под списком отступает от него, но не от вкладок: когда список пуст, отступ
   схлопывался с отступом вкладок в 24 px, и на «Комментариях» содержимое начиналось
   ниже, чем на «Читай также». Между списком и формой лежит скрытый скелет загрузки,
   поэтому соседний селектор здесь общий, а не непосредственный */
.cdb-form--after-list { margin-top: var(--cdb-s5); }
.cdb-list--spaced:not(:has(> *)) ~ .cdb-form--after-list { margin-top: 0; }

/* Шапка экрана — один компонент на весь сайт. Половина экранов рисовала заголовок
   голым .cdb-hero со своим инлайновым отступом, половина — .cdb-pagehead: от
   заголовка до содержимого выходило то 24, то 91 px. Теперь все обёрнуты в
   .cdb-pagehead, а здесь только то, чего в системе нет: размер голого заголовка
   внутри неё и обнуление отступа у подзаголовка — воздух под шапкой даёт сама шапка */
.cdb-pagehead .cdb-hero { font-size: var(--cdb-size-h1); margin: 0 0 var(--cdb-s2); }
.cdb-pagehead .cdb-hero:last-child { margin-bottom: 0; }
.cdb-pagehead .cdb-kicker,
.cdb-pagehead > p { margin: 0; }

/* Пагинация отступает от списка сверху и снизу, но не тогда, когда стоит первой:
   её отступ складывался с отступом шапки, и до содержимого выходило 64 px вместо 24.
   Раньше он был инлайновым в Tools::getPagination и перебивал любое правило */
.cdb-pager { margin: var(--cdb-s4) 0; }
.cdb-cols > main > .cdb-pager:first-child,
.cdb-main > .cdb-pagehead + .cdb-pager { margin-top: 0; }
