/*
 * components.css — переиспользуемые блоки публичной части: постер, оценки,
 * хлебные крошки, карусель, панели сайдбара, лента каталога, фильтры,
 * пагинация, SEO-текст и служебные состояния (загрузка, тост, модалка).
 *
 * Композиция и плотность — MASTER ТЗ №3 (п.16, 19–41, 126–131, 170–179) и
 * референсы 1.png / 3.png. Все значения берутся из UI_TOKENS.css: ни одного
 * инлайнового цвета и ни одного «магического» размера здесь быть не должно.
 */

/* ── Постер: пропорция 2:3 на всех вариантах ──────────────────────────────── */

/*
 * Пропорцию держит сама обёртка, а не картинка. Пока 2:3 задавалось на `img`,
 * высота ячейки зависела от того, загрузилась ли картинка и какой она формы:
 * в сетке рекомендаций соседние карточки получались разной высоты, а
 * заглушка без `src` схлопывалась. Теперь размер ячейки известен до загрузки,
 * а изображение растягивается по ней абсолютом — ряд ровный всегда.
 */
.poster {
    position: relative;
    display: block;
    flex: none;
    aspect-ratio: var(--poster-ratio);
    overflow: hidden;
    background: var(--surface-2);
    border-radius: var(--radius-sm);
}

.poster__image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/*
 * Заглушка картинок (постер, фото персоны, аватар).
 *
 * Адреса в разметке нет: вид приходит атрибутом `data-ph`, а сам файл
 * подставляет `lazy.js` переменной `--ph-<вид>` на корне документа. Заглушка
 * рисуется фоном под картинкой, поэтому видна ровно до тех пор, пока обложка
 * не загрузилась (ленивая — пока не подъехала к экрану), и не требует ни
 * второго тега, ни подмены `src`.
 *
 * `is-loaded` вешает тот же скрипт: под непрозрачной картинкой фон уже не
 * нужен, а держать его — значит держать декодированный SVG в памяти на каждой
 * ячейке ленты.
 */
img[data-ph] {
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    /* У картинки без адреса не должен вылезать `alt` поверх заглушки. */
    color: transparent;
    font-size: 0;
}

img[data-ph="poster"] {
    background-image: var(--ph-poster, none);
}

img[data-ph="profile"] {
    background-image: var(--ph-profile, none);
}

img[data-ph="avatar"] {
    background-image: var(--ph-avatar, none);
}

img[data-ph].is-loaded {
    background-image: none;
}

/*
 * Наведение на обложку — лёгкое затемнение. Приём один на весь сайт: постер
 * в ленте, в карусели и в рекомендациях, портрет актёра, обложка подборки и
 * коллаж франшизы гаснут одинаково, поэтому «наведено» читается без подписи.
 *
 * Гасится само изображение, а не элемент-обёртка. `filter` на обёртке создаёт
 * новый контекст наложения: вместе с картинкой потемнели бы бейдж качества,
 * лента с составом сезона и подсказка `hover-tip`, а они как раз и должны
 * остаться яркими поверх приглушённой обложки.
 *
 * Ховер ловится на обложке целиком, а не на `img`: у постера сверху лежат
 * бейдж и лента, и курсор на них не считался бы наведением на картинку.
 */
.poster__image,
.actor-card__photo,
.entity-cover__image,
.entity-header__cover,
.search-people__photo,
.franchise-index__poster,
.sidebar-franchises__poster {
    transition: filter var(--transition-fast);
}

.poster:hover .poster__image,
.actor-card:hover .actor-card__photo,
.entity-cover:hover .entity-cover__image,
.entity-grid__link:hover .entity-cover__image,
.search-people__link:hover .search-people__photo,
.franchise-index__cover:hover .franchise-index__poster,
.sidebar-franchises__link:hover .sidebar-franchises__poster {
    filter: brightness(.72);
}

.poster__badge {
    position: absolute;
    left: var(--space-1);
    bottom: var(--space-1);
    padding: 1px 5px;
    background: var(--overlay-strong);
    border-radius: var(--radius-xs);
    color: var(--accent-yellow);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: .02em;
}

/*
 * Лента состава сериала (12.png): полоса прижата к нижнему краю обложки и к
 * её правой границе, а слева обрывается уголком-стрелкой. Уголок делает
 * `clip-path`, а не псевдоэлемент с треугольником из бордеров: полоса тянется
 * по тексту, и треугольник пришлось бы двигать вслед за ней.
 */
.poster__ribbon {
    position: absolute;
    right: 0;
    bottom: 0;
    max-width: calc(100% - var(--space-3));
    padding: 3px var(--space-3) 3px var(--space-6);
    background: var(--ribbon-series);
    clip-path: polygon(12px 0, 100% 0, 100% 100%, 12px 100%, 0 50%);
    color: var(--link-hover);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-bold);
    line-height: 1.35;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.poster--feed {
    width: var(--poster-feed-width);
}

.poster--carousel {
    width: var(--poster-carousel-width);
}

.poster--content {
    width: var(--poster-content-width);
}

/* Постер занимает всю ячейку сетки: рекомендации, «Скоро на сайте». */
.poster--card {
    width: 100%;
}

/* Пустой постер: своей ширины у него нет, ячейку он занимает целиком. */
.poster--empty {
    width: 100%;
}

/* Строка «Обновления сериалов»: 44x66 (ТЗ №3, п.25). */
.poster--row {
    width: 44px;
}

/* ── Звёзды и бейджи рейтингов ────────────────────────────────────────────── */

/*
 * Блок оценки (2.png): ряд звёзд, под ним — «3.2/5 (70 гол.)» мелким серым.
 * Колонкой, а не строкой: подпись стоит именно под звёздами и выровнена по их
 * правому краю, потому что весь блок прижат к правому краю шапки материала.
 */
.rating-stars {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 1px;
}

/*
 * Просветы между звёздами даёт не `gap`, а внутреннее поле самих звёзд.
 * Разница видна на интерактивной шкале: `gap` — это ничья полоса, и стоило
 * курсору попасть в неё, как «наведена шкала, но не наведена ни одна звезда»
 * гасило подсветку целиком. При проводке по ряду шкала моргала на каждом
 * пикселе между звёздами. С полем внутри кнопок мёртвых зон в ряду нет.
 */
.stars {
    display: inline-flex;
}

.stars__star {
    display: inline-flex;
    padding: 0 1px;
    background: none;
    border: 0;
    color: var(--text-muted);
    line-height: 0;
}

/* Крайние поля наружу не выпирают: ряд остаётся выровненным по краю блока. */
.stars__star:first-child {
    padding-left: 0;
}

.stars__star:last-child {
    padding-right: 0;
}

.stars__star.is-active {
    color: var(--accent);
}

.rating-stars--interactive .stars__star {
    cursor: pointer;
    transition: color var(--transition-fast), transform var(--transition-fast);
}

/*
 * Наведение показывает будущую оценку целиком: подсвечиваются все звёзды до
 * курсора включительно, а уже проставленные за ним — гаснут. Одна горящая
 * звезда под курсором читалась как «поставить пятую», хотя клик ставит пять.
 * Гашение идёт первым правилом, подсветка — вторым: порядок и решает.
 */
.rating-stars--interactive .stars:hover .stars__star {
    color: var(--text-muted);
}

/*
 * `.stars:hover` в селекторе не для красоты: правило гашения выше весит
 * (0,4,0), и подсветка обязана весить столько же, иначе звезда под курсором
 * гаснет вместе с остальными.
 */
.rating-stars--interactive .stars:hover .stars__star:hover {
    color: var(--accent-hover);
}

/*
 * Звёзды левее курсора. Отдельным правилом, а не через запятую с предыдущим:
 * браузер без `:has()` выбросил бы весь список селекторов разом и потерял
 * бы заодно подсветку самой звезды под курсором. Так теряется только
 * предпросмотр шкалы, а не он вместе с наведением.
 */
