:root {
    --bg-color: #0d1117;
    --card-bg: #161b22;
    --card-border: #30363d;
    --text-primary: #f0f6fc;
    --text-secondary: #8b949e;
    --accent-red: #e50914;
    --accent-orange: #ff9800;
    --dislike-color: #f85149;
    --watched-color: #58a6ff;
    --watchlist-color: #2ea043;

    /* Нижнее меню и полоса жестов.
       Высота меню в одном месте: от неё считаются отступы снизу у всех
       экранов, иначе содержимое уезжает под меню — что и случилось
       на телефоне с кнопкой «Начать подбор». */
    --nav-height: 70px;
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --nav-space: calc(var(--nav-height) + var(--safe-bottom));

    /* ---------- Шаг отступов ----------
       До этого отступы назначались в каждом правиле на глаз: 4, 6, 8, 10,
       12, 13, 14, 16, 18, 20, 24. Глазу разница между 13 и 14 незаметна,
       а вот общее ощущение «собрано на глаз» складывается именно из неё.
       Шесть значений на весь сайт — больше не нужно. */
    --gap-1: 4px;
    --gap-2: 8px;
    --gap-3: 12px;
    --gap-4: 16px;
    --gap-5: 24px;
    --gap-6: 32px;


    /* ---------- Кегль ----------
       Было тридцать пять разных размеров на сто тридцать пять правил,
       и в диапазоне 11–15 px их жило четырнадцать: 11.2, 11.5, 11.8,
       12.0, 12.2, 12.5, 12.8, 13.1, 13.4, 13.6, 13.8, 14.1, 14.4, 14.7.
       Глаз этой разницы не видит, а ощущение «набрано на глаз»
       складывается именно из неё — ровно как было с отступами.

       Девять ступеней. Каждая стоит на самом частом значении своего
       участка, поэтому большинство правил переехало без сдвига,
       а остальные — на пиксель-полтора.

       Исключение одно: буква в кружке партнёра. Там кегль привязан
       к размеру кружка, а не к тексту, и шкала к нему отношения
       не имеет. */
    --fs-0: .66rem;    /* 10.5 px — служебная мелочь: правовая сноска, метка партнёра */
    --fs-1: .72rem;    /* 11.5 px — чип, плашка, подпись под значком */
    --fs-2: .78rem;    /* 12.5 px — пояснение, жанр, описание в карточке */
    --fs-3: .88rem;    /* 14 px — вторичный текст, кнопка, поле */
    --fs-4: 1rem;      /* 16 px — основной текст */
    --fs-5: 1.15rem;   /* 18.4 px — подзаголовок, заголовок блока */
    --fs-6: 1.4rem;    /* 22.4 px — заголовок экрана, реплика кота */
    --fs-7: 1.6rem;    /* 25.6 px — название в карточке, крупная цифра */
    --fs-8: 2rem;      /* 32 px — самое крупное: крестик листа, шкала оценки */

    /* ---------- Радиусы ----------
       Было пять разных: 8, 10, 12, 14, 16, 20, 24. Стало три, и каждый
       отвечает за свой размер объекта: чип, карточка, окно. */
    --r-sm: 10px;
    --r-md: 16px;
    --r-lg: 24px;

    /* ---------- Тени ----------
       Было восемь разных. Две: одна поднимает карточку над фоном,
       вторая — мелкий элемент над карточкой. */
    --shadow-card: 0 12px 32px rgba(0, 0, 0, .6);
    --shadow-pop: 0 6px 20px rgba(0, 0, 0, .5);

    /* ---------- Подложка поверх картинки ----------
       Заменила `backdrop-filter: blur()` у чипов, оценки и штампов.
       Размытие — самая дорогая вещь в CSS на телефоне: браузер отдельно
       размывает то, что под элементом, и делает это заново на каждом
       кадре движения карточки. Таких элементов в колоде жило двенадцать
       штук разом. Плотная тёмная заливка на постере выглядит так же,
       а стоит один слой заливки. */
    --scrim: rgba(9, 12, 18, .88);
}

/* Тёмная тема единственная и светлой не планируется.
   Сказать об этом браузеру нужно явно: иначе он подставляет светлые
   умолчания полям ввода, полосам прокрутки и системным меню, и они
   светятся белым посреди тёмного экрана. */
:root { color-scheme: dark; }

/* Плавное раскрытие <details> — сезоны, спецвыпуски, разделы профиля.
   Раскрывались рывком: содержимое появлялось целиком в один кадр,
   и на длинном списке серий страница прыгала под пальцем.

   Делается без единой строки скрипта: `interpolate-size` разрешает
   анимировать переход к `auto`, а `::details-content` — это и есть
   содержимое раскрывающегося блока. Где этого нет (Safari, Firefox),
   блок раскрывается как раньше, мгновенно, — ничего не ломается
   и ничего не исчезает. */
:root { interpolate-size: allow-keywords; }

.collapse::details-content,
.season-block::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size .24s ease, content-visibility .24s allow-discrete;
}
.collapse[open]::details-content,
.season-block[open]::details-content { block-size: auto; }

@media (prefers-reduced-motion: reduce) {
    .collapse::details-content,
    .season-block::details-content { transition: none; }
}

* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; -webkit-user-select: none; }

body {
    background-color: var(--bg-color);
    color: var(--text-primary);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    /* Высота вьюпорта: три строки подряд, каждая перекрывает предыдущую
       там, где поддерживается.

       100vh на телефоне — это высота БЕЗ панелей браузера, то есть больше
       видимой области, пока адресная строка на экране: низ приложения
       уезжал за край. 100dvh это чинил, но принёс своё: dvh — ДИНАМИЧЕСКАЯ
       единица, она меняется каждый раз, когда браузер прячет или
       показывает адресную строку. На свайпах строка то уезжает, то
       возвращается — и вместе с ней прыгает всё, что привязано к низу:
       подсказка жестов, нижнее меню, плашки.

       100svh — высота при ПОКАЗАННЫХ панелях. Она не меняется вообще
       никогда, и это ровно то, что нужно экрану без прокрутки: низ стоит
       на месте, за край ничего не уезжает. Плата — полоска фона там, где
       была спрятанная панель, но фон у нас один на всё приложение. */
    height: 100vh;
    height: 100dvh;
    height: 100svh;
    display: flex;
    flex-direction: column;
    align-items: center;
    overflow: hidden;
    /* Жест, начатый на пустом месте, не должен уходить браузеру: именно
       так он и прячет адресную строку, а с ней сдвигает низ экрана */
    overscroll-behavior: none;
}

/* Экран подбора прокручивать нечего — там одна карточка на всю площадь.
   Отдавая браузеру вертикальные жесты, мы получали ровно то, ради чего
   их и перехватываем: спрятанную адресную строку и прыгающий низ.
   На остальных экранах прокрутка настоящая, и там touch-action не трогаем. */
#screenDeck { touch-action: none; }

/* ================= Header ================= */
/* Шапка — это чистая рамка: на телефоне каждый её пиксель отнимается
   у постера. Кнопка фильтров при этом остаётся 40×40 — палец меньше
   не нажмёт, и ровно в высоту шапки она и укладывается.
   Высота задана числом, а не отступами вокруг текста, по двум причинам:
   она не поедет от шрифта в чужой системе, и — главное — не даст дробной
   высоты. Дробная превращалась в щель толщиной в пиксель под шапкой,
   сквозь которую на экране списков просвечивали ползущие карточки.
   Черта снизу отделяет рамку приложения от содержимого: без неё заголовок
   экрана слипался с логотипом. */