.rating-stars--interactive .stars__star:has(~ .stars__star:hover) {
    color: var(--accent-hover);
}

.rating-stars--interactive .stars__star:hover {
    transform: scale(1.08);
}

/*
 * Проголосовал — больше нельзя (замечание заказчика). Оценка остаётся видной
 * и горит акцентом: это не «выключенный элемент», а зафиксированный выбор.
 * Поэтому гасится только поведение — курсор, наведение и увеличение, — а не
 * сами звёзды: приглушить их значило бы сообщить, что голос не принят.
 */
.rating-stars.is-voted .stars {
    cursor: default;
}

.rating-stars.is-voted .stars__star {
    cursor: default;
    transform: none;
}

.rating-stars.is-voted .stars:hover .stars__star {
    color: var(--text-muted);
}

.rating-stars.is-voted .stars__star.is-active,
.rating-stars.is-voted .stars:hover .stars__star.is-active {
    color: var(--accent);
}

.rating-stars__summary {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    line-height: 1.3;
    white-space: nowrap;
}

.rating-stars__value {
    color: var(--text-secondary);
    font-weight: var(--font-weight-semibold);
}

.rating-stars__scale,
.rating-stars__votes {
    color: var(--text-muted);
}

.rating-badges {
    display: flex;
    gap: var(--space-1);
}

.rating-badges--stack {
    flex-direction: column;
    align-items: stretch;
}

.rating-badge {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    min-width: 62px;
    padding: 1px var(--space-2);
    border-radius: var(--radius-xs);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: 1.6;
}

.rating-badge--kp {
    background: var(--accent);
    color: var(--text-on-accent);
}

.rating-badge--imdb {
    background: var(--accent-yellow);
    color: var(--text-on-accent);
}

.rating-badge__value {
    font-weight: var(--font-weight-bold);
}

/*
 * Вариант `text` — подвал карточки ТОПа (8.png): «КП: 8.1  IMDb: 6.8» без
 * подложек. Цветом здесь работает сам текст, поэтому подложку и минимальную
 * ширину бейджа приходится снимать, а не переопределять по мелочи.
 */
.rating-badges--text {
    gap: var(--space-5);
}

.rating-badges--text .rating-badge {
    min-width: 0;
    padding: 0;
    background: none;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-bold);
}

.rating-badges--text .rating-badge--kp {
    color: var(--accent);
}

.rating-badges--text .rating-badge--imdb {
    color: var(--accent-yellow);
}

/* Двоеточие после подписи — часть строки, а не отдельный элемент разметки. */
.rating-badges--text .rating-badge__label::after {
    content: ":";
}

.rating-badges--text .rating-badge__value {
    font-weight: var(--font-weight-medium);
}

/* ── Хлебные крошки ───────────────────────────────────────────────────────── */

.breadcrumbs {
    color: var(--text-muted);
    font-size: var(--font-size-sm);
}

/*
 * Крошки — верхняя полоса контентной колонки, и её высота равна высоте шапки
 * блока сайдбара: первая линия фигуры идёт через обе колонки одним уровнем.
 * Поэтому крошки есть и на главной — там они состоят из одного «Главная».
 */
.content-column > .breadcrumbs {
    display: flex;
    align-items: center;
    min-height: var(--sheet-header-height);
}

.breadcrumbs__list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
}

.breadcrumbs__item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.breadcrumbs__link {
    color: var(--text-secondary);
}

.breadcrumbs__link:hover {
    color: var(--link-hover);
}

.breadcrumbs__current {
    color: var(--text-muted);
}

/* ── Карусель постеров ────────────────────────────────────────────────────── */

/*
 * Полка постеров в шапке (1.png). Ширина постера не задаётся числом: её
 * считает сама полоса из --carousel-columns, поэтому в ряд всегда попадает
 * целое число карточек и с краю не остаётся обрезанной половины. По той же
 * причине прокрутка — обязательная (`mandatory`), а не «по возможности».
 */
.carousel {
    position: relative;
    padding: var(--space-2);
    background: var(--carousel-bg);
    /*
     * Полка примыкает к вкладкам меню сверху: тёмная линия стыка и светлая
     * кромка под ней сняты с 1.png пипеткой (#0e0e0e и #848480).
     *
     * Скруглены три угла из четырёх. Меню занимает правую часть верхней
     * кромки и закрывает собой правый верхний угол — там скругление дало бы
     * щель под вкладкой. Левый верхний угол открыт, и квадратным он выглядел
     * ошибкой рядом с закруглённым низом (замечание заказчика).
     */
    border-top: 1px solid var(--carousel-seam);
    border-radius: var(--radius-sm) 0 var(--radius-sm) var(--radius-sm);
    box-shadow:
        inset 0 1px 0 var(--carousel-edge),
        inset 0 0 0 1px var(--border-strong);
}

.carousel__track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% - (var(--carousel-columns) - 1) * var(--carousel-gap)) / var(--carousel-columns));
    gap: var(--carousel-gap);
    overflow-x: auto;
    /*
     * Вертикаль заперта явно. `overflow-x: auto` сам по себе оставляет
     * `overflow-y` в значении `auto` — по спецификации `visible` рядом с
     * невидимым соседом вычисляется именно так. Ряд постеров выше своей
     * полосы на доли пикселя, и этого хватало, чтобы полка ездила вверх-вниз:
     * на телефоне вертикальный свайп по карусели дёргал её вместо страницы.
     */
    overflow-y: hidden;
    /*
     * Свайп по полке крутит только её и только вбок: `pan-y` отдаёт
     * вертикальный жест странице, а `overscroll-behavior-x` не даёт докрутке
     * до края уйти в навигацию «назад» и в прокрутку родителя.
     */
    touch-action: pan-x pan-y;
    overscroll-behavior-x: contain;
    scroll-behavior: smooth;
    scrollbar-width: none;
    scroll-snap-type: x mandatory;
}

.carousel__track::-webkit-scrollbar {
    display: none;
}

.carousel__item {
    min-width: 0;
    scroll-snap-align: start;
}

.carousel__link {
    display: block;
    width: 100%;
    transition: transform var(--transition-fast), filter var(--transition-fast);
}

/* Постер тянется по колонке сетки, а не по фиксированной ширине. */
.carousel .poster--carousel {
    width: 100%;
}

/*
 * Сдвиг ровно 1.02: карусель не должна «прыгать» при наведении (ТЗ №3, п.16).
 * Осветления здесь больше нет — обложка под курсором гаснет общим правилом
 * `.poster:hover .poster__image`, и подсветка ссылки спорила бы с ним.
 */
.carousel__link:hover {
    transform: scale(1.02);
}

/*
 * Подсказка поверх обложки (`data-tip`).
 *
 * Подписей под постерами на референсе нет, а знать название всё же нужно —
 * оно приходит при наведении полосой по нижнему краю самой обложки. Именно
 * внутри картинки, а не всплывающим облаком: полка карусели прокручивается
 * (`overflow-x: auto`), и всё, что выйдет за её границы, будет обрезано.
 *
 * `::after` вместо отдельного элемента — подсказка не должна попадать ни в
 * поток, ни в текст ссылки для скринридера: им название отдаёт `title`.
 */
.hover-tip {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-sm);
}

.hover-tip::after {
    content: attr(data-tip);
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    padding: var(--space-4) var(--space-2) var(--space-2);
    background: linear-gradient(to top, rgba(6, 8, 9, .97) 0, rgba(6, 8, 9, .88) 55%, rgba(6, 8, 9, 0) 100%);
    border-bottom: 2px solid var(--accent);
    color: var(--link-hover);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    text-align: center;
    /* Ховер не должен перехватывать курсор у самой ссылки. */
    pointer-events: none;
    opacity: 0;
    transform: translateY(100%);
    transition: opacity var(--transition-fast), transform var(--transition-fast);
}

.hover-tip:hover::after,
.hover-tip:focus-visible::after,
a:hover > .hover-tip::after,
a:focus-visible > .hover-tip::after {
    opacity: 1;
    transform: none;
}

/* Мышью не наводят: на тач-экране подсказка только мешала бы. */
@media (hover: none) {
    .hover-tip::after {
        content: none;
    }
}

/*
 * Стрелки лежат поверх крайних постеров, как на референсе: вынести их в
 * поток значило бы отнять у ряда 50px и снова получить дробную колонку.
 */
.carousel__arrow {
    position: absolute;
    top: 50%;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    /*
     * Шире прежних 22px и в цвет самой полки (замечание заказчика): чёрная
     * плашка поверх постера читалась как посторонний элемент, а узкая полоса
     * плохо попадала под палец. Фон тот же, что у карусели, — стрелка
     * выглядит вырезом в полке, а не наклейкой на обложке.
     */
    width: 32px;
    height: 54px;
    padding: 0;
    background: var(--carousel-bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-xs);
    color: var(--link-hover);
    cursor: pointer;
    transform: translateY(-50%);
    transition: color var(--transition-fast), border-color var(--transition-fast);
}

.carousel__arrow--prev {
    left: var(--space-1);
}

.carousel__arrow--next {
    right: var(--space-1);
}

.carousel__arrow:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* Дошли до края — стрелка не нужна: скрипт снимает её с обоих концов. */
.carousel__arrow[hidden] {
    display: none;
}

/* ── Общие списки сайдбара ────────────────────────────────────────────────── */

.sidebar-panel__subtitle {
    padding: var(--space-2) 12px 0;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
}

.link-list__item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    line-height: 1.5;
}

.link-list__link {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.link-list__item.is-active .link-list__link {
    color: var(--accent);
}

.link-list__count {
    flex: none;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    font-variant-numeric: tabular-nums;
}

/*
 * Вложенный уровень группы «Категории» («Зарубежные», «Турецкие», «Русские»
 * под «Все сериалы»): сдвиг вправо и тонкая направляющая слева — иначе
 * подкатегории читаются как продолжение общего списка, и «Турецкие» выглядит
 * разделом того же уровня, что и «Все фильмы».
 */
.link-list__item--sub {
    display: block;
}

.link-list--nested {
    margin: 2px 0 var(--space-2) 6px;
    padding-left: var(--space-3);
    border-left: 1px solid var(--border);
}

.link-list--nested .link-list__link {
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
}

.link-list--nested .link-list__link:hover {
    color: var(--link-hover);
}

/* ── Панель навигации: две колонки ────────────────────────────────────────── */

.sidebar-nav__columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4) var(--space-5);
    padding: var(--panel-padding);
}

/*
 * Колонка — свой поток: группы в ней встают вплотную друг под друга, а не
 * выравниваются по строкам сетки. «Категории» стоят над «Годом выхода»
 * (замечание заказчика) и высотой соседней колонки не двигаются.
 */
.sidebar-nav__column {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    min-width: 0;
}

.sidebar-nav__group {
    min-width: 0;
}

.sidebar-nav__heading {
    margin-bottom: var(--space-2);
    color: var(--accent);
    font-size: var(--font-size-sidebar);
    font-weight: var(--font-weight-semibold);
}

.sidebar-nav__more {
    display: inline-block;
    margin-top: var(--space-2);
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
}

.sidebar-nav__more:hover {
    color: var(--accent);
}

/* ── Обновления сериалов: строки, а не сетка постеров ─────────────────────── */

.series-updates__item + .series-updates__item {
    border-top: 1px solid var(--border);
}

.series-updates__link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) 12px;
    transition: background var(--transition-fast);
}

.series-updates__link:hover {
    background: var(--surface-3);
}

.series-updates__text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    flex: 1 1 auto;
    min-width: 0;
}

/*
 * Название материала в строке обновления — на шаг крупнее служебной мелочи
 * рядом (замечание заказчика): в панели это главное слово строки, а номер
 * серии, год и озвучка — приписки к нему.
 */
.series-updates__title {
    overflow: hidden;
    color: var(--text-primary);
    font-size: var(--font-size-sidebar);
    font-weight: var(--font-weight-semibold);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.series-updates__year,
.series-updates__meta {
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-normal);
}

.series-updates__side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 1px;
    flex: none;
    text-align: right;
}

.series-updates__episode {
    color: var(--accent);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
}

.series-updates__translation {
    max-width: 96px;
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Скоро на сайте ───────────────────────────────────────────────────────── */

.upcoming__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
    padding: var(--panel-padding);
}

.upcoming__link,
.upcoming__frame {
    display: block;
}

.upcoming__title {
    display: block;
    margin-top: var(--space-2);
    color: var(--text-primary);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
}

.upcoming__link:hover .upcoming__title {
    color: var(--link-hover);
}

.upcoming__year {
    color: var(--text-muted);
}

.upcoming__note {
    display: block;
    margin-top: 1px;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
}

/* ── Франшизы в сайдбаре (1.png) ──────────────────────────────────────────── */

.sidebar-franchises__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--panel-padding);
}

.sidebar-franchises__link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.sidebar-franchises__link:hover {
    background: var(--surface-3);
    border-color: var(--border-strong);
}

/*
 * Постеры частей идут встык одной лентой (1.png), поэтому у мозаики свой
 * фон и рамка, а не по рамке на каждую обложку: три тонкие рамки подряд в
 * панели шириной 300px превращаются в частокол.
 */
.sidebar-franchises__mosaic {
    display: flex;
    flex: none;
    overflow: hidden;
    background: var(--surface-4);
    border-radius: var(--radius-xs);
}

.sidebar-franchises__poster {
    display: block;
    width: 24px;
    height: 36px;
    object-fit: cover;
}

/* Название франшизы — на шаг крупнее (замечание заказчика): рядом с коллажем
   из трёх обложек подпись в 12px читалась подрисуночной, а не заголовком. */
.sidebar-franchises__title {
    min-width: 0;
    color: var(--text-secondary);
    font-size: var(--font-size-sidebar);
    line-height: var(--line-height-tight);
}

.sidebar-franchises__link:hover .sidebar-franchises__title {
    color: var(--link-hover);
}

/* ── Последние комментарии ────────────────────────────────────────────────── */

.latest-comments__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--panel-padding);
}

/*
 * Пузырь реплики (10.png): скругления со всех сторон, кроме левого верхнего
 * угла — этот прямой угол и делает панель похожей на реплику диалога, а не на
 * обычную карточку. Рамки нет: пузырь отделяется от сайдбара тоном.
 */
.latest-comments__item {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4) var(--space-4);
    /* Пузырь светлее сайдбара — иначе реплика сливается с панелью (10.png). */
    background: var(--surface-4);
    border-radius: 0 var(--radius-bubble) var(--radius-bubble) var(--radius-bubble);
}

.latest-comments__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.avatar {
    flex: none;
    width: 44px;
    height: 44px;
    background: var(--surface-4);
    border-radius: var(--radius-avatar);
    object-fit: cover;
}

.avatar--sm {
    width: 32px;
    height: 32px;
}

/* Название материала над репликой — на шаг крупнее (замечание заказчика):
   в панели читают сначала «к чему комментарий», и только потом сам текст. */
.latest-comments__content {
    min-width: 0;
    overflow: hidden;
    color: var(--text-primary);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-bold);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.latest-comments__content:hover {
    color: var(--accent);
}

/*
 * Сам текст реплики: ник светлее, комментарий приглушён — на 10.png это одна
 * строка «Ник, текст», а не две подписи друг под другом.
 */
.latest-comments__excerpt {
    display: -webkit-box;
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-base);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}

.latest-comments__author {
    color: var(--text-primary);
}

/* ── Подсказки поиска ─────────────────────────────────────────────────────── */