.header { width: 100%; max-width: 420px; height: 44px; padding: 0 16px; display: flex; justify-content: space-between; align-items: center; z-index: 10; flex-shrink: 0; background: var(--bg-color); border-bottom: 1px solid var(--card-border); }
.logo { font-size: var(--fs-6); font-weight: 900; letter-spacing: -0.4px; color: #ffffff; line-height: 1; }
.logo span { color: var(--accent-red); }
.logo-sm { font-size: var(--fs-5); }
.btn-filter { position: relative; background: none; border: none; color: var(--text-primary); cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 8px; border-radius: 50%; transition: background 0.2s; }
.btn-filter svg { width: 24px; height: 24px; display: block; }
.btn-filter:active { background: rgba(255,255,255,0.1); }
.btn-filter[hidden] { display: none; }
.filter-dot { position: absolute; top: 6px; right: 6px; width: 9px; height: 9px; border-radius: 50%; background: var(--accent-red); border: 2px solid var(--bg-color); }

/* ================= Экраны ================= */
.app-screen { width: 100%; max-width: 420px; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.app-screen[hidden] { display: none; }
/* Верхний отступ здесь обязан быть нулевым.
   `position: sticky; top: 0` прилипает к КОНТЕНТНОЙ кромке контейнера, то
   есть ниже его padding-top. Восемь пикселей отступа превращались в щель,
   сквозь которую ползли карточки над прилипшей шапкой списков. Отступ
   сверху даёт себе сам первый блок экрана — см. .lists-tabs, .search-bar
   и .screen-pad-top. */
.screen-scroll { overflow-y: auto; padding: 0 16px calc(var(--nav-space) + 20px); gap: 14px; }
.screen-pad-top { padding-top: 14px; }

/* Экран — колонка флексов, а флексы по умолчанию ужимаются. В прокручиваемом
   контейнере это приводит к тому, чего никто не ожидает: блок не выходит
   за край, а сплющивается. Раскрытый список жанров из-за этого обрезался
   ровно по высоте экрана, а сетка списков схлопывалась и ломала прокрутку.
   В прокрутке ужиматься нельзя ничему. */
.screen-scroll > * { flex-shrink: 0; }
.screen-title { font-size: var(--fs-6); font-weight: 800; }

/* Шапка профиля: морда крупно рядом с заголовком. Самое заметное место
   экрана, и до сих пор в нём стояло одно слово. */
.profile-head { display: flex; align-items: center; gap: var(--gap-3); }
.profile-face {
    width: 104px; height: 104px; flex-shrink: 0; display: block;
    filter: drop-shadow(0 8px 18px rgba(255, 152, 0, .16));
}
.screen-subtitle { font-size: var(--fs-4); font-weight: 700; margin-top: 8px; }
.screen-sub { color: var(--text-secondary); font-size: var(--fs-3); }

/* ================= Deck ================= */
.deck-container { position: relative; width: 100%; max-width: 380px; flex: 1; min-height: 0; margin: 10px auto 0; display: flex; justify-content: center; align-items: center; }

.movie-card {
    position: absolute; inset: 0;
    background-color: var(--card-bg);
    border-radius: var(--r-lg);
    border: 2px solid var(--card-border);
    overflow: hidden;
    box-shadow: var(--shadow-card);
    display: flex; flex-direction: column; justify-content: space-between;
    touch-action: none;
    /* `box-shadow` из перехода убран намеренно. Тень с радиусом 34 px
       перерисовывалась 220 мс подряд по всей площади карточки — а тень,
       в отличие от сдвига, считается процессором, а не видеокартой.
       Свечение по направлению свайпа теперь появляется сразу вместе
       с цветом рамки; на глаз разницы нет, на телефоне она есть. */
    transition: transform 0.22s ease, opacity 0.22s ease, border-color 0.15s ease;
    cursor: grab;
}
.movie-card:active { cursor: grabbing; }
.movie-card[data-dir="watchlist"] { border-color: var(--watchlist-color); box-shadow: 0 0 34px rgba(46, 160, 67, 0.45); }
.movie-card[data-dir="disliked"]  { border-color: var(--dislike-color);  box-shadow: 0 0 34px rgba(248, 81, 73, 0.45); }
.movie-card[data-dir="watched"]   { border-color: var(--watched-color);  box-shadow: 0 0 34px rgba(88, 166, 255, 0.45); }

.poster-img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Цветная заливка по направлению свайпа */
.tint-layer { position: absolute; inset: 0; opacity: 0; pointer-events: none; z-index: 2; transition: opacity 0.12s linear; }

/* Крупные штампы направления.
   Штамп стоит на ОТСТАЮЩЕЙ стороне карточки: ведёшь вправо — «Буду
   смотреть» слева. Так он остаётся на экране, пока карточка уезжает;
   на ведущей стороне его срезало бы краем уже на половине жеста.
   Что изменилось: размер и высота. Раньше штампы жались под самую шапку
   карточки, а на телефоне взгляд держится на середине постера — туда они
   и переехали, заодно подросли вдвое. Это и была «малозаметность». */
.swipe-badge {
    position: absolute; z-index: 5;
    padding: 12px 18px;
    border: 4px solid; border-radius: var(--r-md);
    font-weight: 900; font-size: var(--fs-5); letter-spacing: 0.4px;
    background: var(--scrim);
    box-shadow: var(--shadow-pop);
    opacity: 0; pointer-events: none; white-space: nowrap;
}
.badge-watchlist { top: 42%; left: 18px;  transform: rotate(-11deg); color: var(--watchlist-color); border-color: var(--watchlist-color); }
.badge-disliked  { top: 42%; right: 18px; transform: rotate(11deg);  color: var(--dislike-color);  border-color: var(--dislike-color); }
.badge-watched   { bottom: 26%; left: 50%; transform: translateX(-50%) rotate(-3deg); color: var(--watched-color); border-color: var(--watched-color); }

/* Нижняя карточка стопки: она нужна, только чтобы под уходящей не зияла
   дыра, поэтому чуть меньше, приглушена и не ловит касания */
/* Приглушение сделано прозрачностью, а не `filter: brightness()`.
   Фильтр заставлял браузер 240 мс перерисовывать всю нижнюю карточку
   целиком; прозрачность меняет видеокарта, не трогая пиксели.
   Вид тот же: под карточкой всё равно тёмный фон приложения. */
.movie-card.is-behind {
    transform: scale(.955) translateY(8px);
    opacity: .66;
    pointer-events: none;
    transition: transform .24s ease, opacity .24s ease;
}
/* Пока карточку тащат, браузер предупреждён, что двигать будут именно
   сдвиг: он выносит её на отдельный слой заранее, а не на первом кадре.
   Снимается вместе с классом — постоянный `will-change` держит слой
   в памяти впустую. */
.movie-card.is-dragging { transition: none; will-change: transform; }
/* Улетающая карточка: ещё видна, но уже ничья */
.movie-card.is-gone { pointer-events: none; }

/* Оценка — такой же чип, как «Сериал» и «18+», только с акцентным
   цветом. Раньше это был отдельный компонент: другой радиус, другой
   фон, другая высота. Три элемента одного ранга в одном ряду выглядели
   как из трёх разных макетов. */
.rating-badge {
    position: absolute; top: var(--gap-4); right: var(--gap-4); z-index: 4;
    background: var(--scrim); border: 1px solid rgba(255,152,0,.45);
    color: var(--accent-orange); font-weight: 700; font-size: var(--fs-1);
    padding: 5px 10px; border-radius: var(--r-sm); line-height: 1.35;
}

.card-info { padding: var(--gap-4) var(--gap-5) var(--gap-5); display: flex; flex-direction: column; gap: var(--gap-2); background: linear-gradient(180deg, rgba(22, 27, 34, 0.9) 0%, var(--card-bg) 100%); height: 25%; justify-content: flex-end; }
.movie-title { font-size: var(--fs-6); font-weight: 700; line-height: 1.2; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.movie-meta { font-size: var(--fs-3); color: var(--text-secondary); }
.genre-tags { display: flex; flex-wrap: wrap; gap: var(--gap-2); margin-top: var(--gap-1); }
/* Жанр и описание одного кегля, различаются весом и плашкой.
   Было наоборот: жанры .8rem (12.8 px) мельче описания .82rem (13.1 px)
   — то есть то, по чему человек решает за полсекунды, набрано мельче
   синопсиса, который дочитывают редко. Критерий карточки — «помогает
   решить, включать ли сегодня», и по нему порядок был перевёрнут.
   Радиус --r-sm: 8 px был четвёртым радиусом при трёх заявленных,
   и стоял рядом с чипами, у которых 10 */
.genre-tag { background: #21262d; color: var(--text-primary); font-size: var(--fs-2); font-weight: 600; padding: var(--gap-1) var(--gap-2); border-radius: var(--r-sm); border: 1px solid rgba(255,255,255,0.05); }

/* Подсказка по жестам */
/* `max-width: 380px` при экране в 375 плюс отступ в 4 пикселя означали,
   что «← Не подходит» начинается от нулевого пикселя и буква почти
   срезана краем. Ширина теперь общая с колодой, отступ по бокам —
   как у всего остального содержимого. */
.swipe-legend {
    display: flex; justify-content: space-between; gap: var(--gap-2);
    width: 100%; max-width: 380px;
    margin: var(--gap-2) auto calc(var(--nav-space) + 10px);
    padding: 0 var(--gap-4);
    font-size: var(--fs-1); font-weight: 700; flex-shrink: 0;
}
/* Раньше подсказки были трёх цветов и читались как ссылки, хотя
   ни на что не нажимаются. Цвет остался только у стрелки */
.legend-item { color: var(--text-secondary); opacity: .9; }
.legend-item b { font-weight: 700; }
.legend-disliked  i { color: var(--dislike-color); font-style: normal; }
.legend-watched   i { color: var(--watched-color); font-style: normal; }
.legend-watchlist i { color: var(--watchlist-color); font-style: normal; }

.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 20px; gap: 16px; height: 100%; }

/* ================= Поиск по каталогу ================= */
.search-bar {
    /* top: -1px, а не 0 — прилипшая полоса должна заходить за верхнюю
       кромку прокрутки, иначе на дробных высотах между ней и шапкой
       остаётся щель в пиксель, и в неё видно ползущее содержимое.
       Съеденный пиксель — это пустой отступ, потерять там нечего. */
    position: sticky; top: -1px; z-index: 5;
    display: flex; align-items: center;
    background: var(--bg-color); padding: 13px 0 12px;
}
.search-bar .search-icon { width: 20px; height: 20px; flex-shrink: 0; fill: var(--text-secondary); position: absolute; left: 14px; }
.search-input {
    flex: 1; width: 100%;
    background: #21262d; color: var(--text-primary);
    border: 2px solid var(--card-border); border-radius: var(--r-sm);
    padding: 13px 40px 13px 42px; font-size: var(--fs-4); font-family: inherit;
    outline: none; -webkit-appearance: none;
}
.search-input:focus { border-color: var(--accent-red); }
.search-input::-webkit-search-cancel-button { display: none; }
.search-clear {
    position: absolute; right: 12px;
    background: none; border: none; color: var(--text-secondary);
    font-size: var(--fs-7); line-height: 1; cursor: pointer; padding: 4px 8px;
}
.search-clear[hidden] { display: none; }

.search-results { display: flex; flex-direction: column; gap: 10px; }
/* Сообщения поиска — «ничего не нашлось», «поиск молчит», начальный
   экран — стоят в пустом экране одни. Прижатые к верху, они висели
   под строкой поиска, а под ними оставалось полэкрана пустоты. */
.search-note {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 12px; text-align: center; padding: 24px 12px;
    min-height: min(58vh, 460px);
}
.search-note h2 { font-size: var(--fs-5); font-weight: 800; }
.search-note p { color: var(--text-secondary); font-size: var(--fs-3); line-height: 1.45; max-width: 320px; }
.search-fuzzy { text-align: center; padding: 4px 0 6px; }

.search-row {
    display: flex; gap: 12px; align-items: stretch;
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-left-width: 4px; border-left-color: var(--card-border);
    border-radius: var(--r-md); overflow: hidden; cursor: pointer;
    transition: border-color .15s ease, transform .1s ease;
}
.search-row:active { transform: scale(0.99); }
.search-row[data-status="WATCHLIST"] { border-left-color: var(--watchlist-color); }
.search-row[data-status="WATCHED"]   { border-left-color: var(--watched-color); }
.search-row[data-status="DISLIKED"]  { border-left-color: var(--dislike-color); }

.search-poster { width: 62px; flex-shrink: 0; background: #21262d; }
.search-poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
.search-info { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px 10px 0; min-width: 0; flex: 1; }
.search-title { font-size: var(--fs-4); font-weight: 700; line-height: 1.25; }
.search-meta { font-size: var(--fs-2); color: var(--text-secondary); }
.search-actions { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.search-score { color: var(--accent-orange); font-size: var(--fs-2); font-weight: 700; margin-left: auto; }

.search-status-btn {
    /* 40×40 — нижняя граница цели нажатия. Было 36×32, и это основной
       способ разметить тайтл, не заходя в колоду: три кнопки подряд,
       причём соседние означают противоположное — «буду смотреть» и «не
       подходит» стоят через одну, и промах меняет решение на обратное */
    width: 40px; height: 40px; border-radius: var(--r-sm);
    background: transparent; border: 2px solid var(--card-border);
    color: var(--text-secondary); font-size: var(--fs-3); cursor: pointer;
    font-family: inherit; display: flex; align-items: center; justify-content: center;
    /* Перечислены поимённо, а не `all`: анимировать всё подряд на слабом
       телефоне — лишняя работа в каждом кадре, а меняются три свойства */
    transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.search-status-btn.watchlist.active { border-color: var(--watchlist-color); color: var(--watchlist-color); background: rgba(46,160,67,.15); }
.search-status-btn.watched.active   { border-color: var(--watched-color);   color: var(--watched-color);   background: rgba(88,166,255,.15); }
.search-status-btn.disliked.active  { border-color: var(--dislike-color);   color: var(--dislike-color);   background: rgba(248,81,73,.15); }

/* ================= Мои списки ================= */
/* Число стоит В СТРОКУ с подписью, а не под ней. В столбик у активной
   вкладки «Буду смотреть» подпись переносилась на две строки, у соседних
   оставалась одна — и три одинаковых по смыслу вкладки выглядели
   по-разному. В строку все три помещаются одинаково. */
.lists-tabs { display: flex; gap: var(--gap-2); position: sticky; top: -1px; background: var(--bg-color); padding: var(--gap-3) 0 var(--gap-2); z-index: 5; }
.list-tab {
    flex: 1; min-width: 0;
    background: transparent; border: 2px solid var(--card-border);
    color: var(--text-secondary); border-radius: var(--r-sm);
    /* Вертикальный отступ --gap-3, а не --gap-2: с ним высота вкладки
       42 px, то есть выше порога нажатия в 40. Было 34 — три вкладки
       в ряд жались во всех измерениях сразу, и промахнуться было легко */
    padding: var(--gap-3) var(--gap-1);
    font-size: var(--fs-1); font-weight: 700; cursor: pointer;
    display: flex; align-items: baseline; justify-content: center;
    gap: var(--gap-1); font-family: inherit; white-space: nowrap;
}
/* Троеточие остаётся страховкой на случай чужого шрифта, но подписи
   подобраны так, чтобы до него не доходило: «Буду смотря… 4» обрывалось
   ровно на том слове, которым вкладки и различаются */
.list-tab .tab-label { overflow: hidden; text-overflow: ellipsis; }
.list-tab .tab-count { font-size: var(--fs-2); font-weight: 900; opacity: .85; }
.list-tab[data-status="WATCHLIST"].active { border-color: var(--watchlist-color); color: var(--watchlist-color); background: rgba(46,160,67,0.12); }
.list-tab[data-status="WATCHED"].active   { border-color: var(--watched-color);   color: var(--watched-color);   background: rgba(88,166,255,0.12); }
.list-tab[data-status="DISLIKED"].active  { border-color: var(--dislike-color);   color: var(--dislike-color);   background: rgba(248,81,73,0.12); }

/* `align-items: stretch` у grid по умолчанию, но высоту ряда задаёт
   самая высокая плитка — а у соседей оставалась своя. Из-за этого
   «Планета Земля» с названием в одну строку была ниже «Братьев по
   оружию» с названием в две, и ряд выглядел рваным.
   Лечится не сеткой, а подписью: место под две строки названия
   резервируется всегда, занято оно или нет. */
/* Сетка списков подстраивается под ширину, а не держит жёсткие две
   колонки. На телефоне (390 px) их по-прежнему две: 160 + 160 + зазор
   помещается, третья — нет. На планшете становится четыре, на широком
   экране шесть, и половина экрана перестаёт пустовать.

   Минимум 160 px — ровно та ширина плитки, что сейчас на телефоне:
   мельче нынешнего она не станет нигде. Три колонки на телефоне
   пробовали отдельно (docs/lists-grid.html): постер ужимается
   до 106 px, это меньше превью в поиске, а названия начинают
   обрываться на первом же слове. */
.lists-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: var(--gap-3);
}
.list-card {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); overflow: hidden; cursor: pointer;
    display: flex; flex-direction: column;
    transition: transform .15s ease, border-color .15s ease;
}
.list-card:active { transform: scale(0.97); }
.list-card:hover { border-color: #43505f; }

/* Цветная полоса поверху убрана. Внутри вкладки статус у всех плиток
   один и тот же — полоса не несла никакой информации, зато читалась
   как «этот элемент выделен». Статус теперь виден там, где он и правда
   разный: в поиске и на странице тайтла. */
/* position: relative — точка отсчёта для плашки партнёра в углу обложки */
.list-poster { position: relative; width: 100%; aspect-ratio: 2 / 3; background: #21262d; }
.list-poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
.list-card-info { padding: var(--gap-2) var(--gap-3) var(--gap-3); display: flex; flex-direction: column; gap: var(--gap-1); }
.list-title {
    font-size: var(--fs-3); font-weight: 700; line-height: 1.25;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: calc(2 * 1.25em);
}
.list-meta { font-size: var(--fs-2); color: var(--text-secondary); }
/* Пустой список стоит в экране один, и прижатый к верху он висел
   под вкладками, а под ним оставалось полэкрана пустоты.
   То же, что и у сообщений поиска. */
.list-empty {
    grid-column: 1 / -1; text-align: center; padding: 24px 12px;
    display: flex; flex-direction: column; justify-content: center; gap: 8px;
    min-height: min(58vh, 460px);
}

/* ================= Профиль ================= */
.stat-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.stat-cell { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: var(--r-md); padding: var(--gap-4); display: flex; flex-direction: column; gap: var(--gap-1); }
.stat-value { font-size: var(--fs-7); font-weight: 900; }
.stat-label { font-size: var(--fs-2); color: var(--text-secondary); }
.stat-note { font-size: var(--fs-1); color: var(--text-secondary); opacity: 0.7; }
/* Разбор вкуса: зелёное — нравится, красное — нет.
   Цвета взяты у списков не случайно: зелёный там уже означает
   «буду смотреть», красный — «не подходит», и человеку не приходится
   учить вторую цветовую азбуку */
.taste-facets { display: flex; flex-direction: column; gap: var(--gap-3); }
.taste-row { display: flex; flex-direction: column; gap: var(--gap-2); }
.taste-row .lbl {
    font-size: var(--fs-1); color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: .04em;
}
.taste-chips { display: flex; flex-wrap: wrap; gap: var(--gap-2); }
.taste-chip {
    border-radius: var(--r-sm); padding: var(--gap-2) var(--gap-3);
    font-size: var(--fs-2); border: 1px solid transparent;
}
.taste-chip.up {
    color: var(--watchlist-color);
    border-color: color-mix(in srgb, var(--watchlist-color) 45%, transparent);
    background: color-mix(in srgb, var(--watchlist-color) 12%, transparent);
}
.taste-chip.down {
    color: var(--dislike-color);
    border-color: color-mix(in srgb, var(--dislike-color) 45%, transparent);
    background: color-mix(in srgb, var(--dislike-color) 12%, transparent);
}

.genre-stats { display: flex; flex-wrap: wrap; gap: 8px; }
.genre-stat { background: #21262d; border: 1px solid var(--card-border); border-radius: var(--r-sm); padding: var(--gap-2) var(--gap-3); font-size: var(--fs-2); color: var(--text-secondary); }
.genre-stat b { color: var(--text-primary); }

/* ================= Фильтры ================= */
.filter-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100svh; background: rgba(0,0,0,0.6); z-index: 200; display: flex; justify-content: center; align-items: flex-end; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; }
.filter-overlay.active { opacity: 1; pointer-events: all; }
/* Шторка меряется в dvh, а не vh, и держит свою высоту сама.
   Причина: когда человек трогает поле года, снизу выезжает клавиатура.
   vh её не замечает — и кнопка «Применить фильтры» уезжала под клавиатуру,
   до неё было не дотянуться. dvh — та высота, что видна прямо сейчас,
   поэтому список фильтров ужимается, а кнопка остаётся на экране. */
.filter-modal { background: var(--card-bg); width: 100%; max-width: 420px; max-height: 92dvh; border-radius: var(--r-lg) var(--r-lg) 0 0; padding: var(--gap-5); display: flex; flex-direction: column; gap: 20px; transform: translateY(100%); transition: transform 0.3s ease; }
.filter-overlay.active .filter-modal { transform: translateY(0); }
.filter-header { display: flex; justify-content: space-between; align-items: center; }
.filter-header h3 { font-size: var(--fs-6); font-weight: 700; }
.btn-close { background: none; border: none; color: var(--text-secondary); font-size: var(--fs-8); cursor: pointer; line-height: 1; }
.filter-body { display: flex; flex-direction: column; gap: 20px; flex: 1; min-height: 0; overflow-y: auto; padding-right: 4px; }
.filter-group h4 { font-size: var(--fs-3); color: var(--text-secondary); margin-bottom: 12px; font-weight: 500; }
.filter-footer { display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }
.genre-toggles { display: flex; flex-wrap: wrap; gap: 8px; }

.toggle-item { padding: var(--gap-2) var(--gap-4); border-radius: var(--r-sm); font-size: var(--fs-3); font-weight: 600; cursor: pointer; border: 2px solid var(--card-border); transition: all 0.2s; }
.toggle-item[data-state="neutral"] { background: transparent; color: var(--text-secondary); }
.toggle-item[data-state="include"] { background: rgba(46, 160, 67, 0.15); border-color: var(--watchlist-color); color: var(--watchlist-color); }
.toggle-item[data-state="exclude"] { background: rgba(248, 81, 73, 0.15); border-color: var(--dislike-color); color: var(--dislike-color); }

/* Чипы «Мои списки» в фильтрах.
   Каждый горит своим цветом, а не общим зелёным «включено»: тот же цвет
   человек потом увидит на карточке в колоде и узнает его без чтения. */
.list-toggle { display: inline-flex; align-items: center; gap: 7px; }
.list-toggle .toggle-count { font-size: var(--fs-2); font-weight: 700; opacity: .75; }
.list-toggle.list-watchlist[data-state="include"] { background: rgba(46,160,67,.15); border-color: var(--watchlist-color); color: var(--watchlist-color); }
.list-toggle.list-watched[data-state="include"]   { background: rgba(88,166,255,.15); border-color: var(--watched-color); color: var(--watched-color); }
.list-toggle.list-disliked[data-state="include"]  { background: rgba(248,81,73,.15); border-color: var(--dislike-color); color: var(--dislike-color); }
.list-toggle.list-untouched[data-state="include"] { background: rgba(255,152,0,.15); border-color: var(--accent-orange); color: var(--accent-orange); }

.filter-select, .filter-input { background: #21262d; color: var(--text-primary); border: 2px solid var(--card-border); border-radius: var(--r-sm); padding: var(--gap-3) 14px; font-size: var(--fs-4); font-family: inherit; width: 100%; outline: none; }
.filter-select:focus, .filter-input:focus { border-color: var(--accent-red); }
.range-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.range-row:last-child { margin-bottom: 0; }
.range-dash { color: var(--text-secondary); }
.filter-hint { color: var(--text-secondary); font-size: var(--fs-3); }

/* Чип «Не определился» снять нельзя, пока не выбран ни один список:
   колода собралась бы из пустоты. Вместо молчаливого игнорирования
   нажатия — короткий отклик, чтобы было видно, что нажатие дошло */
@keyframes chip-locked-pulse {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.06); }
    100% { transform: scale(1); }
}
.toggle-item.chip-locked { animation: chip-locked-pulse .22s ease; }
@media (prefers-reduced-motion: reduce) {
    .toggle-item.chip-locked { animation: none; }
}

.btn-apply { background: var(--accent-red); color: white; border: none; width: 100%; padding: var(--gap-4); border-radius: var(--r-sm); font-size: var(--fs-5); font-weight: 700; cursor: pointer; font-family: inherit; }
.btn-apply:active { background: #b80710; }
.btn-reset { background-color: var(--card-border); color: var(--text-primary); border: none; padding: 14px var(--gap-5); border-radius: var(--r-sm); font-weight: 600; cursor: pointer; width: 100%; font-family: inherit; }

/* ================= Bottom Navigation ================= */
.bottom-nav { position: fixed; bottom: 0; left: 50%; transform: translateX(-50%); width: 100%; max-width: 420px; height: var(--nav-space); padding-bottom: var(--safe-bottom); background-color: var(--card-bg); border-top: 1px solid var(--card-border); display: flex; justify-content: space-around; align-items: center; z-index: 100; }
/* flex:1 вместо фиксированной доли — пунктов стало четыре */
.nav-item { display: flex; flex-direction: column; align-items: center; gap: 6px; color: var(--text-secondary); text-decoration: none; font-size: var(--fs-1); font-weight: 600; cursor: pointer; transition: color 0.2s ease; flex: 1; min-width: 0; text-align: center; }
.nav-item.active { color: var(--accent-red); }
.nav-item:hover:not(.active) { color: var(--text-primary); }
.nav-item svg { width: 24px; height: 24px; fill: currentColor; }

/* ================= Страница фильма ================= */
.detail-body { overflow-y: auto; height: auto; min-height: 100vh; min-height: 100dvh; display: block; }
/* снизу добавлен отступ под полосу жестов, иначе последняя ссылка
   упирается прямо в неё */
/* Снизу оставлено место под меню: оно здесь такое же фиксированное,
   как на главной, и без запаса накрыло бы последний блок страницы. */
/* Нижнего отступа под меню у контейнера нет намеренно: подвал с атрибуцией
   стоит ЗА ним, и этот отступ превращался в пустую полосу в 94 px между
   последней ссылкой и подвалом. Место под нижним меню отводит сам подвал
   своим margin-bottom — он последний на странице, ему и отводить. */
.detail-container { width: 100%; max-width: 480px; margin: 0 auto; padding: 20px 20px 0; display: flex; flex-direction: column; gap: 18px; }
/* Кнопка «назад» и распорка одинаковой ширины по краям — логотип от этого
   стоит ровно по центру, а не «примерно посередине». Считать его отступы
   вручную пришлось бы заново при каждой смене кегля. */
.detail-nav { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-2); }
.detail-nav-spacer { width: 40px; flex: none; }
.back-btn {
    width: 40px; height: 40px; flex: none;
    display: flex; align-items: center; justify-content: center;
    background: var(--card-bg); color: var(--text-primary);
    border: 1px solid var(--card-border); border-radius: 50%;
    cursor: pointer; text-decoration: none;
    transition: background .15s ease, border-color .15s ease;
}
.back-btn svg { width: 22px; height: 22px; fill: currentColor; display: block; }
.back-btn:hover { background: #1b2029; border-color: #43505f; }
.back-btn:active { background: #21262d; }
.back-btn:focus-visible { outline: 2px solid var(--accent-red); outline-offset: 2px; }

.pill { background: var(--scrim); border: 1px solid var(--card-border); padding: 5px 11px; border-radius: var(--r-sm); font-size: var(--fs-1); font-weight: 600; color: var(--text-secondary); }

.detail-title h1 { font-size: var(--fs-7); font-weight: 800; line-height: 1.2; }

/* Блок «в каком это списке». Был карточкой с подписью-заголовком
   и тремя одинаково-серыми кнопками: глазу не за что зацепиться,
   и какая из трёх сейчас выбрана, читалось только по тонкой рамке.

   Теперь подпись — метка с точкой цвета списка, а кнопки различаются
   собственным цветом ещё до нажатия. Выбранная заливается им целиком,
   остальные гаснут: состояние видно с одного взгляда, а не вычитывается. */
.status-block {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); padding: var(--gap-3) var(--gap-3) var(--gap-3);
    display: flex; flex-direction: column; gap: var(--gap-3);
}
.status-current {
    display: inline-flex; align-items: center; gap: var(--gap-2);
    font-size: var(--fs-1); font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--text-secondary);
}
.status-current::before {
    content: ''; width: 7px; height: 7px; border-radius: 50%;
    background: currentColor; opacity: .55; flex-shrink: 0;
    transition: background .15s ease, opacity .15s ease;
}
.status-current[data-status]:not([data-status=""])::before { opacity: 1; }
.status-current[data-status="watchlist"] { color: var(--watchlist-color); }
.status-current[data-status="watched"]   { color: var(--watched-color); }
.status-current[data-status="disliked"]  { color: var(--dislike-color); }
/* В три колонки на телефоне «✕ Не подходит» и «✓ Буду смотреть»
   переносились на две строки, а «Смотрел» оставался на одной — ряд
   выглядел сломанным. Кнопка шириной 107 px не вмещает «Буду смотреть»
   никаким кеглем, который ещё можно читать. Поэтому в столбик:
   три широкие кнопки, каждая в одну строку и с честной площадью нажатия.
   Три колонки возвращаются там, где они помещаются. */
.status-buttons { display: grid; grid-template-columns: 1fr; gap: 6px; }
/* Порог 440, а не 420. Считается он так: контейнер карточки не шире
   480 px и не шире окна, у него 20 px полей с каждой стороны, у блока
   статусов ещё 12, между кнопками два зазора по 6. При окне 420 на кнопку
   остаётся 113 px, а «✓ Буду смотреть» требует 101 — и это ДО учёта
   собственных полей кнопки. Колонки разъезжались, и текст лез за рамку.

   minmax(0, 1fr) вместо 1fr — по той же причине: обычный 1fr не даёт
   колонке стать уже своего содержимого, и переполнение одной кнопки
   плющит две соседние. */
@media (min-width: 440px) {
    .status-buttons { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.status-btn {
    --tone: var(--text-secondary);
    display: flex; align-items: center; justify-content: center; gap: 5px;
    min-width: 0;
    background: rgba(255, 255, 255, .03);
    border: 1px solid var(--card-border);
    color: var(--text-secondary); border-radius: var(--r-sm);
    padding: var(--gap-3) 6px; font-size: var(--fs-2); font-weight: 700;
    cursor: pointer; font-family: inherit; white-space: nowrap;
    transition: border-color .15s ease, color .15s ease,
                background .15s ease, transform .1s ease;
}
/* В столбик кнопкам тесно не бывает — там кегль можно вернуть */
@media (max-width: 439px) {
    .status-btn { font-size: var(--fs-3); gap: 7px; padding: var(--gap-3) var(--gap-2); }
}
.status-btn.disliked  { --tone: var(--dislike-color); }
.status-btn.watched   { --tone: var(--watched-color); }
.status-btn.watchlist { --tone: var(--watchlist-color); }
/* Значок берёт цвет своего списка и на невыбранной кнопке — так три
   кнопки различаются до нажатия, а не только после */
.status-btn .status-ico { color: var(--tone); opacity: .8; flex: none;
    transition: opacity .15s ease; }
.status-btn:hover:not(.active) { border-color: var(--tone); color: var(--text-primary); }
.status-btn:active { transform: scale(.98); }
.status-btn.active {
    border-color: var(--tone); color: var(--tone);
    background: color-mix(in srgb, var(--tone) 16%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 35%, transparent);
}
.status-btn.active .status-ico { opacity: 1; }
/* Запасной цвет для браузеров без color-mix: без него активная кнопка
   осталась бы просто прозрачной, то есть неотличимой от остальных */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
    .status-btn.disliked.active  { background: rgba(248,81,73,.15); }
    .status-btn.watched.active   { background: rgba(88,166,255,.15); }
    .status-btn.watchlist.active { background: rgba(46,160,67,.15); }
}
.status-clear {
    align-self: flex-start;
    background: none; border: none; padding: 0;
    color: var(--text-secondary); font-size: var(--fs-2);
    text-decoration: underline; text-underline-offset: 3px;
    cursor: pointer; font-family: inherit;
}
.status-clear:hover { color: var(--text-primary); }
.status-clear[hidden] { display: none; }

.genres-flex { display: flex; gap: 8px; flex-wrap: wrap; }
.genre-pill { background: #21262d; padding: var(--gap-2) var(--gap-3); border-radius: var(--r-sm); font-size: var(--fs-2); color: var(--text-secondary); }


.description-box { line-height: 1.6; color: #cbd5e1; font-size: var(--fs-4); user-select: text; }


/* ================= Карточка: постер на всю площадь ================= */
.movie-card { border-width: 2px; }

.movie-card .poster-img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover; display: block;
}

.movie-card .tint-layer { position: absolute; inset: 0; z-index: 2; opacity: 0; pointer-events: none; transition: opacity .12s linear; }

.card-top { position: absolute; top: var(--gap-4); left: var(--gap-4); right: var(--gap-4); z-index: 4; display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.movie-card .rating-badge { position: static; }

/* Отпечаток лапы вместо «★» у нашего балла.
   Оценку на сервисе ставит кот-критик, и звезда тут чужая: она значок
   чужих рейтингов — Кинопоиска, IMDb, магазинов приложений.

   Маска, а не картинка: цвет берётся из `currentColor`, как у символа,
   который лапка заменила. Значит одно правило работает и на оранжевом
   бейдже колоды, и в серой строке списка, и крупно на странице тайтла.

   Размер — 1em, то есть по кеглю соседнего текста: 12 px в колоде
   и в поиске, 24 px на странице тайтла. Сдвиг вниз на 0.1em ставит
   отпечаток на оптическую середину строки: у цифр нет выносных
   элементов, и выравнивание по базовой линии задирает значок вверх. */
.paw {
    display: inline-block; width: 1em; height: 1em;
    vertical-align: -0.1em;
    background-color: currentColor;
    -webkit-mask: url(../img/mascot/paw.svg) center / contain no-repeat;
    mask: url(../img/mascot/paw.svg) center / contain no-repeat;
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { background: var(--scrim); border: 1px solid var(--card-border); color: var(--text-primary); font-size: var(--fs-1); font-weight: 700; padding: 5px 10px; border-radius: var(--r-sm); }
/* Единственное отличие вариантов — цвет текста и рамки. Размер,
   радиус, фон и высота у всех общие */
.chip-type { color: var(--accent-orange); border-color: rgba(255,152,0,.45); }
.chip-age { color: var(--text-secondary); }
.chip-status { color: var(--text-secondary); }
.chip-status.status-airing { color: var(--watched-color); border-color: var(--watched-color); }
.chip-status.status-not_released { color: var(--accent-orange); border-color: rgba(255,152,0,.5); }

/* «В каком списке лежит эта карточка».
   Отличать его от плашек свайпа («✓ Буду смотреть» по углам) приходится
   и словами, и видом: у плашек крупный шрифт с поворотом и прозрачный
   фон, здесь — обычный чип в одном ряду с типом и возрастом. Цвет тот же,
   что у чипа этого списка в фильтрах. */
.chip-list { font-weight: 700; }
.chip-list.list-watchlist { color: var(--watchlist-color); border-color: var(--watchlist-color); background: rgba(46,160,67,.2); }
.chip-list.list-watched   { color: var(--watched-color);   border-color: var(--watched-color);   background: rgba(88,166,255,.2); }
.chip-list.list-disliked  { color: var(--dislike-color);   border-color: var(--dislike-color);   background: rgba(248,81,73,.2); }

/* Подложка под текстом. Прежняя доходила всего до 0.78 к тому месту,
   где начинается название, и на ярком постере («Рик и Морти», «Смешарики»)
   буквы плавали в засвете, а сквозь них проступала типографика самого
   постера. Теперь к первой строке текста фон уже почти плотный. */
.movie-card .card-info {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
    /* 72px сверху — не отступ, а высота градиента: под ним прячется
       низ постера, и число подобрано под него, а не под шкалу */
    height: auto; padding: 72px var(--gap-4) var(--gap-4);
    background: linear-gradient(180deg,
        transparent 0%,
        rgba(9, 12, 18, .58) 26%,
        rgba(9, 12, 18, .93) 48%,
        #090c12 74%);
    display: flex; flex-direction: column; gap: var(--gap-2);
}
.movie-card .movie-title { font-size: var(--fs-6); }
/* Тот же кегль, что у жанров, но обычный вес и приглушённый цвет:
   иерархия делается весом и цветом, а не третьим размером шрифта */
.card-desc { font-size: var(--fs-2); line-height: 1.45; color: var(--text-secondary); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; margin-top: 2px; }

/* Видно только чтению с экрана.

   Не `display: none` и не `visibility: hidden` — те убирают элемент
   и оттуда тоже. Приём старый и единственный работающий: клип в точку
   при сохранённом месте в дереве документа.

   `white-space: nowrap` обязателен: без него длинный заголовок
   переносится внутри рамки в 1 px и некоторые читалки проговаривают
   его по слогам. */
.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ================= Знакомство с сервисом ================= */
/* Шаг 1: экран гаснет, кроме нижнего меню.

   Затемнение — сплошной цвет, а не backdrop-filter: размытие
   пересчитывается каждый кадр, а под ним живая колода с анимацией. */
.tour-veil {
    position: fixed; inset: 0; z-index: 300;
    background: var(--scrim);
    display: flex; flex-direction: column; justify-content: flex-end;
    padding: var(--gap-5) var(--gap-4);
    padding-bottom: calc(var(--nav-space) + var(--gap-4));
    animation: tour-in .24s ease both;
}
/* Без этой строки атрибут hidden не работает вовсе: встроенное
   правило браузера `[hidden] { display: none }` слабее любого
   объявленного display, и оверлей висит поверх экрана с загрузки.
   В проекте так скрываются все листы — см. .sheet-overlay[hidden] */
.tour-veil[hidden] { display: none; }
@keyframes tour-in { from { opacity: 0 } to { opacity: 1 } }

.tour-tip {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); padding: var(--gap-4);
    display: flex; flex-direction: column; gap: var(--gap-2);
    max-width: 420px; margin: 0 auto; width: 100%;
}
.tour-tip b { font-size: var(--fs-5); }
.tour-tip p { font-size: var(--fs-3); color: var(--text-secondary); line-height: 1.5; }

/* Подсвеченная вкладка поднимается НАД затемнением: иначе подсказка
   объясняет кнопку, которой не видно */
body.tour-on .bottom-nav { z-index: 301; }
body.tour-on .nav-item:not(.is-spot) { opacity: .35; }
.nav-item.is-spot {
    color: var(--accent-orange);
    filter: drop-shadow(0 0 12px rgba(255, 152, 0, .45));
}
.nav-item.is-spot svg { fill: var(--accent-orange); }


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

/* ================= Профиль: аккаунт ================= */
.account-box {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); padding: var(--gap-4);
    display: flex; flex-direction: column; gap: var(--gap-3);
}
.account-row { display: flex; align-items: center; gap: 12px; }
.account-avatar {
    width: 44px; height: 44px; flex-shrink: 0; border-radius: 50%;
    background: var(--accent-red); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: var(--fs-5); font-weight: 800;
}
/* У гостя нет буквы — вместо неё морда. Пустой кружок читался бы
   как не загрузившийся аватар. */
.account-avatar-cat { background: #21262d; overflow: hidden; padding: 2px; }
.account-avatar-cat img { width: 100%; height: 100%; object-fit: contain; display: block; }

.account-info { min-width: 0; }
.account-email { font-weight: 700; font-size: var(--fs-4); overflow-wrap: anywhere; }
.btn-google {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    background: #fff; color: #1f1f1f; text-decoration: none;
    border-radius: var(--r-sm); padding: 13px 18px;
    font-size: var(--fs-4); font-weight: 700;
    transition: background .15s ease;
}
.btn-google:hover { background: #e8eaed; }
.btn-google:active { background: #dadce0; }

/* ================= Профиль: зона сброса ================= */
.danger-zone { border: 1px dashed var(--dislike-color); border-radius: var(--r-md); padding: var(--gap-4); display: flex; flex-direction: column; gap: var(--gap-3); margin-top: var(--gap-2); }
.btn-danger {
    background: rgba(248, 81, 73, .15); color: var(--dislike-color);
    border: 1px solid var(--dislike-color); border-radius: var(--r-sm);
    padding: 11px var(--gap-5); font-size: var(--fs-3); font-weight: 700;
    cursor: pointer; font-family: inherit;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.btn-danger:hover:not(:disabled) { background: rgba(248, 81, 73, .25); }
/* Пока почта не введена верно, кнопка гасится совсем: красная кнопка,
   на которую можно нажать в любой момент, — это приглашение нажать.
   Настоящая проверка всё равно на сервере, здесь только вид */
.btn-danger:disabled {
    background: transparent; color: var(--text-secondary);
    border-color: var(--card-border); cursor: default;
}
.confirm-row { display: flex; flex-direction: column; gap: 8px; font-size: var(--fs-3); }
.confirm-row[hidden], .btn-danger[hidden] { display: none; }

/* ================= Страница фильма ================= */
.detail-hero { position: relative; border-radius: var(--r-lg); overflow: hidden; min-height: 300px; background: #21262d; }
/* Размытие снято. Оно стоило отдельного слоя перерисовки на всю ширину
   экрана, а решало задачу градиента — сделать фон спокойнее под текстом.
   Градиент это делает лучше и бесплатно, поэтому он стал плотнее книзу:
   белый заголовок раньше местами ложился на светлое небо и терялся. */
.hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transform: scale(1.04); }
.hero-shade {
    position: absolute; inset: 0;
    background: linear-gradient(180deg,
        rgba(13,17,23,.20) 0%,
        rgba(13,17,23,.55) 45%,
        rgba(13,17,23,.88) 72%,
        rgba(13,17,23,.97) 100%);
}
.hero-content { position: relative; z-index: 2; display: flex; gap: 14px; align-items: flex-end; padding: 20px 16px 16px; min-height: 300px; }
.hero-poster { width: 108px; border-radius: var(--r-sm); box-shadow: 0 8px 24px rgba(0,0,0,.6); flex-shrink: 0; }
.hero-text h1 { font-size: var(--fs-6); font-weight: 800; line-height: 1.15; }
.hero-original { color: var(--text-secondary); font-size: var(--fs-3); margin: 4px 0 8px; }

/* Раньше части разделялись только пробелами, и строка «2006   Великобритания,
   США, Япония» читалась как набор обрывков. В карточке колоды то же самое
   разделено точками — здесь теперь так же. */
.detail-meta { display: flex; flex-wrap: wrap; gap: 2px var(--gap-2); color: var(--text-secondary); font-size: var(--fs-3); }
/* Точка стоит ПОСЛЕ своего слова, а не перед следующим. Перед следующим
   она при переносе строки повисала в начале новой строки сиротой:
   «• 3 сезона • 19 серий». Приклеенная к предыдущему слову, она остаётся
   в конце предыдущей строки, где и должна быть */
.detail-meta > span:not(:last-child)::after { content: '•'; margin-left: var(--gap-2); opacity: .55; }
.detail-slogan { font-style: italic; color: var(--text-secondary); }

.detail-section { display: flex; flex-direction: column; gap: 12px; }
.detail-section h2 { font-size: var(--fs-5); font-weight: 800; }
.section-sub { font-weight: 400; font-size: var(--fs-2); color: var(--text-secondary); }


.people-row, .relation-row { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 6px; }
.person-card { width: 96px; flex-shrink: 0; }
.person-photo {
    width: 96px; height: 130px; border-radius: var(--r-sm); overflow: hidden;
    background: #21262d;
    display: flex; align-items: center; justify-content: center;
}
/* Фотографии нет — инициалы. Пустой прямоугольник читался как
   не загрузившаяся картинка, хотя загружать было нечего. */
.person-initials {
    font-size: var(--fs-7); font-weight: 800; letter-spacing: 1px;
    color: var(--card-border);
}
.person-photo img { width: 100%; height: 100%; object-fit: cover; }
.person-name { font-size: var(--fs-2); font-weight: 700; margin-top: 6px; line-height: 1.2; }
.person-role { font-size: var(--fs-1); color: var(--text-secondary); line-height: 1.2; }

.crew-list { display: flex; flex-direction: column; gap: 8px; }
.crew-row { display: flex; justify-content: space-between; gap: 12px; font-size: var(--fs-3); border-bottom: 1px solid var(--card-border); padding-bottom: 6px; }
.crew-role { color: var(--text-secondary); }

.season-block { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: var(--r-md); overflow: hidden; }
.season-block summary { cursor: pointer; padding: 14px 16px; display: flex; align-items: center; gap: 10px; font-weight: 700; }
.season-block.special summary { color: var(--accent-orange); }

/* ================= Сворачиваемый блок =================
   Длинные справочные списки — съёмочная группа, разбор по жанрам — на
   телефоне вытесняют собой всё остальное, а читают их единицы. Свёрнутый
   заголовок с числом внутри говорит, что там лежит, и занимает одну строку.
   Оформление общее с сезонами: два разных вида «раскрывашки» на одной
   странице читаются как недоделка. */
.collapse { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: var(--r-md); overflow: hidden; }
.collapse > summary { cursor: pointer; padding: 14px 16px; display: flex; align-items: center; gap: 10px; font-size: var(--fs-4); font-weight: 800; }
.collapse-count { color: var(--text-secondary); font-weight: 600; font-size: var(--fs-3); }

/* Действие в ряду раскрывашек: «Написать коту».
   Снаружи выглядит так же — та же высота, фон, радиус и вес букв, —
   но стрелка смотрит вправо, а не вниз. Разница не косметическая:
   раскрывашка показывает содержимое на месте, а это открывает лист
   поверх экрана, и знак обязан об этом предупреждать. */
.profile-action {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 14px 16px; text-align: left; cursor: pointer;
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); color: var(--text-primary);
    font-family: inherit; font-size: var(--fs-4); font-weight: 800;
}
.profile-action::after {
    content: ''; flex-shrink: 0; margin-left: auto;
    width: 8px; height: 8px;
    border-right: 2px solid var(--text-secondary);
    border-bottom: 2px solid var(--text-secondary);
    transform: rotate(-45deg);
}
.collapse-body { padding: 0 16px 14px; }

/* Галочка-стрелка рисуется двумя границами, а не символом: у символов
   «▾» разная ширина и базовая линия в разных шрифтах. */
.collapse > summary, .season-block > summary { list-style: none; }
.collapse > summary::-webkit-details-marker,
.season-block > summary::-webkit-details-marker { display: none; }
.collapse > summary::after,
.season-block > summary::after {
    content: ''; flex-shrink: 0; margin-left: auto;
    width: 8px; height: 8px;
    border-right: 2px solid var(--text-secondary);
    border-bottom: 2px solid var(--text-secondary);
    transform: translateY(-2px) rotate(45deg);
    transition: transform 0.18s ease;
}
.collapse[open] > summary::after,
.season-block[open] > summary::after { transform: translateY(2px) rotate(225deg); }
.season-count { margin-left: auto; color: var(--text-secondary); font-weight: 400; font-size: var(--fs-3); }
.episode-list { padding: 0 16px 14px; display: flex; flex-direction: column; gap: 12px; }
.episode-row { display: flex; gap: 12px; }
.episode-number { color: var(--text-secondary); font-weight: 800; min-width: 26px; }
.episode-name { font-size: var(--fs-3); font-weight: 600; }
.episode-date { font-size: var(--fs-2); color: var(--text-secondary); }
.episode-synopsis { font-size: var(--fs-2); color: var(--text-secondary); line-height: 1.45; margin-top: 4px; user-select: text; }

.relation-card { width: 118px; flex-shrink: 0; text-decoration: none; color: var(--text-primary); }
.relation-poster { width: 118px; aspect-ratio: 2/3; border-radius: var(--r-sm); overflow: hidden; background: #21262d; }
.relation-poster img { width: 100%; height: 100%; object-fit: cover; }
.relation-type { font-size: var(--fs-1); color: var(--accent-orange); margin-top: 6px; font-weight: 700; }
.relation-title { font-size: var(--fs-2); line-height: 1.25; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.relation-inactive { opacity: .55; cursor: default; }

.external-link { display: block; text-align: center; color: var(--text-secondary); font-size: var(--fs-3); padding: 12px; text-decoration: underline; }
/* ================= Правки от 14.09: пустые оценки, премьера, страны ================= */
.rating-badge.rating-empty { color: var(--text-secondary); font-weight: 600; font-size: var(--fs-2); }
.meta-accent { color: var(--accent-orange); font-weight: 600; }
/* Строка про отсутствие оценок стоит на месте целого блока рейтингов,
   и голой она читалась как ошибка. Кот спит — оценивать нечего. */
.ratings-empty {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); padding: var(--gap-4);
    color: var(--text-secondary); font-size: var(--fs-3);
    display: flex; align-items: center; gap: var(--gap-3);
}
/* Высота задана, ширина считается сама.

   Было `width: 56px; height: 56px` — под квадратную картинку rate-awful
   512×512. 27.09 её сменил nothing-yet 640×402, и кота сплющило
   по ширине: квадратная рамка не спрашивает, какие у картинки
   пропорции. Фиксируем высоту, чтобы строка не прыгала, остальное
   отдаём картинке. */
.ratings-empty-cat { height: 64px; width: auto; flex-shrink: 0; display: block; }

/* ================= Маскот ================= */
.mascot {
    display: block; width: 100%; max-width: 230px; height: auto;
    margin: 0 auto;
    pointer-events: none; -webkit-user-drag: none;
    /* Тень была чёрной: `drop-shadow(0 14px 26px rgba(0,0,0,.55))`.
       На фоне #0d1117 чёрное на чёрном не читается как объём — читается
       как грязный прямоугольник вокруг кота. Сам файл прозрачный,
       дело было именно в тени. Заменена на тёплое свечение: кот рыжий,
       оно его поднимает над фоном, а пятна не даёт. */
    filter: drop-shadow(0 10px 22px rgba(255, 152, 0, .16));
}
.mascot-sm { max-width: 150px; }
.mascot-lg { max-width: 300px; }

.empty-state h2 { font-size: var(--fs-5); font-weight: 800; line-height: 1.25; }
.empty-state p { color: var(--text-secondary); font-size: var(--fs-3); line-height: 1.45; max-width: 300px; }
.empty-state .btn-reset { width: auto; min-width: 200px; }
.empty-state .mascot + h2 { margin-top: -4px; }

.list-empty .mascot { margin-bottom: 4px; }
/* Уходишь — он уже отвернулся. Стоит под списком того, что исчезнет,
   и смягчает его ровно настолько, чтобы список остался читаемым. */
.danger-cat {
    display: flex; align-items: center; gap: var(--gap-3);
    margin: var(--gap-2) 0;
}
.danger-cat .mascot { width: 96px; max-width: 96px; margin: 0; flex-shrink: 0; }

/* Кот с двумя подушками — над строкой связи, а не сбоку от неё:
   картинка широкая, и рядом с текстом от неё осталась бы полоска.
   Высота не задаётся: позы у маскота разной формы, и рамка
   с обеими сторонами их плющит */

.profile-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; padding: 8px 0 4px; width: 100%; }

/* Приветствие при первом запуске (§1) */
/* Всё содержимое — одной группой по центру, чуть выше середины.

   Двигали дважды. По центру с равными полями экран провисал: сверху
   пустая треть. Прижатое к верху с кнопкой внизу дало дыру посередине,
   между подсказками и кнопкой. Держится группой, а смещение вверх
   даёт разница отступов: снизу вдвое больше, чем сверху. */
.welcome-state {
    position: absolute; inset: 0; z-index: 6;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: clamp(8px, 1.6vh, 14px); text-align: center;
    padding: clamp(8px, 2vh, 20px) 16px clamp(16px, 4vh, 36px);
    background: var(--bg-color);
    border-radius: var(--r-lg);
    overflow: hidden;
}
/* Кнопка отделена от подсказок, но не улетает вниз: с `margin-top: auto`
   между ними открывалась пустота во весь оставшийся экран. */
.welcome-state .btn-welcome { margin-top: clamp(6px, 2.4vh, 22px); }
/* Плашка про хранение висит над нижним меню и накрывает кнопку.
   Появляются они на одном и том же первом заходе, поэтому место
   под плашку резервируется, пока она на экране. */
body.storage-on .welcome-state { padding-bottom: 96px; }
.welcome-state[hidden] { display: none; }
/* Кот сжимается первым: текст и кнопка важнее, чем размер картинки.

   Сжимать нужно РОДИТЕЛЯ. `flex-shrink` достаётся <picture>, а его
   высоту держит картинка внутри: пока у picture нет своей высоты,
   он не ужимается, и на низком экране кнопка уезжала под нижнее меню.
   Поэтому picture сам становится флексом, а картинка внутри тянется
   по нему и вписывается через object-fit. */
.welcome-state picture {
    display: flex; align-items: center; justify-content: center;
    width: 100%; flex: 0 1 auto; min-height: 0; max-height: 38vh;
}
.welcome-state .mascot {
    width: auto; max-width: min(280px, 66vw);
    height: 100%; max-height: 38vh; min-height: 0;
    object-fit: contain;
}
body.welcome-on .swipe-legend { visibility: hidden; }
.welcome-state h2 { font-size: clamp(1.15rem, 4.4vw, 1.45rem); font-weight: 800; }
.welcome-state p { color: var(--text-secondary); font-size: clamp(.82rem, 3.3vw, .92rem); line-height: 1.45; max-width: 300px; }
/* Три подсказки в строку, а не в столбец: столбец забирал около сорока
   пикселей высоты, из-за которых на невысоком экране поджимался кот. */
.welcome-hints {
    display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
    gap: var(--gap-1) var(--gap-3); font-size: var(--fs-2); font-weight: 700;
}
.welcome-hints span { opacity: .85; }
.welcome-hints i { color: var(--card-border); font-style: normal; }
/* Кнопка в цвет заголовка, а не красная: красная спорила с котом
   за внимание на экране, где смотреть нужно на кота. */
.btn-welcome {
    background: var(--text-primary); color: var(--bg-color); border: none;
    padding: 14px 32px; border-radius: var(--r-sm); font-size: var(--fs-4); font-weight: 800;
    cursor: pointer; font-family: inherit;
}
/* Нажатая кнопка темнеет, и оттенок выводится из токена, а не
   подбирается заново: #c9d1d9 был третьим серым без имени */
.btn-welcome:active { background: color-mix(in srgb, var(--text-primary) 80%, var(--bg-color)); }

/* ================= Страницы ошибок (404 / 500) ================= */
.error-body { height: auto; min-height: 100vh; overflow-y: auto; justify-content: flex-start; }
.error-page { width: 100%; max-width: 480px; margin: 0 auto; padding: 0 20px 48px; display: flex; flex-direction: column; align-items: center; gap: 16px; text-align: center; }
/* Верхние углы прямые: баннер прижат к шапке, скругление там
   оставило бы щель с фоном */
.error-banner { position: relative; width: 100vw; max-width: 480px; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 0 0 var(--r-lg) var(--r-lg); margin-bottom: 8px; }
.error-banner img { width: 100%; height: 100%; object-fit: cover; display: block; }
.error-banner-shade { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(13,17,23,.55) 0%, rgba(13,17,23,.15) 45%, var(--bg-color) 100%); }
.error-banner .logo { position: absolute; top: 16px; left: 20px; z-index: 2; font-size: var(--fs-6); }
.error-code { font-size: var(--fs-2); font-weight: 800; letter-spacing: 2px; color: var(--accent-red); }
.error-page h1 { font-size: var(--fs-6); font-weight: 800; line-height: 1.25; }
.error-page p { color: var(--text-secondary); font-size: var(--fs-4); line-height: 1.5; max-width: 340px; }
.error-actions { display: flex; flex-direction: column; gap: 10px; width: 100%; max-width: 280px; margin-top: 4px; }
.error-actions .btn-apply { display: block; text-align: center; text-decoration: none; }

/* ================= Трейлер: превью-ссылка ================= */
/* Вместо встроенного плеера — кадр из ролика. Клик в любое место открывает
   площадку: половина трейлеров встраиваться всё равно не даёт. */
.trailer-card {
    position: relative; display: block;
    width: 100%; aspect-ratio: 16 / 9;
    border-radius: var(--r-md); overflow: hidden;
    background: #21262d; border: 1px solid var(--card-border);
    cursor: pointer; text-decoration: none;
    transition: border-color .15s ease, transform .1s ease;
}
.trailer-card:hover { border-color: var(--accent-red); }
.trailer-card:active { transform: scale(0.99); }
.trailer-card:focus-visible { outline: 2px solid var(--accent-red); outline-offset: 3px; }
.trailer-card[hidden] { display: none; }

/* hqdefault приходит в 4:3 с чёрными полями — cover их срезает */
.trailer-thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.trailer-shade {
    position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(13,17,23,.15) 0%, rgba(13,17,23,.15) 55%, rgba(13,17,23,.85) 100%);
}
.trailer-play {
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 64px; height: 64px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-red); color: #fff;
    box-shadow: 0 6px 20px rgba(0,0,0,.5);
    transition: transform .15s ease;
}
.trailer-card:hover .trailer-play { transform: translate(-50%, -50%) scale(1.08); }
.trailer-card-label {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 12px 14px; text-align: center;
    color: var(--text-primary); font-size: var(--fs-3); font-weight: 700;
}

/* ================= Трейлер: кадр не загрузился ================= */
/* Кликабелен весь блок: отдельной кнопки нет, ссылка — сама карточка */
.trailer-fallback[hidden] { display: none; }
.trailer-fallback {
    display: flex; flex-direction: column; align-items: center; gap: 12px;
    text-align: center; cursor: pointer;
    color: inherit; text-decoration: none;
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); padding: var(--gap-5) var(--gap-4);
    transition: border-color .15s ease, background .15s ease, transform .1s ease;
}
.trailer-fallback:hover { border-color: var(--accent-red); background: #1b2029; }
.trailer-fallback:active { transform: scale(0.99); }
.trailer-fallback:focus-visible { outline: 2px solid var(--accent-red); outline-offset: 3px; }
.trailer-fallback .mascot { max-width: 190px; }
.trailer-fallback-text { color: var(--text-secondary); font-size: var(--fs-3); line-height: 1.45; max-width: 320px; }
.trailer-fallback-cta { color: var(--accent-red); font-weight: 700; font-size: var(--fs-3); }

/* ======================================================================
   Карточка тайтла: блоки, добавленные при перестройке под «решение
   в первом экране». Порядок на странице: мета → авторы → жанры → балл →
   слоган → о чём это → списки. Всё подробное ушло ниже трейлера.
   ====================================================================== */

/* --- Авторы под названием --- */
.detail-authors {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 18px;
    padding: 0 0 var(--gap-2);
    font-size: var(--fs-3);
    color: var(--text-primary);
}

.detail-authors i {
    font-style: normal;
    color: var(--text-secondary);
    margin-right: 6px;
}

/* --- Наш балл крупно --- */
.score-block { display: flex; flex-direction: column; gap: var(--gap-2); padding: var(--gap-1) 0 var(--gap-2); }

.score-line {
    display: flex;
    align-items: baseline;
    gap: var(--gap-3);
}

/* Оценки источников: строкой под нашим баллом, без плашки.
   Плашка два на два показывала все четыре источника всегда, и половина
   её была занята прочерками. Здесь пустых строк нет вовсе, а перенос
   на вторую строку происходит сам, когда источников много */
.score-sources {
    display: flex; flex-wrap: wrap; gap: var(--gap-2) var(--gap-4);
    list-style: none; margin: 0; padding: 0;
}
.score-sources li { display: flex; align-items: baseline; gap: 6px; font-size: var(--fs-3); }
.score-sources i { font-style: normal; color: var(--text-secondary); }
.score-sources b { font-weight: 700; font-variant-numeric: tabular-nums; }

/* Значок «сколько голосов». Размер 20×20 маловат для пальца, поэтому
   область нажатия растянута невидимым полем через ::after — сам кружок
   при этом остаётся мелким и не спорит с оценкой за внимание */
.score-votes {
    position: relative;
    width: 18px; height: 18px; flex: none;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--card-border); border-radius: 50%;
    background: transparent; color: var(--text-secondary);
    font: 700 .68rem/1 Georgia, 'Times New Roman', serif;
    cursor: pointer; padding: 0;
    transition: border-color .15s ease, color .15s ease;
}
.score-votes::after {
    content: ''; position: absolute; inset: -9px;   /* до 36×36 под палец */
}
.score-votes:hover { border-color: var(--text-secondary); color: var(--text-primary); }
.score-votes:focus-visible { outline: 2px solid var(--accent-red); outline-offset: 2px; }

/* Подсказка. Живёт в конце body, а не рядом с кнопкой: внутри строки
   с `overflow` её обрезало бы краем, а позицию всё равно считает скрипт */
.score-hint {
    position: fixed; z-index: 300;
    max-width: min(280px, calc(100vw - var(--gap-6)));
    padding: 8px var(--gap-3);
    background: #1b2029; border: 1px solid var(--card-border);
    border-radius: var(--r-sm); box-shadow: var(--shadow-pop);
    color: var(--text-primary); font-size: var(--fs-2); line-height: 1.35;
    pointer-events: none; opacity: 0; transform: translateY(4px);
    transition: opacity .15s ease, transform .15s ease;
}
.score-hint.is-shown { opacity: 1; transform: none; }
.score-hint[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
    .score-hint { transition: none; }
}

/* Пояснение к баллу: свёрнуто, потому что читают его один раз,
   а строку оно занимало на каждой странице */
.score-why > summary {
    list-style: none; cursor: pointer;
    font-size: var(--fs-2); color: var(--text-secondary);
    text-decoration: underline; text-underline-offset: 3px;
    width: max-content;
}
.score-why > summary::-webkit-details-marker { display: none; }
.score-why p { margin-top: var(--gap-2); font-size: var(--fs-2); line-height: 1.45; color: var(--text-secondary); }

.score-main { display: flex; align-items: baseline; gap: 6px; }

/* На странице тайтла лапка крупнее текста блока: она стоит рядом
   с числом в 2.1rem, а не внутри строки. По центру, а не по базовой
   линии, — у отпечатка её попросту нет. */
.score-main .paw { width: 1.5rem; height: 1.5rem; align-self: center; }

.score-main b {
    font-size: var(--fs-8);
    font-weight: 800;
    line-height: 1;
    color: var(--accent-orange);
    font-variant-numeric: tabular-nums;
}

.score-main i { font-style: normal; font-size: var(--fs-3); color: var(--text-secondary); }

.score-caption {
    margin-left: 6px;
    font-size: var(--fs-1);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-secondary);
}


/* --- Лента кадров --- */
.frame-strip {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    margin: 0 -20px;
    padding: 2px 20px 6px;
    scrollbar-width: none;
}

.frame-strip::-webkit-scrollbar { display: none; }

.frame-shot {
    flex: 0 0 auto;
    width: min(86%, 420px);
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--r-sm);
    border: 1px solid var(--card-border);
    scroll-snap-align: center;
    background: var(--card-bg);
}

/* --- Ключевые слова --- */
.keyword-flex {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: var(--gap-1) 0 var(--gap-3);
}



/* --- Голоса и пояснение к баллу --- */


/* --- Награды --- */
.awards-line {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: var(--gap-3) 0 0;
    padding: 9px var(--gap-3);
    border-radius: var(--r-sm);
    background: rgba(255, 152, 0, .08);
    border: 1px solid rgba(255, 152, 0, .22);
    color: var(--accent-orange);
    font-size: var(--fs-3);
    font-weight: 700;
}

.awards-icon { font-size: var(--fs-4); }

/* ======================================================================
   Движение
   ----------------------------------------------------------------------
   Правило одно: анимируются только transform и opacity. Всё остальное
   заставляет браузер пересчитывать раскладку, а на телефоне это тот самый
   рывок, из-за которого футер моргал между карточками.

   Длительности короткие намеренно. Интерфейс, которым пользуются свайпами,
   не должен заставлять ждать свою анимацию: 160–220 мс читается как
   «отозвалось», 400 мс — уже как «тормозит».
   ====================================================================== */

/* Переключение экранов: появление со сдвигом снизу */
.app-screen:not([hidden]) { animation: screen-in .22s cubic-bezier(.2,.8,.3,1) both; }

@keyframes screen-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

/* Нижнее меню: активный пункт подпрыгивает, чтобы касание было заметно */
.nav-item svg { transition: transform .2s cubic-bezier(.34,1.56,.64,1); }
.nav-item.active svg { transform: translateY(-2px) scale(1.12); }
.nav-item:active svg { transform: scale(.92); }

/* Лестничное появление карточек в списках и строк поиска.
   Задержка задаётся переменной из JS и обрывается на десятом элементе:
   дальше человек всё равно листает, а ждать полсекунды ради красоты
   он не подписывался. */
.list-card, .search-row {
    animation: item-in .22s ease both;
    animation-delay: calc(var(--i, 0) * 28ms);
}

@keyframes item-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* Скелеты вместо слова «Загрузка…»: место под карточки занято сразу,
   и список не прыгает, когда данные приезжают */
.skeleton-card {
    border-radius: var(--r-md);
    background: linear-gradient(100deg, #161b22 30%, #1d242e 50%, #161b22 70%);
    background-size: 220% 100%;
    animation: skeleton-sweep 1.15s linear infinite;
    aspect-ratio: 2 / 3.32;
}
.skeleton-row { height: 92px; border-radius: var(--r-md); }

@keyframes skeleton-sweep {
    from { background-position: 180% 0; }
    to   { background-position: -80% 0; }
}

/* Уход на страницу тайтла: короткое затухание вместо рывка */
body.is-leaving { opacity: 0; transition: opacity .14s ease; }

/* Приход на страницу тайтла: содержимое проявляется.
   Анимируется КОНТЕЙНЕР, а не body. Анимация с transform делает элемент
   containing block для всего, что внутри с position: fixed, — и нижнее
   меню переставало быть прижатым к экрану, уезжая в конец документа. */
.detail-container { animation: screen-in .26s cubic-bezier(.2,.8,.3,1) both; }

@media (prefers-reduced-motion: reduce) {
    .app-screen:not([hidden]), .list-card, .search-row, .detail-container { animation: none; }
    .skeleton-card { animation: none; }
    .nav-item svg { transition: none; }
    body.is-leaving { transition: none; }
}

/* --- Обратная связь из профиля --- */
/* ======================================================================
   Поля и действия в профиле: «Написать нам», «Удалить аккаунт»
   ----------------------------------------------------------------------
   Были собраны из чего попало: поле ввода с классом feedback-text
   и в форме письма, и в подтверждении удаления, кнопка отправки —
   серой полосой во всю ширину, из-за чего читалась как «закрыть»,
   а не как «отправить». Подпись про почту стояла над полем отдельным
   абзацем и висела в воздухе.

   Здесь три вещи на оба блока: поле с подписью и счётчиком, ряд
   действия и кнопка. Одинаковые — потому что это одинаковые по смыслу
   места, и разными они были только по недосмотру.
   ====================================================================== */
.field { display: flex; flex-direction: column; gap: 6px; margin-top: var(--gap-3); }
.field-label { font-size: var(--fs-2); color: var(--text-secondary); }
.field-input {
    width: 100%;
    box-sizing: border-box;
    padding: 12px var(--gap-3);
    border-radius: var(--r-sm);
    /* Поле темнее карточки, а не вровень с ней: так видно, где можно
       писать, без лишней рамки */
    border: 1px solid var(--card-border);
    background: rgba(0, 0, 0, .25);
    color: var(--text-primary);
    font: inherit;
    font-size: var(--fs-3);
    line-height: 1.5;
    resize: vertical;
    transition: border-color .15s ease, box-shadow .15s ease;
}
/* Подсказка — тот же приглушённый цвет, что у подписей. Было
   #5c6470: 2.55:1 на фоне поля при норме 4.5, то есть её просто
   не читали. Токен даёт 5.6:1 и ничего не путает с введённым
   текстом — тот набран --text-primary */
.field-input::placeholder { color: var(--text-secondary); }
.field-input:focus {
    outline: none;
    border-color: var(--accent-orange);
    box-shadow: 0 0 0 3px rgba(255, 152, 0, .12);
}
.field-foot {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--gap-2); font-size: var(--fs-1); color: var(--text-secondary);
}
.field-note b { color: var(--text-primary); font-weight: 600; }
.field-counter { flex: none; font-variant-numeric: tabular-nums; }
.field-counter.near-limit { color: var(--accent-orange); }

.action-row {
    display: flex; align-items: center; gap: var(--gap-3);
    margin-top: var(--gap-4); flex-wrap: wrap;
}
.action-note { font-size: var(--fs-2); color: var(--text-secondary); }

/* Кнопка действия блока. По ширине содержимого, а не во всю строку:
   во всю строку у нас главные кнопки экрана, а это не они */
.btn-primary {
    background: var(--accent-orange); color: #1a1205;
    border: none; border-radius: var(--r-sm);
    padding: 11px var(--gap-5);
    font: inherit; font-size: var(--fs-3); font-weight: 700;
    cursor: pointer;
    transition: background .15s ease, opacity .15s ease;
}
.btn-primary:hover:not(:disabled) { background: #ffab33; }
.btn-primary:disabled {
    background: transparent; color: var(--text-secondary);
    box-shadow: inset 0 0 0 1px var(--card-border);
    cursor: default;
}

/* Перечень того, что исчезнет, вместо абзаца сплошным текстом:
   решение принимают, пересчитав пункты, а не прочитав предложение */
.danger-list {
    list-style: none; display: flex; flex-direction: column; gap: 6px;
    margin: 0; padding: 0; font-size: var(--fs-3); color: var(--text-primary);
}
.danger-list li { display: flex; align-items: baseline; gap: var(--gap-2); }
/* Раздел удаления: воздух между частями и кнопка во всю ширину.
   Без отступов перечисление того, что исчезнет, слипалось с фразой
   про резервные копии в сплошной текст, а кнопка стояла в углу —
   и выглядела как второстепенная, хотя она здесь единственная
   и необратимая. */
.danger-body > * + * { margin-top: var(--gap-3); }
.danger-body .action-row {
    flex-direction: column; align-items: stretch; gap: var(--gap-2);
}
.danger-body .action-row .btn-danger { width: 100%; }
.danger-body .action-note { text-align: center; }

.danger-list li::before {
    content: ''; flex: none; width: 5px; height: 5px; border-radius: 50%;
    background: var(--dislike-color); opacity: .7;
}

/* ======================================================================
   Широкий экран
   ----------------------------------------------------------------------
   До этого вся вёрстка была жёстко зажата в 420 пикселей по центру,
   и на ноутбуке приложение занимало треть ширины, а вокруг лежало
   пустое поле. Первое, что об этом думает человек, — «сайт недоделан».

   Колоду при этом растягивать НЕЛЬЗЯ: свайп-карточка узкая по своей
   природе, широкая карточка перестаёт быть карточкой. Поэтому широкому
   экрану даётся не размер колоды, а то, чего у него не было вовсе:
   меню наверху вместо телефонного нижнего, больше колонок в списках
   и в поиске, нормальные поля по краям.

   Кнопок действий у колоды на десктопе сознательно нет: свайп мышью
   работает, а десктоп для этого сервиса — не основной сценарий.
   ====================================================================== */
/* ----------------------------------------------------------------------
   Планшет: приложение перестаёт быть узкой колонкой
   ----------------------------------------------------------------------
   До 900 px экран ограничен 420 пикселями, и на планшете 768 это
   выглядело так: узкий столбец по центру, а по бокам пустота в треть
   ширины. Сетка списков тут ни при чём — она подстраивается, просто
   подстраиваться было не подо что.

   Меню остаётся внизу: планшет держат в руках так же, как телефон,
   и переносить навигацию наверх рано — это делает медиазапрос 900 px,
   там уже мышь.

   Колода не расширяется вовсе. Карточка во всю ширину планшета —
   это не карточка, а плакат: смахивать её неудобно, а читать нечего,
   текста столько же. */
@media (min-width: 600px) and (max-width: 899px) {
    /* Только ширина. Боковые отступы задаёт `.screen-scroll` вместе
       с нижним, и переписать их здесь значит отобрать у прокручиваемых
       экранов место под нижнее меню — содержимое уедет под него */
    .app-screen { max-width: 720px; }
    #screenDeck { max-width: 420px; }
}

@media (min-width: 900px) {
    /* Меню переезжает в шапку целиком — сам элемент переносит туда JS
       (см. `moveNavForWideScreen` в index.html). Здесь только его вид:
       из столбика «значок над подписью» он становится строкой. */
    .header {
        max-width: none;
        height: 60px;
        padding: 0 var(--gap-5);
        gap: var(--gap-5);
    }
    .header .bottom-nav {
        position: static; transform: none;
        width: auto; max-width: none; height: auto;
        margin-left: auto; padding-bottom: 0;
        background: none; border-top: none;
        gap: var(--gap-1); justify-content: flex-end;
    }
    .header .bottom-nav .nav-item {
        flex: 0 0 auto; flex-direction: row; gap: var(--gap-2);
        padding: var(--gap-2) var(--gap-3);
        border-radius: var(--r-sm); font-size: var(--fs-3);
    }
    .header .bottom-nav .nav-item:hover:not(.active) { background: #1b2029; }
    .header .bottom-nav .nav-item svg { width: 20px; height: 20px; }
    /* Подпрыгивание значка на десктопе лишнее: там нет касания,
       ради отклика на которое оно и заводилось */
    .header .bottom-nav .nav-item.active svg { transform: none; }

    /* Раз меню наверху, снизу под него место резервировать не нужно */
    .screen-scroll { padding-bottom: var(--gap-6); }
    .swipe-legend { margin-bottom: var(--gap-4); }
    .detail-container { padding-bottom: var(--gap-6); }

    /* Колода остаётся узкой, всё остальное получает ширину */
    .app-screen { max-width: 1120px; padding: 0 var(--gap-5); }
    #screenDeck { max-width: 460px; }
    .lists-grid { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
    .search-results { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: var(--gap-3); }
    .detail-container { max-width: 760px; }

    /* Наведение мышью: на телефоне его нет, и заводить эти правила
       глобально значит ловить «залипший» hover после касания */
    .list-card:hover { transform: translateY(-2px); }
    .search-row:hover { border-color: #43505f; }
}

@media (min-width: 1280px) {
    .app-screen { max-width: 1280px; }
    .detail-container { max-width: 840px; }
}

/* ======================================================================
   Подборка «вы это смотрели — как вам?»
   ----------------------------------------------------------------------
   Поверх всего, но не на весь экран: это не отдельный раздел приложения,
   а короткий вопрос. Полноэкранная версия читалась бы как «вас увели
   куда-то», а увести человека с колоды мы не собирались.
   ====================================================================== */
/* ---------- Одна кнопка, которая разъезжается на две ----------
   «Поделиться кодом» и «Ввести код» — два выхода из одного решения,
   и стоящие рядом с первой секунды они заставляли выбирать раньше,
   чем человек понял, о чём речь. Сначала одна кнопка «Связать списки»,
   и только по нажатию она расходится на два варианта. */
.partner-split { position: relative; }
.partner-split-main {
    width: 100%;
    transition: opacity .18s ease, transform .28s cubic-bezier(.2, .8, .3, 1);
}
.partner-split.is-open .partner-split-main {
    position: absolute; inset: 0;
    opacity: 0; transform: scaleX(.86);
    pointer-events: none;
}
.partner-split-pair { display: flex; gap: var(--gap-2); }
.partner-split:not(.is-open) .partner-split-pair {
    position: absolute; inset: 0; visibility: hidden;
}
.partner-split-pair > button {
    flex: 1 1 0; min-width: 0;
    opacity: 0;
    transition: opacity .2s ease .06s,
                transform .28s cubic-bezier(.2, .8, .3, 1);
}
/* Обе выходят из середины — оттуда, где была одна кнопка */
.partner-split-pair > button:first-child { transform: translateX(50%); }
.partner-split-pair > button:last-child  { transform: translateX(-50%); }
.partner-split.is-open .partner-split-pair > button { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    .partner-split-main,
    .partner-split-pair > button { transition: none; }
    .partner-split-pair > button { transform: none; }
}

/* ================= Выезжающий снизу лист =================
   Общая основа для подборки оценок и обращения к нам: оба разговора
   короткие, оба прерывают то, чем человек занят, и оба должны так же
   легко закрываться. Своя копия этих правил у второго расходилась бы
   с первым на первой же правке. */
.sheet-overlay {
    position: fixed; inset: 0; z-index: 250;
    display: flex; align-items: flex-end; justify-content: center;
    background: rgba(0, 0, 0, .72);
    padding: var(--gap-4);
}
.sheet-overlay[hidden] { display: none; }

.sheet {
    width: 100%; max-width: 420px;
    max-height: 92dvh; overflow-y: auto;
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-lg);
    padding: var(--gap-4) var(--gap-4) calc(var(--gap-4) + var(--safe-bottom));
    display: flex; flex-direction: column; gap: var(--gap-3);
    animation: sheet-in .24s cubic-bezier(.2,.8,.3,1) both;
}

@keyframes sheet-in {
    from { opacity: 0; transform: translateY(24px); }
    to   { opacity: 1; transform: none; }
}

.sheet-head { display: flex; align-items: flex-start; gap: var(--gap-3); }
.sheet-head b { font-size: var(--fs-5); font-weight: 800; line-height: 1.25; }
.sheet-close {
    background: none; border: 0; color: var(--text-secondary);
    font-size: var(--fs-7); line-height: 1; cursor: pointer; padding: 0 4px;
    margin-left: auto;
}

/* Кот в листе обращения сидит спиной и смотрит через плечо: разговор
   начинает человек, а не он. Картинка здесь не иллюстрация к тексту,
   а сам собеседник, поэтому она стоит первой и крупно. */
.sheet-cat { max-width: 168px; margin: 0 auto; }

/* Кнопка в листе — во всю ширину.
   Лист узкий, и кнопка в его углу читалась как второстепенная, хотя
   она здесь единственное действие. Заодно по ней проще попасть
   большим пальцем: лист выезжает снизу, и низ экрана — его зона. */
.sheet .action-row { flex-direction: column; align-items: stretch; }
.sheet .action-row .btn-primary { width: 100%; }
.sheet .action-note { text-align: center; }

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

.story-top { display: flex; align-items: center; gap: var(--gap-3); }

/* Полоски сверху, как в сториз. Видимый конец — единственное, что
   отличает короткий вопрос от бесконечной ленты */
.story-progress { display: flex; gap: 4px; flex: 1; }
.story-progress i {
    /* 2px — скругление линии высотой 3 px, а не угол компонента:
       здесь токен превратил бы полоску в капсулу */
    flex: 1; height: 3px; border-radius: 2px;
    background: var(--card-border); transition: background .2s ease;
}
.story-progress i.done { background: var(--watchlist-color); }
.story-progress i.now { background: var(--text-primary); }



.story-body { display: flex; flex-direction: column; gap: var(--gap-2); align-items: center; text-align: center; }
.story-poster {
    width: 108px; aspect-ratio: 2/3; object-fit: cover;
    border-radius: var(--r-sm); background: #21262d;
    box-shadow: var(--shadow-card);
}
.story-title { font-size: var(--fs-5); font-weight: 800; line-height: 1.25; }
.story-meta { font-size: var(--fs-2); color: var(--text-secondary); }
.story-ask { font-size: var(--fs-3); color: var(--text-secondary); margin-top: var(--gap-1); }

.story-skip {
    background: none; border: 0; color: var(--text-secondary);
    font-size: var(--fs-3); text-decoration: underline; text-underline-offset: 3px;
    cursor: pointer; padding: var(--gap-2); align-self: center;
}

.story-done { display: flex; flex-direction: column; align-items: center; gap: var(--gap-2); text-align: center; }
.story-done h3 { font-size: var(--fs-5); font-weight: 800; }
.story-done p { font-size: var(--fs-3); color: var(--text-secondary); line-height: 1.45; max-width: 300px; }

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

/* ======================================================================
   Подсказка про фильтры
   ----------------------------------------------------------------------
   Полоской над нижним меню, а не окном поверх всего и не блоком в потоке.

   Окно человек закрывает не читая. Блок в потоке отнял бы у колоды
   свою высоту — карточка на телефоне и так занимает весь экран,
   и подсказка сплющила бы ровно то, ради чего человек пришёл.
   Полоска же остаётся в поле зрения, пока он решает, нужна она ему
   или нет, и свайпать при этом не мешает.
   ====================================================================== */
.hint-card {
    position: fixed;
    left: 50%; transform: translateX(-50%);
    bottom: calc(var(--nav-space) + 8px);
    z-index: 60;
    width: calc(100% - 24px); max-width: 380px;
    box-sizing: border-box;
    padding: var(--gap-3) var(--gap-3) var(--gap-3) 0;
    background: var(--card-bg);
    border: 1px solid var(--accent-orange);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-card);
    display: grid;
    grid-template-columns: 84px 1fr;
    grid-template-areas: 'cat text' 'cat go';
    gap: 8px var(--gap-2);
    align-items: center;
    animation: hint-in .26s cubic-bezier(.2,.8,.3,1) both;
}
.hint-card[hidden] { display: none; }

@keyframes hint-in {
    from { opacity: 0; transform: translate(-50%, 14px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}

.hint-cat { grid-area: cat; width: 84px; max-width: 84px; margin: 0; align-self: end; }
.hint-text { grid-area: text; }
.hint-text b { display: block; font-size: var(--fs-3); font-weight: 800; margin-bottom: 2px; }
.hint-text p { font-size: var(--fs-2); line-height: 1.4; color: var(--text-secondary); }

.hint-go {
    grid-area: go; justify-self: start;
    background: var(--accent-orange); color: #16130c;
    border: 0; border-radius: var(--r-sm);
    padding: 8px 14px; font-size: var(--fs-3); font-weight: 700;
    cursor: pointer; font-family: inherit;
}
.hint-go:active { background: #e08900; }

.hint-close {
    position: absolute; top: 2px; right: 4px;
    background: none; border: 0; color: var(--text-secondary);
    font-size: var(--fs-6); line-height: 1; cursor: pointer; padding: 4px 8px;
}

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

/* ======================================================================
   Правовая информация в профиле и уведомление в приветствии (A9–A11)
   ====================================================================== */
/* Правовой раздел намеренно тихий. Оранжевые ссылки притягивали взгляд
   сильнее, чем всё остальное в профиле, — а человек пришёл смотреть кино.
   Раскрытие должно находиться не спрашивая, но не спорить за внимание
   с тем, ради чего сюда заходят: цвет обычного текста, подчёркивание
   вместо заливки цветом. */
.legal-links { list-style: none; display: flex; flex-direction: column; gap: var(--gap-3); }
.legal-links li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
.legal-links a {
    color: var(--text-primary); font-size: var(--fs-3); font-weight: 500;
    text-decoration: underline; text-decoration-color: var(--card-border);
    text-underline-offset: 3px;
    transition: text-decoration-color .15s ease, color .15s ease;
}
.legal-links a:hover { text-decoration-color: var(--text-secondary); }
.legal-link-lead {
    flex-basis: 100%;
    font-size: var(--fs-2); line-height: 1.4; color: var(--text-secondary);
}
.legal-soon {
    font-size: var(--fs-1); color: var(--text-secondary);
    border: 1px solid var(--card-border); border-radius: var(--r-sm); padding: 1px 7px;
}

/* Уведомление в приветствии. Мелко, но не спрятано: по 277-ФЗ факт
   применения рекомендательных технологий скрывать нельзя, а перекрывать
   им приветствие незачем — это уведомление, а не согласие. */
.welcome-legal {
    margin-top: var(--gap-2);
    font-size: var(--fs-2);
    color: var(--text-secondary);
}
.welcome-legal a { color: var(--text-secondary); text-decoration: underline; }

/* Подпись источника под фрагментом описания (ст. 1274 п. 1).
   Отдельной строкой и мельче текста: это не часть описания,
   а указание, чьё оно. */
/* ----------------------------------------------------------------------
   Разбор отложенного
   ----------------------------------------------------------------------
   Карточка внутри разбора — та же, что в колоде, и собирается тем же
   кодом. Разница в одном: в колоде она лежит стопкой и потому
   `position: absolute; inset: 0`, а здесь стоит одна в потоке листа
   и высоту должна задать себе сама.

   Высота в `vh`, а не в пикселях: лист и так занимает почти весь
   экран, и карточка обязана поместиться вместе с вопросом сверху
   и подсказками жестов снизу — иначе жест некуда делать. */
.revision-card {
    position: relative; inset: auto;
    width: 100%; max-width: 300px;
    height: 54vh; max-height: 430px;
    margin: 0 auto;
    will-change: transform;
}

/* Подсказки жестов у разбора те же, что под колодой, но стоят внутри
   листа — значит нижний отступ им не нужен: под ними край листа,
   а не полоса жестов телефона */
#revisionOverlay .swipe-legend { margin: 12px 0 0; }

/* Подпись под цитатой: имя источника и кружок с пояснением.
   Строкой, а не блоком — кнопка должна стоять рядом со словом,
   а не под ним. */
.description-source {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: var(--gap-2);
    font-size: var(--fs-1);
    color: var(--text-secondary);
    opacity: .8;
}

/* Две ссылки в подвале одной строкой. Раньше «Открыть на TMDB» стояла
   отдельной строкой посреди страницы и была третьим упоминанием
   источника на одном экране. */
.attribution-links {
    width: 100%;
    text-align: center;
    font-size: var(--fs-1);
    color: var(--text-secondary);
}
.attribution-links a { color: inherit; }

/* Атрибуция TMDB в подвале страницы тайтла (A9).
   Их условия требуют, чтобы логотип был менее заметен, чем логотип
   сервиса, и запрещают его перекрашивать, растягивать, отражать
   и поворачивать — поэтому здесь нет ни filter, ни transform. */
/* Строка атрибуции TMDB обязательна их условиями и удалению не подлежит
   (A9). Зато она не обязана быть заметной: раньше подвал занимал экран
   рамкой, отступами и парой строк. Теперь это одна приглушённая строка
   мелким кеглем — требование выполнено, внимание не отнято. */
.detail-attribution {
    display: flex; align-items: center; justify-content: center;
    flex-wrap: wrap; gap: 6px var(--gap-2);
    /* Ширина как у содержимого страницы, а не своя: иначе линия-разделитель
       шире текста над ней и выглядит чужой */
    max-width: 480px;
    margin: var(--gap-4) auto calc(var(--nav-space) + var(--gap-2));
    padding: var(--gap-3) 20px 0;
    border-top: 1px solid rgba(255, 255, 255, .05);
    text-align: center;
}
/* Прозрачности у логотипа нет намеренно. Условия TMDB запрещают менять
   его цвет, а полупрозрачность — это и есть изменение цвета. Требование
   «логотип менее заметен, чем логотип MovieMatch» выполняется размером:
   14 px против 20 px у нашего названия в шапке. */
.detail-attribution img { height: 14px; width: auto; display: block; }
.detail-attribution p { margin: 0; font-size: var(--fs-0); line-height: 1.4; color: #6b727c; }
.detail-attribution a { font-size: var(--fs-0); color: #6b727c; text-decoration: underline; text-underline-offset: 2px; }
.detail-attribution a:hover { color: var(--text-secondary); }

/* ======================================================================
   Плашка про хранение данных (П4)
   ----------------------------------------------------------------------
   Не куки-баннер. Тот спрашивает согласие на нетехнические куки, а сервис
   ставит ровно две технические, без которых не работает. Здесь другое
   сообщение: списки лежат в браузере — значит, очистка данных сайта
   их унесёт. Это уведомление, а не согласие, поэтому оно не перекрывает
   экран и закрывается совсем.
   ====================================================================== */
.storage-note {
    position: fixed;
    left: 50%; transform: translateX(-50%);
    bottom: calc(var(--nav-space) + 8px);
    z-index: 55;
    width: calc(100% - 24px); max-width: 420px;
    box-sizing: border-box;
    display: flex; align-items: flex-start; gap: var(--gap-2);
    padding: 10px var(--gap-3);
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: var(--r-sm);
    box-shadow: var(--shadow-card);
}
.storage-note[hidden] { display: none; }
.storage-note p { font-size: var(--fs-2); line-height: 1.45; color: var(--text-secondary); }
.storage-note a { color: var(--accent-orange); }

.storage-close {
    background: none; border: 0; color: var(--text-secondary);
    font-size: var(--fs-5); line-height: 1; cursor: pointer;
    padding: 0 2px; margin-left: auto; flex-shrink: 0;
}

/* Подсказка про фильтры важнее: она про то, что человек делает прямо
   сейчас. Если обе всплыли разом, плашка про хранение уступает место */
.hint-card:not([hidden]) ~ .storage-note { display: none; }

/* ======================================================================
   Связь списков с другим человеком (C31)
   ----------------------------------------------------------------------
   Первая правка показала главное: буква сама по себе не говорит ничего.
   Кружок «М» поверх постера читался как часть обложки, а что он значит —
   приходилось вспоминать. Поэтому везде теперь буква И статус рядом:
   «М — Не подходит».

   Цвета те же, что у собственных списков: зелёный «буду смотреть»,
   синий «смотрел», красный «не подходит». Вторую палитру человеку
   запоминать не надо.

   Полная почта не показывается нигде — это чужие персональные данные,
   и для кружка она не нужна.
   ====================================================================== */

/* Кружок с буквой. Живёт внутри чипа или строки, самостоятельно
   не встречается: один он бессмыслен */
.partner-dot {
    display: inline-flex; align-items: center; justify-content: center;
    width: 17px; height: 17px; flex: none;
    border-radius: 50%;
    font-style: normal; font-size: var(--fs-0); font-weight: 800; line-height: 1;
    /* Тёмная буква на цветной заливке: на светлой она терялась бы
       на любом из трёх цветов */
    color: #0d1117;
    background: var(--text-secondary);
}

/* Заливка кружка задаётся РОДИТЕЛЕМ, а не currentColor.
   С currentColor выходил круг: буква тёмная, значит currentColor тоже
   тёмный, значит и фон тёмный — кружок пропадал целиком. Наступил
   на это сразу же, на странице тайтла */
.list-watchlist > .partner-dot { background: var(--watchlist-color); }
.list-watched   > .partner-dot { background: var(--watched-color); }
.list-disliked  > .partner-dot { background: var(--dislike-color); }

/* ---------- Колода: чип под плашкой типа ---------- */
/* Фон и рамка — как у «Фильм» и «12+»: тёмная плашка, цветом только
   подсвечено. Цветная заливка ставила его в один ряд со штампами
   свайпа, хотя он про другое — где тайтл лежит СЕЙЧАС, а не куда
   уедет.
   Занимает свою строку: вплотную к «Фильм» слова «Не подходит»
   читаются как продолжение его подписи */
.chip-partner {
    display: inline-flex; align-items: center; gap: 6px;
    font-weight: 700;
}

/* Пустой разрыв строки внутри ряда чипов.
   `flex-basis: 100%` на самом чипе растягивал его во всю ширину:
   в гибком контейнере базовый размер главнее `width: fit-content`,
   и справа оставалась пустая подложка. Разрыв делает то же самое —
   переносит чип на новую строку, — но ширину ему оставляет
   по содержимому. */
.chips-break { flex-basis: 100%; height: 0; margin: 0; padding: 0; border: 0; }
.chip-partner.list-watchlist { color: var(--watchlist-color); border-color: rgba(46,160,67,.45); }
.chip-partner.list-watched   { color: var(--watched-color);   border-color: rgba(88,166,255,.45); }
.chip-partner.list-disliked  { color: var(--dislike-color);   border-color: rgba(248,81,73,.45); }

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

   Ряд с названием: название сжимается, метка держит свою ширину */
.search-head { display: flex; align-items: flex-start; gap: var(--gap-2); }
.search-head .search-title { flex: 1; min-width: 0; }

.search-partner {
    flex: none;
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 7px 2px 3px;
    border: 1px solid var(--card-border); border-radius: var(--r-sm);
    background: rgba(255, 255, 255, .03);
    font-size: var(--fs-0); font-weight: 700; white-space: nowrap;
    color: var(--text-secondary);
}
.search-partner > .partner-dot { width: 14px; height: 14px; font-size: .55rem; }
.search-partner.list-watchlist { color: var(--watchlist-color); border-color: rgba(46,160,67,.45); }
.search-partner.list-watched   { color: var(--watched-color);   border-color: rgba(88,166,255,.45); }
.search-partner.list-disliked  { color: var(--dislike-color);   border-color: rgba(248,81,73,.45); }

/* ---------- Мои списки: плашка в углу постера ---------- */
/* Та же плашка, что в колоде, только мельче: тёмный фон, цветная
   рамка, цветной текст.

   Стоит в левом верхнем углу обложки. Сперва она была строкой под
   названием, но там её приходилось искать: плитки в сетке читаются
   сверху вниз картинками, а подписи проглядывают вторым проходом.

   Фон здесь непрозрачным быть обязан — под ним живая обложка,
   и полупрозрачная плашка на светлом кадре теряется вместе
   с текстом. Переменная --scrim для этого и заведена */
.partner-note {
    position: absolute; top: 8px; left: 8px; z-index: 2;
    max-width: calc(100% - 16px);
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px 8px;
    border: 1px solid var(--card-border); border-radius: var(--r-sm);
    background: var(--scrim);
    font-size: var(--fs-0); font-weight: 700;
    color: var(--text-secondary);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.partner-note > .partner-dot { width: 15px; height: 15px; font-size: .58rem; }
.partner-note.list-watchlist { color: var(--watchlist-color); border-color: rgba(46,160,67,.45); }
.partner-note.list-watched   { color: var(--watched-color);   border-color: rgba(88,166,255,.45); }
.partner-note.list-disliked  { color: var(--dislike-color);   border-color: rgba(248,81,73,.45); }

/* ---------- Страница тайтла: строка под кнопками списков ---------- */
.partner-line {
    display: flex; align-items: center; gap: var(--gap-2);
    margin-top: var(--gap-1);
    padding: var(--gap-2) var(--gap-3);
    border-radius: var(--r-sm);
    background: rgba(255, 255, 255, .03);
    /* Полоска слева цветом списка: тот же приём, что у карточек
       дашборда, — цвет читается даже боковым зрением */
    border-left: 3px solid currentColor;
    color: var(--text-secondary);
}
.partner-line.list-watchlist { color: var(--watchlist-color); }
.partner-line.list-watched   { color: var(--watched-color); }
.partner-line.list-disliked  { color: var(--dislike-color); }
.partner-line > .partner-dot { width: 20px; height: 20px; font-size: var(--fs-1); }
.partner-line-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.partner-line-text b { font-size: var(--fs-3); font-weight: 700; }
/* Подпись серая всегда: цвет статуса уже несут кружок, полоска
   и само слово — четвёртый раз он только шумит */
.partner-line-sub { font-size: var(--fs-1); color: var(--text-secondary); }

/* ---------- Блок в профиле ---------- */
.partner-box {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: var(--r-md); padding: var(--gap-4);
    display: flex; flex-direction: column; gap: var(--gap-3);
}
/* У самого блока правило скрытия тоже обязательно, а не только
   у состояний внутри. Без него `hidden` в разметке не работает —
   `display: flex` выше по силе, — и у человека без аккаунта
   в профиле висела пустая рамка. Нашлось 27.09 тестом,
   заведённым по другому поводу */
.partner-box[hidden] { display: none; }
.partner-state[hidden] { display: none; }
.partner-state { display: flex; flex-direction: column; gap: var(--gap-2); }
.partner-head { display: flex; flex-direction: column; gap: 4px; }
.partner-head b { font-size: var(--fs-4); font-weight: 800; }

/* Две кнопки рядом: «поделиться» — главное действие, «ввести» — ответное */
.partner-actions { display: flex; gap: var(--gap-2); flex-wrap: wrap; margin-top: var(--gap-2); }
.partner-actions .btn-primary,
.partner-actions .btn-reset { flex: 1; min-width: 150px; width: auto; }

/* Кнопки состояний связи — столбиком и одной ширины.
   Раньше они наследовали общий .action-row: главная кнопка шла
   по содержимому, а серая растягивалась во всю ширину, и на телефоне
   получалась лесенка из разных прямоугольников. Ширина у кнопок,
   стоящих друг под другом, должна быть одна — иначе глаз читает
   разницу как разницу в важности. */
.partner-box .action-row {
    flex-direction: column;
    align-items: stretch;
    gap: var(--gap-2);
}
.partner-box .action-row .btn-primary,
.partner-box .action-row .btn-reset,
.partner-box .action-row .btn-danger { width: 100%; }
.partner-box .action-note { text-align: center; }

/* Код набран моноширинным и крупно: его переписывают руками и диктуют */
.partner-code {
    font-family: ui-monospace, "SF Mono", Consolas, "Liberation Mono", monospace;
    font-size: var(--fs-5); font-weight: 700; letter-spacing: .06em;
    color: var(--text-primary);
    background: rgba(0, 0, 0, .25);
    border: 1px dashed var(--card-border); border-radius: var(--r-sm);
    padding: var(--gap-3); text-align: center;
    user-select: all;
    overflow-wrap: anywhere;
}

.partner-row { display: flex; align-items: center; gap: var(--gap-3); }
.partner-avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex: none; border-radius: 50%;
    background: var(--accent-orange); color: #1a1205;
    font-size: var(--fs-5); font-weight: 800;
}
.partner-info { min-width: 0; }
.partner-title { font-weight: 700; font-size: var(--fs-4); }