.search-suggest {
    position: absolute;
    top: calc(100% + var(--space-1));
    right: 0;
    left: auto;
    z-index: var(--z-nav);
    /*
     * Шире поля ввода: в строке подсказки — постер, название, оригинальное
     * название и два бейджа оценок, и в 250px они превращаются в кашу из
     * многоточий. Растём влево, поле прижато к правому краю шапки.
     */
    width: 420px;
    max-width: calc(100vw - 2 * var(--gap));
    max-height: 380px;
    overflow-y: auto;
    padding: var(--space-1) 0;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-1);
}

/* Заголовок секции подсказок: «Материалы», «Люди», «Подборки». */
/*
 * Ссылка на полную выдачу первой строкой. Липкая: подсказок бывает больше,
 * чем помещается, и «показать все» не должно уезжать за край прокрутки.
 */
.search-suggest__all {
    position: sticky;
    top: 0;
    z-index: 1;
    display: block;
    padding: var(--space-3) var(--space-4);
    background: var(--surface-3);
    border-bottom: 1px solid var(--border-strong);
    color: var(--text-primary);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}

.search-suggest__all:hover {
    color: var(--accent);
}

.search-suggest__original {
    overflow: hidden;
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Оценки прижаты вправо: колонка бейджей, а не хвост строки метаданных. */
.search-suggest__marks {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-left: auto;
    flex: none;
}

.search-suggest__section {
    padding: var(--space-2) var(--space-4) var(--space-1);
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
}

.search-suggest__link {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    padding: var(--space-2) var(--space-4);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.search-suggest__item + .search-suggest__item {
    border-top: 1px solid var(--border);
}

.search-suggest__poster {
    flex: 0 0 auto;
    width: 32px;
    height: 48px;
    object-fit: cover;
    border-radius: var(--radius-xs);
    background: var(--surface-3);
}

.search-suggest__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.search-suggest__title {
    overflow: hidden;
    color: var(--text-primary);
    white-space: nowrap;
    text-overflow: ellipsis;
}

.search-suggest__meta {
    color: var(--text-muted);
    font-size: var(--font-size-xs);
}

.search-suggest__link:hover,
.search-suggest__link.is-active {
    background: var(--surface-3);
    color: var(--link-hover);
}

.search-suggest__link:hover .search-suggest__title,
.search-suggest__link.is-active .search-suggest__title {
    color: var(--link-hover);
}

/* ── Служебная панель над лентой ──────────────────────────────────────────── */

.service-panel {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-5);
    padding: var(--space-3) var(--space-5);
}

.service-panel__text {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.service-panel--accent .service-panel__text {
    color: var(--text-primary);
}

.service-panel__button {
    flex: none;
}

/* ── Строка фильтров ──────────────────────────────────────────────────────── */

.filter-bar {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
}

/*
 * Две строки: сортировка со счётчиком сверху, фильтры снизу. Раскрытая
 * панель фильтра растягивается на всю ширину строки, поэтому именно строка,
 * а не группа, задаёт её систему координат.
 */
.filter-bar__row {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.filter-bar__legend {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

/*
 * Фильтры занимают строку целиком и делят её поровну (1.png): фиксированная
 * минимальная ширина оставляла справа пустое место, а на узком экране
 * сбрасывала последнюю группу на вторую строку. Поэтому `nowrap` и
 * `flex: 1 1 0` — группы сжимаются, но остаются в одном ряду.
 */
.filter-bar__row--filters {
    flex-wrap: nowrap;
    padding-top: var(--space-3);
    border-top: 1px solid var(--sheet-rule);
    box-shadow: inset 0 1px 0 var(--border);
}

/*
 * Ряд полей приходит с сервера пустым: сами поля строит `filters.js` по
 * `window.__KINOSORT__`. Пока их нет (скрипт ещё не выполнился или отключён),
 * ряд не должен занимать место — иначе над лентой висит пустая полоса с
 * разделителем.
 */
.filter-bar__row--filters:empty {
    display: none;
}

/*
 * Группа и виджет намеренно не позиционируются: система координат раскрытой
 * панели — строка фильтров, иначе `left/right: 0` растянут её по ширине
 * кнопки, а не ряда.
 */
.filter-bar__group,
.filter-bar__row .fdrop {
    position: static;
}

.filter-bar__row--filters .filter-bar__group,
.filter-bar__row--filters .fdrop {
    flex: 1 1 0;
    min-width: 0;
}

.filter-bar__select {
    width: 100%;
    height: 30px;
    padding: 0 var(--space-3);
    background: var(--sheet-inset);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.filter-bar__select.is-active {
    border-color: var(--accent);
    color: var(--text-primary);
}

/*
 * Оформленный список поверх нативного. Пока JS не отработал, виден обычный
 * <select> — он же остаётся носителем значения, поэтому убирается с глаз
 * только после того, как виджет построен (класс вешает скрипт).
 */
.fdrop__native {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
}

.fdrop {
    position: relative;
}

/*
 * `min-width` здесь нет намеренно: кнопка обязана сжиматься вместе с долей
 * строки, иначе ряд фильтров снова уезжает на вторую строку.
 */
.fdrop__toggle {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    height: 30px;
    padding: 0 var(--space-3);
    background: var(--sheet-inset);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--font-size-sm);
    line-height: 1;
    text-align: left;
    cursor: pointer;
    transition: border-color var(--transition-base), color var(--transition-base);
}

.fdrop__toggle:hover {
    border-color: var(--accent);
    color: var(--text-primary);
}

.fdrop__label {
    flex: 1 1 auto;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.fdrop__arrow {
    flex: none;
    color: var(--text-muted);
    transition: transform var(--transition-base);
}

.fdrop.is-open .fdrop__toggle,
.fdrop.is-active .fdrop__toggle {
    border-color: var(--accent);
    color: var(--text-primary);
}

.fdrop.is-open .fdrop__arrow {
    transform: rotate(180deg);
}

.fdrop.is-active .fdrop__arrow {
    color: var(--accent);
}

/*
 * Раскрытая панель фильтра: шапка с поиском, сетка галочек, подвал с
 * «Применить»/«Отмена». Панель во всю ширину строки, а не узкий столбик под
 * кнопкой: тридцать лет или сорок жанров в один столбец — это экран прокрутки
 * ради четырёх галочек.
 */
/* `display: flex` перебивает [hidden], поэтому скрытие задаётся явно. */
.fdrop__panel[hidden] {
    display: none;
}

.fdrop__panel {
    position: absolute;
    z-index: var(--z-nav);
    top: calc(100% + var(--space-2));
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-1);
}

.fdrop__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border);
}

.fdrop__heading {
    flex: 1 1 auto;
    color: var(--accent);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-bold);
}

.fdrop__search {
    width: 160px;
    height: 28px;
    padding: 0 var(--space-3);
    background: var(--surface-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-sm);
}

.fdrop__search::placeholder {
    color: var(--text-muted);
}

.fdrop__close {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    background: none;
    border: 0;
    color: var(--text-secondary);
    cursor: pointer;
}

.fdrop__close:hover {
    color: var(--accent);
}

/* Варианты в четыре колонки: так список лет умещается без прокрутки. */
.fdrop__list {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-1) var(--space-3);
    max-height: 300px;
    padding: var(--space-4);
    overflow-y: auto;
}

.fdrop__option {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-2);
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    line-height: 1.35;
    cursor: pointer;
}

.fdrop__option:hover {
    background: var(--surface-4);
    color: var(--text-primary);
}

.fdrop__box {
    flex: none;
    width: 14px;
    height: 14px;
    accent-color: var(--accent);
    cursor: pointer;
}

.fdrop__text {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.fdrop__option:has(.fdrop__box:checked) .fdrop__text {
    color: var(--text-primary);
}

.fdrop__foot {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border);
}

/* На узком экране четыре колонки превращаются в четыре обрезанных слова. */
@media (max-width: 640px) {
    .fdrop__list {
        grid-template-columns: repeat(2, 1fr);
    }

    .fdrop__search {
        width: 100%;
        min-width: 0;
    }
}

.fdrop__apply {
    background: var(--success);
    border-color: var(--success);
    color: var(--link-hover);
}

.fdrop__apply:hover {
    filter: brightness(1.12);
}

.filter-bar__submit {
    height: 32px;
}

/*
 * «Сбросить фильтр» стоит в строке сортировки сразу за выпадающим списком.
 * Вид у неё — контурная кнопка, а не текстовая ссылка: рядом стоит поле в
 * рамке, и голая надпись между ним и счётчиком читалась подписью, а не
 * действием. Акцентом при этом не заливается — сброс отменяет выбор, а
 * не подтверждает его.
 */
.filter-bar__reset {
    display: inline-flex;
    align-items: center;
    height: 28px;
    padding: 0 var(--space-3);
    background: var(--surface-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    white-space: nowrap;
    transition: background var(--transition-fast), border-color var(--transition-fast),
        color var(--transition-fast);
}

/*
 * Кнопка лежит в разметке всегда и прячется атрибутом `hidden` — и сервером
 * при пустом отборе, и `syncReset()` в живом режиме. Без этого правила она
 * не пряталась вовсе: собственный `display` компонента перебивает браузерное
 * `[hidden] { display: none }`, и «Сбросить фильтр» висело в строке
 * сортировки даже там, где сбрасывать нечего.
 */
.filter-bar__reset[hidden] {
    display: none;
}

.filter-bar__reset:hover {
    background: var(--surface-4);
    border-color: var(--accent);
    color: var(--accent);
}

.filter-bar__count {
    margin-left: auto;
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.filter-bar__count strong {
    color: var(--text-primary);
}

/* Затемнение результатов на время AJAX-обновления (ТЗ №3, п.161). */
.is-filter-loading {
    opacity: .45;
    pointer-events: none;
    transition: opacity var(--transition-base);
}

/* ── Шапка листинга ───────────────────────────────────────────────────────── */

/*
 * Контентная колонка — один лист, а не стопка отдельных панелей (референсы
 * 1.png и 2.png). Секции внутри разделены врезанной линией: тёмная сверху,
 * светлая снизу. Отсюда и запрет на собственный фон, рамку и скругление у
 * секции: два фона поверх друг друга дают тот самый «блочный» вид, которого
 * на референсе нет.
 *
 * Горизонтальные отступы задаёт секция, а не лист: у ленты карточек и у
 * панели плеера они разные.
 */
.content-column {
    background: var(--sheet-bg);
    border: 1px solid var(--sheet-edge);
    border-radius: var(--radius-sm);
    box-shadow: inset 0 0 0 1px var(--border);
    /* Разделители не должны выступать из-под скругления углов листа. */
    overflow: hidden;
}

/*
 * Рядом с сайдбаром колонка — правая половина общей фигуры: скругления слева
 * нет, стык рисует одна левая граница. Правило висит на сетке, а не на самой
 * колонке: на страницах без сайдбара (`--full`) лист снова круглый со всех
 * сторон.
 */
.layout-grid:not(.layout-grid--full) > .content-column {
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* На мобильном колонки идут друг под другом — лист снова круглый. */
@media (max-width: 768px) {
    .layout-grid:not(.layout-grid--full) > .content-column {
        border-radius: var(--radius-sm);
    }
}

/*
 * Ни одна секция не рисует свою панель поверх листа. Снимается здесь, у всех
 * прямых детей разом: иначе каждый новый блок внутренней страницы пришлось бы
 * править отдельно, а забытый оставался бы «карточкой в карточке».
 */
.content-column > * {
    margin: 0;
    padding: var(--space-4) var(--space-5);
    background: none;
    border: 0;
    border-radius: 0;
}

.content-column > * + * {
    border-top: 1px solid var(--sheet-rule);
    box-shadow: inset 0 1px 0 var(--border);
}

/* Секции с собственной внутренней раскладкой отступы задают сами. */
.content-column > .content-feed,
.content-column > .filter-bar,
.content-column > .feed-card {
    padding: 0;
}

.content-column > .filter-bar {
    padding: var(--space-4) var(--space-5);
}

.page-title {
    margin: 0;
    color: var(--text-primary);
    font-size: var(--font-size-h1);
    line-height: var(--line-height-tight);
}

.page-intro {
    margin: 0;
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-base);
}

/*
 * Разбивка страницы на соседние: «Топ 100 · Топ 1000 фильмов · Топ 1000
 * сериалов». Ряд плоских кнопок в тон панелям, текущая — акцентная. Это
 * навигация между готовыми страницами, а не фильтр: у каждой свой адрес,
 * свой canonical и своё содержимое.
 */
.page-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.page-tabs__item {
    padding: 5px var(--space-4);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.page-tabs__item:hover {
    background: var(--surface-3);
    color: var(--text-primary);
}

.page-tabs__item.is-current {
    background: var(--surface-3);
    border-color: var(--accent);
    color: var(--accent);
}

/* ── Лента карточек ───────────────────────────────────────────────────────── */

/*
 * Лента — не стопка отдельных карточек с промежутками, а сплошной список
 * внутри листа колонки: карточки разделены врезанной линией (1.png).
 */
.content-feed {
    display: flex;
    flex-direction: column;
}

.content-feed > * + * {
    border-top: 1px solid var(--sheet-rule);
    box-shadow: inset 0 1px 0 var(--border);
}

/*
 * Пустая выдача — это состояние страницы, а не ошибка: обычная панель с текстом,
 * без красных рамок и восклицательных знаков.
 */
.content-feed__empty {
    margin: 0;
    padding: var(--space-6) var(--space-5);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    text-align: center;
}

/*
 * Две раскладки одной карточки — обе сеткой по областям.
 *
 * Лента (7.png): заголовок во всю ширину, под ним постер, метаданные и бейджи
 * оценок, подвал снова во всю ширину.
 *
 * ТОП (8.png): постер стоит слева на всю высоту карточки, а заголовок,
 * описание и подвал идут одной колонкой справа от него.
 */
.feed-card {
    display: grid;
    grid-template-columns: var(--poster-feed-width) 1fr auto;
    grid-template-areas:
        "head   head head"
        "poster main marks"
        "footer footer footer";
    gap: var(--space-4) var(--space-5);
    padding: var(--space-4) var(--space-5) var(--space-5);
}

/*
 * Строка ТОПа (8.png): обложка слева, справа — название с местом, четыре
 * строки описания и подвал с кнопкой и оценками. Постер здесь заметно меньше
 * ленточного: строка обязана читаться списком мест, а не десятью карточками
 * в столбик.
 */
.feed-card--top {
    grid-template-columns: var(--poster-top-width) 1fr;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
        "poster head"
        "poster main"
        "poster footer";
}

.feed-card--top .feed-card__poster {
    /*
     * Без этого постер тянулся на всю высоту строки: элемент сетки по
     * умолчанию растягивается, и заданное на `.poster` `aspect-ratio`
     * проигрывало явной высоте ячейки. Обложка выходила 175×295 вместо 2:3
     * и занимала пол-экрана — это и есть «большие постеры» на /top/.
     */
    align-self: start;
}

/* Ширину держит сама обложка: обёртка — только ячейка сетки. */
.feed-card--top .feed-card__poster .poster {
    width: var(--poster-top-width);
}

/* На 8.png описание в строке ТОПа — ровно четыре строки. */
.feed-card--top .feed-card__description {
    -webkit-line-clamp: 4;
}

.feed-card__head {
    grid-area: head;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-5);
}

.feed-card__poster {
    grid-area: poster;
}

.feed-card__main {
    grid-area: main;
}

.feed-card__ratings {
    grid-area: marks;
}

.feed-card__footer {
    grid-area: footer;
}

.feed-card__title {
    font-size: var(--font-size-card-title);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
    /* Длинное название не должно рвать сетку (ТЗ №3, п.190). */
    overflow-wrap: anywhere;
}

.feed-card__title a {
    color: var(--text-primary);
}

.feed-card__title a:hover {
    color: var(--accent);
}

.feed-card__marks {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: none;
}

/*
 * Место в ТОПе. Три призовых места различаются оттенком молнии, дальше —
 * приглушённый: разница между первым и третьим осмысленна, между сороковым
 * и сорок первым — нет.
 */
.feed-card__rank {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 0 auto;
    flex: none;
    color: var(--text-secondary);
    font-size: var(--font-size-card-title);
    font-weight: var(--font-weight-bold);
    white-space: nowrap;
}

.feed-card__rank svg {
    color: var(--text-muted);
}

/* Цвета первых трёх мест — с 8.png: золото, лаванда, оранжевый. */
.feed-card__rank--1,
.feed-card__rank--1 svg {
    color: var(--top-rank-1);
}

.feed-card__rank--2,
.feed-card__rank--2 svg {
    color: var(--top-rank-2);
}

.feed-card__rank--3,
.feed-card__rank--3 svg {
    color: var(--top-rank-3);
}

/* Оценка зрителей сайта — зелёный бейдж: другая шкала, другой цвет. */
.feed-card__site-rating {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 38px;
    padding: var(--space-1) var(--space-2);
    background: var(--success);
    border-radius: var(--radius-sm);
    color: var(--link-hover);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-bold);
    font-variant-numeric: tabular-nums;
}

/*
 * Закладка (1.png) — сама лента и есть кнопка: ни подложки, ни рамки вокруг
 * неё нет, иначе в углу карточки появляется второй прямоугольник рядом с
 * блоком оценки. `currentColor` красит только полотно ленты, галочка и
 * перфорация берут свои цвета сами — на референсе они светлее фона ленты.
 */
.favorite-toggle {
    display: inline-flex;
    align-items: flex-start;
    justify-content: center;
    padding: 0;
    background: none;
    border: 0;
    color: var(--ribbon-bookmark);
    cursor: pointer;
    transition: color var(--transition-fast);
}

.favorite-toggle__ribbon {
    fill: currentColor;
}

.favorite-toggle__perforation {
    fill: none;
    stroke: var(--ribbon-bookmark-edge);
    stroke-width: 2.4;
    stroke-dasharray: 1.6 1.7;
}

.favorite-toggle__check {
    fill: none;
    stroke: var(--link-hover);
    stroke-width: 2.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.favorite-toggle:hover {
    color: var(--accent);
}

.favorite-toggle:hover .favorite-toggle__check {
    stroke: var(--text-on-accent);
}

.favorite-toggle[aria-pressed="true"] {
    color: var(--success);
}

.favorite-toggle[aria-pressed="true"] .favorite-toggle__check {
    stroke: var(--link-hover);
}

.feed-card__description {
    display: -webkit-box;
    overflow: hidden;
    color: var(--text-secondary);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 5;
}

/* В ленте описание идёт после таблицы метаданных, в ТОПе — сразу под заголовком. */
.meta-list + .feed-card__description {
    margin-top: var(--space-4);
}

/* ── Подвал карточки: просмотр слева, дата или оценки справа ──────────────── */

.feed-card__footer {
    display: flex;
    align-items: center;
    gap: var(--space-5);
}

/*
 * «Смотреть онлайн» стоит ровно под обложкой и повторяет её ширину: подвал —
 * продолжение колонки постера, и кнопка обязана держать ту же вертикаль, что
 * и картинка над ней. Ширина берётся из того же токена, что и сам постер, —
 * два разных числа для одной колонки разъедутся при первой же правке.
 */
.feed-card__watch {
    flex: none;
    width: var(--poster-feed-width);
    height: 28px;
    padding: 0 var(--space-4);
    font-size: var(--font-size-sm);
}

/* Правый край подвала: `auto` слева, чтобы дата прижималась к краю карточки. */
.feed-card__date {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.feed-card__date svg {
    color: var(--text-muted);
}

.feed-card__footer .rating-badges--text {
    margin-right: auto;
}

.feed-card__footer .feed-card__site-rating {
    margin-left: auto;
}

/* ── Список метаданных (лента и внутренняя страница) ──────────────────────── */

.meta-list {
    display: flex;
    flex-direction: column;
    gap: 3px;
    font-size: var(--font-size-meta);
}

/*
 * Строка метаданных — не таблица в две колонки: на референсе 7.png значение
 * идёт сразу за подписью, поэтому колонка фиксированной ширины и убрана.
 * Подпись при этом заметно жирнее и светлее значения — это она отвечает на
 * вопрос «что это за число».
 */
.meta-list__row {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--space-2);
    line-height: var(--line-height-tight);
}

.meta-list__label {
    flex: none;
    color: var(--text-primary);
    font-weight: var(--font-weight-bold);
}

/*
 * Выделенная строка (качество, озвучки) — 2.png: подпись оранжевая, а сама
 * строка отбита от паспортных пустой строкой. Отступ ставится только у первой
 * выделенной подряд, иначе между «Качеством» и «Озвучками» появится вторая
 * щель, которой на референсе нет.
 */
.meta-list__row--accent .meta-list__label {
    color: var(--accent);
}

.meta-list__row:not(.meta-list__row--accent) + .meta-list__row--accent {
    margin-top: var(--space-3);
}

.meta-list__value {
    margin: 0;
    min-width: 0;
    color: var(--text-secondary);
}

.meta-list__link {
    color: var(--link);
}

.meta-list__link:hover {
    color: var(--accent);
}

.meta-list__sep {
    color: var(--text-muted);
}

/* ── Пагинация ────────────────────────────────────────────────────────────── */

.pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    padding: var(--space-3);
}

.pagination__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 26px;
    padding: 0 var(--space-2);
    background: var(--surface-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.pagination__link:hover {
    border-color: var(--accent);
    color: var(--link-hover);
}

.pagination__link.is-current {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
    font-weight: var(--font-weight-semibold);
}

.pagination__gap {
    color: var(--text-muted);
    padding: 0 var(--space-1);
}

/* ── SEO-текст под каталогом ──────────────────────────────────────────────── */

.seo-text {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-5);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-loose);
}

.seo-text__heading {
    color: var(--text-primary);
    font-size: var(--font-size-section);
}

/* ── Чипы: сезоны, серии, теги ────────────────────────────────────────────── */

.chip {
    display: inline-flex;
    align-items: center;
    height: 26px;
    padding: 0 var(--space-3);
    background: var(--surface-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

.chip:hover {
    border-color: var(--accent);
    color: var(--link-hover);
}

.chip.is-current {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
    font-weight: var(--font-weight-semibold);
}

.chip--new::after {
    content: "•";
    margin-left: var(--space-1);
    color: var(--accent-yellow);
}

/* ── Состояния загрузки ───────────────────────────────────────────────────── */

.spinner {
    display: inline-block;
    width: 22px;
    height: 22px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: var(--radius-avatar);
    animation: spinner-rotate .8s linear infinite;
}

@keyframes spinner-rotate {
    to {
        transform: rotate(360deg);
    }
}

.skeleton {
    background: var(--surface-3);
    border-radius: var(--radius-sm);
    animation: skeleton-pulse 1.4s ease-in-out infinite;
}

@keyframes skeleton-pulse {
    50% {
        opacity: .55;
    }
}

/* ── Тосты ────────────────────────────────────────────────────────────────── */

.toast-stack {
    position: fixed;
    right: 18px;
    bottom: 62px;
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: 320px;
}

/*
 * Уведомление — такая же серо-чёрная панель, как блоки сайдбара (замечание
 * заказчика): подложка `--surface-2`, тонкая рамка, мелкий uppercase-заголовок
 * и метка типа слева. Цветом различается только метка и полоса слева: заливать
 * зелёным или красным весь блок значило бы завести на сайте вторую палитру.
 */
.toast {
    --toast-tone: var(--accent);

    display: grid;
    grid-template-columns: 20px minmax(0, 1fr) 16px;
    gap: var(--space-3);
    align-items: start;
    padding: var(--space-3) var(--space-4);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-left: 2px solid var(--toast-tone);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-1);
    color: var(--text-primary);
    animation: toast-in .18s ease-out;
}

.toast__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: 1px solid var(--toast-tone);
    border-radius: var(--radius-sm);
    color: var(--toast-tone);
}

.toast__body {
    min-width: 0;
}

.toast__title {
    margin: 0;
    color: var(--toast-tone);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: .06em;
    text-transform: uppercase;
}

.toast__text {
    margin: 2px 0 0;
    font-size: var(--font-size-sm);
    line-height: var(--line-height-base);
    word-break: break-word;
}

.toast__close {
    padding: 0;
    background: none;
    border: 0;
    color: var(--text-muted);
    line-height: 0;
    cursor: pointer;
}

.toast__close:hover {
    color: var(--text-primary);
}

.toast--success {
    --toast-tone: var(--success);
}

.toast--error {
    --toast-tone: var(--danger);
}

.toast--info {
    --toast-tone: var(--accent);
}

@keyframes toast-in {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
}

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

/* ── Модальные окна ───────────────────────────────────────────────────────── */

.modal {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5);
    background: rgba(0, 0, 0, .6);
}

.modal[hidden] {
    display: none;
}

.modal__dialog {
    width: 100%;
    max-width: 380px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-1);
}

.modal__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--panel-padding);
    border-bottom: 1px solid var(--border);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
}

.modal__close {
    padding: 0 var(--space-2);
    background: none;
    border: 0;
    color: var(--text-muted);
    font-size: var(--font-size-md);
    cursor: pointer;
}

.modal__close:hover {
    color: var(--link-hover);
}

.modal__body {
    padding: var(--space-5);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.modal__actions {
    display: flex;
    gap: var(--space-3);
    padding: 0 var(--space-5) var(--space-5);
}

/* ── Адаптив компонентов ──────────────────────────────────────────────────── */

@media (max-width: 1100px) {
    .feed-card {
        gap: var(--space-3) var(--space-4);
    }
}

@media (max-width: 768px) {
    /* Лента: постер слева, оценки уезжают под метаданные (ТЗ №3, п.141). */
    .feed-card {
        grid-template-columns: var(--poster-feed-width) 1fr;
        grid-template-areas:
            "head   head"
            "poster main"
            "marks  marks"
            "footer footer";
    }

    .rating-badges--stack {
        flex-direction: row;
    }

    .feed-card__description {
        -webkit-line-clamp: 3;
    }

    /* Фильтры на узком экране всё-таки переносятся: шесть групп в ряд нечитаемы. */
    .filter-bar__row--filters {
        flex-wrap: wrap;
    }

    .filter-bar__row--filters .filter-bar__group,
    .filter-bar__row--filters .fdrop {
        flex: 1 1 45%;
    }

    .sidebar-nav__columns,
    .upcoming__grid {
        grid-template-columns: 1fr;
    }

    /*
     * В одну колонку правая половина панели встаёт первой: на телефоне
     * сайдбар — основной способ навигации, и начинаться он обязан
     * «Категориями», а не одиннадцатью жанрами перед ними.
     */
    .sidebar-nav__column:last-child {
        order: -1;
    }

    /*
     * Плотность сайдбара — десктопная: на референсе это колонка, которую
     * читают глазами, а не пальцем. На телефоне тот же сайдбар раскрывается
     * кнопкой «Каталог и фильтры» и становится основным способом навигации,
     * а строки в нём высотой 17px промахиваются мимо пальца. Расстояние между
     * строками растёт до пригодного для касания, шрифт остаётся прежним:
     * увеличивать заодно и его значило бы переверстать панель целиком.
     */
    .link-list__item {
        min-height: 30px;
        align-items: center;
    }

    .link-list__link {
        padding: var(--space-2) 0;
    }

    .carousel__arrow {
        display: none;
    }

    .filter-bar__count {
        margin-left: 0;
        width: 100%;
    }

    .toast-stack {
        right: var(--gap);
        left: var(--gap);
        max-width: none;
    }
}

@media (max-width: 480px) {
    /* Раскладка «постер слева + метаданные справа» сохраняется: постер шириной
       120–130px читается, а колонка в одну ширину экрана выглядела бы пустой
       (ТЗ №3, п.141 — крошечный постер запрещён). */
    .feed-card {
        gap: var(--space-3);
        padding: var(--space-3);
    }

    .feed-card__watch {
        padding: 0 var(--space-3);
    }
}

/* ── Личные действия зрителя: «В список» и «Следить» ─────────────────────── */

/*
 * Общая база для кнопок личных действий (ТЗ №10, п.39, 61). Раньше «В список»
 * был `<summary>` с полями 2px, а «Следить» — кнопкой в 22px: рядом они стояли
 * разной высоты и с разным скруглением, и полоса под плеером выглядела
 * собранной из чужих деталей. Метрика теперь одна на обе.
 */
.action-button {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    height: 26px;
    padding: 0 var(--space-4);
    background: var(--surface-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--font-size-sm);
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    transition: background var(--transition-fast), color var(--transition-fast),
        border-color var(--transition-fast);
}

.action-button:hover {
    background: var(--surface-4);
    border-color: var(--accent);
    color: var(--accent);
}

.action-button__icon {
    flex: none;
    /* Значок держит тон подписи и не спорит с ней за внимание. */
    opacity: 0.85;
}

.subscribe-button.is-active {
    border-color: var(--success);
    color: var(--success);
}

.subscribe-button.is-active:hover {
    border-color: var(--success);
    color: var(--success);
}

/* Бейдж непрочитанных в шапке. Круглым остаётся только аватар, поэтому
   у бейджа обычное мелкое скругление. */
.account-links__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 16px;
    padding: 0 var(--space-2);
    background: var(--accent);
    border-radius: var(--radius-xs);
    color: #1a1204;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: 1;
}

.account-links__badge:hover {
    background: var(--accent-hover);
    color: #1a1204;
}

/* `display` из правила выше перебивает браузерное `[hidden] { display: none }`,
   и бейдж показывал бы «0» при нуле непрочитанных. */
.account-links__badge[hidden] {
    display: none;
}

/* ── Личные модули главной (ТЗ №8, п.149, 154–155, 254, 369) ─────────────── */

/* Каркас приходит с сервера пустым и одинаковым для всех, содержимое
   подставляет personal.js. Пока рядов нет, обёртка скрыта и не занимает
   места — иначе главная «прыгала» бы на каждой загрузке. */
.personal-modules {
    display: grid;
    gap: var(--gap);
    margin-bottom: var(--gap);
}

.personal-modules[hidden],
.personal-row[hidden] {
    display: none;
}

.personal-row__grid {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.personal-card {
    position: relative;
}

.personal-card__link {
    display: block;
    color: var(--link);
}

.personal-card__title {
    display: -webkit-box;
    overflow: hidden;
    margin-top: var(--space-2);
    color: var(--text-primary);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}

.personal-card__link:hover .personal-card__title {
    color: var(--accent);
}

.personal-card__note {
    display: block;
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* «S2E5» поверх постера (п.154). */
.personal-card__badge {
    position: absolute;
    top: var(--space-2);
    left: var(--space-2);
    padding: 1px var(--space-2);
    background: rgba(0, 0, 0, .72);
    border-radius: var(--radius-xs);
    color: var(--text-primary);
    font-size: var(--font-size-xs);
    line-height: 1.5;
}

/* Прогресс просмотра прижат к низу постера, а не к низу карточки:
   под постером идёт название, и полоса там читалась бы как подчёркивание. */
.personal-card__progress {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    height: 3px;
    background: rgba(0, 0, 0, .55);
}

.personal-card__progress-bar {
    display: block;
    height: 100%;
    background: var(--accent);
}

.personal-card__hide {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    width: 20px;
    height: 20px;
    padding: 0;
    background: rgba(0, 0, 0, .72);
    border: 0;
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-size: 15px;
    line-height: 20px;
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease, color .12s ease;
}

.personal-card:hover .personal-card__hide,
.personal-card__hide:focus-visible {
    opacity: 1;
}

.personal-card__hide:hover {
    color: var(--text-primary);
}

@media (max-width: 1100px) {
    .personal-row__grid {
        grid-template-columns: repeat(5, 1fr);
    }
}

@media (max-width: 768px) {
    .personal-row__grid {
        grid-template-columns: repeat(3, 1fr);
    }

    /* На тач-экранах наведения нет — кнопка обязана быть видна сразу. */
    .personal-card__hide {
        opacity: 1;
    }
}

@media (max-width: 480px) {
    .personal-row__grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ── Примитивы форм: поле, подпись, подсказка, галочка ────────────────────── */

/*
 * Живут здесь, а не в `auth.css`: те же поля стоят в формах обращений на
 * правовых страницах и в кабинете, а `auth.css` подключается только на входе
 * и регистрации. Из-за этого форма на `/pravoobladatelyam/` и `/contacts/`
 * оставалась без стилей — с системным чекбоксом и системным выпадающим
 * списком, единственными светлыми элементами на тёмной странице.
 */
.auth-field {
    margin-bottom: var(--space-5);
    /* Поле и его подсказка — один блок: в сетке из двух колонок подписи
       под соседними полями обязаны начинаться на одной высоте. */
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.auth-field__label {
    display: block;
    margin-bottom: var(--space-2);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.auth-field__input {
    padding: 8px 10px;
    font-size: var(--font-size-base);
}

.auth-field--invalid .auth-field__input {
    border-color: var(--danger);
}

.auth-field__hint {
    margin: var(--space-2) 0 0;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: 1.45;
}

.auth-field__errors {
    margin: var(--space-2) 0 0;
    padding: 0;
    list-style: none;
    color: var(--danger);
    font-size: var(--font-size-sm);
    line-height: 1.45;
}

/*
 * Выпадающий список. Системный вид убирается целиком: браузер рисует его
 * светлым и своей стрелкой, и на тёмной странице такой список выглядел
 * вставкой из чужого интерфейса. Стрелка — фоновая картинка в цвете подписи.
 */
select.field {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 28px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 5'%3E%3Cpath d='M0 0h8L4 5z' fill='%23a9a9a9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 8px 5px;
    cursor: pointer;
}

/* Список раскрывается силами браузера, но его строки наследуют наши тона. */
select.field option {
    background: var(--surface-2);
    color: var(--text-primary);
}

/* ── Оформленный список (select.js) ───────────────────────────────────────── */

/*
 * Виджет поверх нативного `<select>` на страницах обращений (замечание
 * заказчика). Пока JS не отработал, виден обычный список — он же остаётся
 * носителем значения, поэтому убирается с глаз только после того, как виджет
 * построен (класс вешает скрипт).
 *
 * Раскрытая панель повторяет `fdrop` из строки фильтров: те же подложка,
 * рамка и тень. Двух разных выпадающих списков на сайте быть не должно.
 */
.kselect__native {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
}

.kselect {
    position: relative;
}

.kselect__toggle {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: 6px 10px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-size: var(--font-size-sm);
    text-align: left;
    cursor: pointer;
}

.kselect.is-open .kselect__toggle,
.kselect__toggle:focus-visible {
    border-color: var(--accent);
    outline: none;
}

/* Не выбрано — подпись читается как подсказка, а не как значение. */
.kselect.is-empty .kselect__label {
    color: var(--text-muted);
}

.kselect__label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kselect__arrow {
    flex: none;
    color: var(--text-secondary);
    transition: transform var(--transition-fast);
}

.kselect.is-open .kselect__arrow {
    transform: rotate(180deg);
}

/*
 * `display: flex` ниже перебивает браузерное `[hidden] { display: none }` —
 * правило браузера слабее любого нашего селектора с классом, и без этой
 * строки список стоял бы раскрытым всегда. Атрибут в селекторе поднимает вес
 * выше `.kselect__list`, поэтому отдельного класса-состояния не нужно:
 * скрытость несёт сам `hidden`, который ставит скрипт.
 */
.kselect__list[hidden] {
    display: none;
}

.kselect__list {
    position: absolute;
    z-index: var(--z-nav);
    top: calc(100% + var(--space-2));
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    max-height: 260px;
    padding: var(--space-2);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-1);
    overflow-y: auto;
}

.kselect__option {
    padding: 6px var(--space-3);
    background: none;
    border: 0;
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    text-align: left;
    cursor: pointer;
}

.kselect__option:hover {
    background: var(--surface-3);
    color: var(--text-primary);
}

.kselect__option.is-current {
    background: var(--surface-3);
    color: var(--accent);
}

.kselect__option--empty {
    color: var(--text-muted);
}

/* ── Поле выбора файла (file-field.js) ────────────────────────────────────── */

/*
 * Пока скрипт не отработал, видно нативное поле: подложка, рамка и кнопка
 * покрашены под сайт, подпись остаётся браузерной. После сборки виджета
 * поле уезжает с глаз (`kfile__native`), а на его месте стоят наша кнопка
 * и имя файла.
 */
input[type="file"] {
    max-width: 100%;
    padding: 5px 10px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--font-size-sm);
}

input[type="file"]::file-selector-button {
    margin-right: var(--space-3);
    padding: 4px 12px;
    background: var(--surface-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
}

input[type="file"]::file-selector-button:hover {
    background: var(--surface-4);
    border-color: var(--border-strong);
    color: var(--accent);
}

.kfile {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    max-width: 100%;
    padding: 4px 6px 4px 4px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
}

.kfile.is-focused {
    border-color: var(--accent);
}

.kfile__native {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
}

.kfile__button {
    flex: none;
    height: 24px;
    padding: 0 var(--space-4);
    background: var(--surface-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.kfile__button:hover {
    background: var(--surface-4);
    border-color: var(--border-strong);
    color: var(--accent);
}

/* Имя файла не растягивает поле: длинное обрезается многоточием. */
.kfile__name {
    min-width: 0;
    max-width: 180px;
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kfile.is-filled .kfile__name {
    color: var(--text-primary);
}

/* ── Галочка ──────────────────────────────────────────────────────────────── */

.auth-check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    cursor: pointer;
}

/*
 * Своя галочка вместо системной: `accent-color` красит только заливку, а
 * рамка, размер и радиус остаются браузерными — на macOS и в Windows это
 * два разных элемента. Здесь квадрат 15px со скруглением 3px, как у кнопок.
 */
.auth-check__input {
    appearance: none;
    -webkit-appearance: none;
    flex: none;
    width: 15px;
    height: 15px;
    /* Подпись идёт в 1.45 строки: без сдвига квадрат встаёт выше первой строки. */
    margin: 2px 0 0;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-xs);
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.auth-check__input:hover {
    border-color: var(--accent);
}

.auth-check__input:checked {
    background: var(--accent);
    border-color: var(--accent);
    /* Птичка — маской, чтобы не тянуть отдельный файл и не зависеть от шрифта. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M4.6 9.4L1.2 6l1.2-1.2 2.2 2.2 5-5L10.8 3z' fill='%23151719'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 11px 11px;
}

.auth-check__input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.auth-check__label {
    color: var(--text-secondary);
    font-size: var(--font-size-base);
    line-height: 1.45;
}
