/* ===================================================================
   Каталог: панель управления выдачей, снятые фильтры, карточка сервера,
   пагинация.

   Здесь же живут АТОМЫ карточки сервера — имя, характеристики, кнопка IP,
   кнопка голоса. Они нужны трём страницам (каталог, карточка сервера,
   профиль), поэтому и лежат в одном файле, а раскладку под себя каждая
   страница задаёт сама.

   Значения — только из tokens.css.
   =================================================================== */

/* ==================================================================
   1. Подводка посадочной страницы
   ================================================================== */

/* Описание тега/платформы под первым экраном. Была карточка с рамкой над
   списком карточек с рамками; текст описания — это подводка к выдаче, а
   не отдельная сущность, и рамка ей не нужна. */
.servers__landing-text {
    margin: 0 0 var(--stack);
    max-width: var(--measure);
    font-size: var(--fs-body);
    line-height: var(--lh-base);
    color: var(--text-3);
}

/* ==================================================================
   2. Панель управления выдачей

   Одна панель на четыре бывших независимых блока: вкладки, платформа,
   поиск, фильтры. Раньше они стояли друг под другом, разделённые только
   отступами, — глазу не за что было зацепиться, чтобы понять, что это
   один орган управления одним списком.

   .servers-header сохранён: по нему filters.js вешает filters-hidden,
   когда открыта вкладка «Новые» (там фильтров нет).
   ================================================================== */

.servers-header {
    margin-bottom: var(--sp-5);
}

.toolbar {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-3);
    background-color: var(--surface-2);
    background-image: var(--sheen);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
}

/* Верхняя строка: режим выдачи слева, платформа справа. «Что показываем»
   и «на какой платформе» — вопросы одного уровня, и стоят они в одной
   строке; всё, что сужает выдачу, ушло строкой ниже. */
.toolbar__modes {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
}

/* Во вкладке «Новые» фильтровать нечего: выдача — это последние
   добавленные, и поиск с тегами там показывал бы органы управления,
   которые ничего не меняют. Класс ставит filters.js. */
.servers-header.filters-hidden search {
    display: none;
}

/* Нижняя строка панели: поиск тянется, фильтры прижаты вправо. */
.servers__inline-elements {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex-wrap: wrap;
}

.servers__search-bar {
    position: relative;
    display: flex;
    align-items: center;
    flex: 1 1 260px;
    min-width: 0;
}

.servers__search-input {
    box-sizing: border-box;
    width: 100%;
    height: var(--control-h);
    padding: 0 var(--sp-4) 0 var(--sp-9);
    border: 1px solid var(--border-input);
    border-radius: var(--r-md);
    background: var(--surface-3);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-body);
    transition: border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

.servers__search-input::placeholder {
    color: var(--text-4);
}

.servers__search-input:hover {
    border-color: var(--text-4);
}

.servers__search-input:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
    outline: none;
}

/* Иконка внутри поля, а не рядом: поле и так узкое, а лупа снаружи
   отъедала бы у него ширину. */
.servers__search-icon {
    position: absolute;
    left: var(--sp-4);
    width: var(--icon-sm);
    height: var(--icon-sm);
    color: var(--text-4);
    pointer-events: none;
}

.servers__search-input:focus-visible ~ .servers__search-icon,
.servers__search-bar:focus-within .servers__search-icon {
    color: var(--accent);
}

/* Крестик очистки в type="search" рисуется белым квадратом в Chrome —
   на тёмном поле это грязное пятно. Убираем: очистка есть в чипе
   снятого фильтра над выдачей. */
.servers__search-input::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

fieldset.servers__filters {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin: 0;
    padding: 0;
    border: 0;
    flex-wrap: wrap;
}

.servers__version {
    min-width: 0;
}

.version-picker,
.tag-picker {
    position: relative;
}

/* Панель тегов шире пикера версии: в ней три категории чипов, а не
   столбец пунктов. */
.tag-picker__dropdown {
    width: max(100%, 320px);
    padding: var(--sp-3);
}

.tag-picker__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

.tag-picker__divider {
    height: 1px;
    margin: var(--sp-3) 0;
    background: var(--border);
}

/* ==================================================================
   3. Снятые фильтры
   ================================================================== */

/* Обёртка держит только отбивку от выдачи: раскладку ряда задаёт
   .results-bar__filters. Раньше здесь стоял space-between — он разводил
   по краям число найденного и чипы; числа больше нет, разводить нечего. */
.results-bar {
    margin-bottom: var(--sp-4);
}

.results-bar__filters {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex-wrap: wrap;
}

/* Чип снятого фильтра. Крестик внутри самого чипа, а не отдельной
   кнопкой рядом: две цели нажатия шириной в 16px, стоящие вплотную, —
   это промах через раз на телефоне. Здесь вся пилюля и есть кнопка
   «убрать», и её площадь — вся подпись целиком. */
.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    box-sizing: border-box;
    height: var(--control-h-xs);
    padding: 0 var(--sp-3);
    border: 1px solid var(--accent-line);
    border-radius: var(--r-pill);
    background: var(--accent-soft);
    color: var(--accent);
    font-family: inherit;
    font-size: var(--fs-caption);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
    max-width: 100%;
    cursor: pointer;
    transition: background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease);
}

.filter-chip:hover {
    background: var(--accent-softer);
    border-color: var(--accent);
}

/* Ограничение — доля СТРОКИ, а не число знаков: поисковый запрос
   доезжает сюда длиной до ста символов, и любое число знаков было бы
   подобрано наугад. 100% ширины ряда чипов держит его в границах
   раскладки, а перенос делает остальное. */
.filter-chip__text {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.filter-chip__x {
    width: var(--icon-sm);
    height: var(--icon-sm);
    opacity: 0.7;
    flex-shrink: 0;
}

.filter-chip:hover .filter-chip__x {
    opacity: 1;
}

.results-bar__reset {
    padding: 0;
    border: 0;
    background: none;
    color: var(--text-4);
    font-family: inherit;
    font-size: var(--fs-caption);
    font-weight: var(--fw-medium);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    transition: color var(--dur) var(--ease);
}

.results-bar__reset:hover {
    color: var(--text);
}

/* ==================================================================
   4. Список
   ================================================================== */

/* Отступ под списком принадлежит ПАГИНАЦИИ, а не списку: страниц может
   не быть вовсе, и тогда 32px висели между последней карточкой и
   подвалом ни за что. */
.pagination {
    margin-top: var(--stack);
}

.servers__items {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

/* Список в момент подмены. Не opacity: 0 — исчезнувший список читается
   как поломка; полупрозрачный говорит «сейчас будет другой». */
.servers__list.is-loading {
    opacity: 0.5;
    pointer-events: none;
    transition: opacity var(--dur) var(--ease);
}

/* ==================================================================
   5. Карточка сервера

   Сетка из четырёх колонок: ранг · название · характеристики · действие.

   Раньше карточка была стопкой из двух ярусов — шапка с рангом и
   названием, под ней ряд «баннер + характеристики + кнопка». На широком
   экране это давало 150px высоты, из которых половина уходила в воздух
   между ярусами, а колонка чисел не выравнивалась между соседними
   карточками: у одной баннер есть, у другой нет, и ряд метрик уезжал по
   вертикали. Одноимённые величины обязаны стоять друг под другом —
   иначе список из тридцати серверов нельзя просмотреть глазом, только
   прочитать целиком.
   ================================================================== */

.server-card {
    --card-spine: transparent;

    position: relative;
    display: grid;
    grid-template-columns: var(--box-lg) minmax(0, 1fr) auto auto;
    align-items: center;
    gap: var(--sp-4) var(--sp-5);
    box-sizing: border-box;
    padding: var(--pad-card-sm) var(--pad-card);
    background-color: var(--surface-2);
    background-image: var(--sheen);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    transition: border-color var(--dur) var(--ease),
                background-color var(--dur) var(--ease),
                transform var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

.server-card:hover {
    border-color: var(--border-strong);
    background-color: var(--surface-3);
    transform: translateY(-2px);
    box-shadow: var(--shadow-card);
}

/* Первая тройка. Полоса слева и подкрашенный номер — ровно столько,
   сколько нужно, чтобы отличить топ от остальных, не превращая выдачу
   в гирлянду. Купленное выделение (--card-spine из data-highlight)
   ставится тем же свойством и перебивает медаль: за него заплатили. */
.server-card--top {
    --card-spine: var(--border-strong);
}

.server-card[data-rank="1"] { --medal: var(--medal-1); --card-spine: var(--medal-1); }
.server-card[data-rank="2"] { --medal: var(--medal-2); --card-spine: var(--medal-2); }
.server-card[data-rank="3"] { --medal: var(--medal-3); --card-spine: var(--medal-3); }

.server-card[data-highlight] {
    border-color: color-mix(in srgb, var(--card-spine) 30%, transparent);
}

/* Накладка для клика по всей карточке. z-index: 0 — она обязана лежать
   НИЖЕ кнопок IP и голоса, но выше фона; без него любое нажатие внутри
   карточки уводило бы на страницу сервера. */
.server-card__stretched-link {
    position: absolute;
    inset: 0;
    z-index: 0;
}

.server-card > *:not(.server-card__stretched-link) {
    position: relative;
    z-index: 1;
}

/* ---- Ранг ---- */

.server-card__rank {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--box-lg);
    height: var(--box-lg);
    border-radius: var(--r-md);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
}

.rank-number {
    font-size: var(--fs-small);
    font-weight: var(--fw-bold);
    line-height: 1;
    color: var(--text-4);
    font-variant-numeric: tabular-nums;
}

.server-card--top .rank-number {
    color: var(--medal, var(--text));
}

.server-card--top .server-card__rank {
    background: color-mix(in srgb, var(--medal, var(--accent)) 12%, transparent);
    border-color: color-mix(in srgb, var(--medal, var(--accent)) 35%, transparent);
}

/* ---- Название и слоган ---- */

.server-card__main {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    min-width: 0;
}

.server-name {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin: 0;
    font-size: var(--fs-lead);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-snug);
    line-height: var(--lh-snug);
    color: var(--text);
    min-width: 0;
}

.server-name a {
    color: inherit;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: color var(--dur) var(--ease);
}

.server-card:hover .server-name a {
    color: var(--accent);
}

.server-description {
    margin: 0;
    max-width: var(--measure-sm);
    font-size: var(--fs-small);
    line-height: var(--lh-snug);
    color: var(--text-4);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Баннер 468×60. Атрибутов width/height в HTML нет намеренно — adblock
   матчит именно их как стандартный IAB Full Banner, — поэтому размер
   живёт только здесь. */
/* Высота слота ФИКСИРОВАНА, а не выведена из картинки.

   При `height: auto` баннер с loading="lazy" до загрузки занимает ноль:
   карточка стоит низкой, потом картинка приезжает и толкает вниз всё,
   что под ней. На списке из тридцати серверов это прыжок на каждой
   прокрутке. С фиксированной высотой место занято сразу, и заодно
   строка с настоящим баннером совпадает по высоте со строкой, где стоит
   заглушка.

   object-fit: contain — на случай, если владелец загрузил не 468×60:
   картинка впишется целиком, а не растянется. */
.server-placeholder {
    box-sizing: border-box;
    display: block;
    width: 100%;
    max-width: var(--banner-w);
    height: var(--banner-h);
    object-fit: contain;
    margin-top: var(--sp-2);
    border-radius: var(--r-sm);
    border: 1px solid var(--border);
}

/* Заглушка для сервера без баннера. Держит строку списка одной высоты
   со строками, где баннер загружен: иначе колонка чисел уезжает по
   вертикали от карточки к карточке, и список нельзя просмотреть глазом.

   Рисуется целиком CSS. Прежний placeholder.png был плоским
   светло-серым слэбом 450×74 — на тёмной странице самое яркое пятно в
   кадре, причём сразу на десяти карточках из одиннадцати. Здесь
   утопленная поверхность и та же техническая сетка, что у первого
   экрана: заглушка читается как часть системы, а не как дыра.

   Высота — от --banner-h, а не от содержимого: слот обязан совпадать с
   настоящим баннером, иначе смысл выравнивания теряется. */
.server-placeholder--empty {
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    height: var(--banner-h);
    padding-inline: var(--sp-4);
    overflow: hidden;
    background-color: var(--surface-sunken);
    background-image:
        linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
        linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
    background-size: var(--sp-5) var(--sp-5);
}

/* Название сервера вместо пустоты. Не «нет баннера»: отрицательная
   подпись на девяти карточках из десяти — это девять сообщений о том,
   чего нет. Имя же делает заглушку разной у разных серверов и потому
   похожей на оформление, а не на прореху. */
.server-placeholder__word {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--fs-lead);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--text-4);
    /* 0.7, а не 0.35: при 0.35 надпись давала 1.66:1 и читалась пятном
       грязи, а не словом. 0.7 — это 3.31:1, то есть даже декоративный
       водяной знак проходит порог для графики и выглядит намеренным. */
    opacity: 0.7;
}

/* ---- Характеристики ---- */

/* Три метрики в колонках ФИКСИРОВАННОЙ ширины, а не по содержимому.

   По содержимому ширина колонки зависит от числа в ней: «11 онлайн» уже,
   чем «11608 онлайн», и версия «1.21» уже, чем «26.2». В списке из
   тридцати серверов это значило, что ни онлайн, ни баллы не стоят друг
   под другом — глазом сравнить два сервера было нельзя, только прочитать
   оба. 92px хватает самому широкому случаю: пятизначный онлайн (50px)
   плюс иконка и отступ. */
.server-details {
    display: grid;
    grid-template-columns: repeat(3, 92px);
    align-items: center;
    gap: var(--sp-3);
}

/* Значение над подписью, иконка слева на обе строки. Раньше подпись
   стояла В СТРОКУ за значением («1.21 версия»), и ряд из трёх метрик
   растягивался на 320px — на узком экране он переносился по одному
   элементу, и карточка росла на две строки. */
.detail-item {
    display: grid;
    grid-template-columns: auto auto;
    align-items: center;
    column-gap: var(--sp-2);
}

.detail-icon {
    grid-row: 1 / 3;
    grid-column: 1;
    width: var(--icon-md);
    height: var(--icon-md);
    color: var(--text-4);
}

.detail-text {
    grid-column: 2;
    grid-row: 1;
    font-size: var(--fs-body);
    font-weight: var(--fw-bold);
    line-height: var(--lh-tight);
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.detail-label {
    grid-column: 2;
    grid-row: 2;
    font-size: var(--fs-caption);
    font-weight: var(--fw-regular);
    line-height: 1.4;
    color: var(--text-4);
}

/* Купленные баллы — единственная величина карточки, которую можно
   получить за деньги, и единственная, помеченная акцентом. */
.detail-item--paid .detail-text {
    color: var(--accent);
}

/* ---- Действие ---- */

.server-link-wrapper {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--sp-2);
    min-width: 190px;
}

/* Кнопка с адресом НЕЙТРАЛЬНАЯ, жёлтая у неё только иконка копирования.
   Залитая акцентом, она давала два десятка жёлтых плашек в колонку, на
   фоне которых не читались ни топ-3, ни активная вкладка: жёлтым было
   помечено то, что и так есть у каждой строки. */
.server-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    box-sizing: border-box;
    width: 100%;
    height: var(--control-h);
    padding: 0 var(--sp-4);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    background-color: var(--surface-3);
    background-image: var(--sheen);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-small);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    transition: background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                color var(--dur) var(--ease);
}

.server-link:hover {
    background-color: var(--surface-4);
    border-color: var(--border-input);
}

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

.copy-icon {
    flex-shrink: 0;
    width: var(--icon-sm);
    height: var(--icon-sm);
    color: var(--accent);
}

/* У лаунчера иконка нейтральная: это переход по внешней ссылке, а не
   копирование, и акцентом там помечать нечего. */
.server-link--launcher .copy-icon {
    color: var(--text-3);
}

/* ---- Голосование ---- */

.vote__button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    box-sizing: border-box;
    width: 100%;
    height: var(--control-h-sm);
    padding: 0 var(--sp-4);
    border: 1px solid var(--accent-line);
    border-radius: var(--r-md);
    background: var(--accent-soft);
    color: var(--accent);
    font-family: inherit;
    font-size: var(--fs-small);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    transition: background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                color var(--dur) var(--ease);
}

.vote__button:hover:not([aria-disabled="true"]) {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-contrast);
}

/* Проголосовали — кнопка гаснет в нейтральное состояние, а не остаётся
   зовущей: повторить действие всё равно нельзя до завтра. */
.vote__button.is-voted {
    background: var(--surface-3);
    border-color: var(--border);
    color: var(--success);
    cursor: default;
}

.vote__icon {
    flex-shrink: 0;
    width: var(--icon-sm);
    height: var(--icon-sm);
}

/* ---- Появление карточек ---- */

@keyframes serverCardIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

#servers-list .server-card {
    animation: serverCardIn var(--dur-slow) var(--ease-out) both;
}

/* ==================================================================
   6. Пагинация
   ================================================================== */

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    flex-wrap: wrap;
}

.pagination__page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-width: var(--control-h-sm);
    height: var(--control-h-sm);
    padding: 0 var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--text-3);
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    font-variant-numeric: tabular-nums;
    transition: background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                color var(--dur) var(--ease);
}

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

.pagination__page--current {
    background-color: var(--accent);
    background-image: var(--accent-fill);
    border-color: var(--accent);
    color: var(--accent-contrast);
    font-weight: var(--fw-bold);
}

.pagination__ellipsis {
    color: var(--text-4);
    padding-inline: var(--sp-1);
}

/* ==================================================================
   7. Прочее, что нужно каталогу и карточке сервера
   ================================================================== */

/* Кликабельный скриншот на странице сервера. */
.screenshot-clickable {
    cursor: zoom-in;
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.screenshot-clickable:hover {
    opacity: 0.9;
    transform: scale(1.01);
}

/* ==================================================================
   8. Адаптив. Четыре брейкпоинта: 1200 / 1024 / 768 / 480.
   ================================================================== */

/* Планшет-альбом: характеристики и действие уезжают на вторую строку
   карточки, название остаётся с рангом. Иначе колонка метрик сжимает
   название до трёх букв с многоточием. */
@media (max-width: 1024px) {
    .server-card {
        grid-template-columns: var(--box-lg) minmax(0, 1fr);
        gap: var(--sp-4);
    }

    .server-details {
        grid-column: 2;
        justify-content: start;
        gap: var(--sp-5);
    }

    .server-link-wrapper {
        grid-column: 2;
        flex-direction: row;
        align-items: center;
        min-width: 0;
    }

    .server-link {
        width: auto;
        min-width: 200px;
    }

    .vote__button {
        width: auto;
        height: var(--control-h);
    }
}

@media (max-width: 768px) {
    .toolbar__modes {
        align-items: stretch;
        flex-direction: column;
    }

    .servers__categories,
    .platform-switch {
        justify-content: flex-start;
    }

    fieldset.servers__filters {
        flex: 1 1 100%;
    }

    fieldset.servers__filters > .servers__version {
        flex: 1 1 0;
    }

    .servers__filters .select-toggle {
        width: 100%;
    }

    /* Дропдаун тегов прижат к правому краю пикера: раскрытый от левого
       края он уезжал за вьюпорт на узком экране. */
    .tag-picker__dropdown {
        width: min(320px, calc(100vw - var(--sp-8)));
    }
}

@media (max-width: 480px) {
    .server-card {
        padding: var(--pad-card-sm);
        gap: var(--sp-3);
    }

    /* На телефоне фиксированные 92px в три колонки требуют 300px при
       доступных ~280: колонки уезжают за карточку. Здесь ширину задаёт
       содержимое — сравнивать соседние карточки глазом на экране, где
       помещается одна, всё равно нельзя. */
    .server-details {
        grid-template-columns: repeat(3, auto);
        justify-content: space-between;
        gap: var(--sp-3);
    }

    .server-link-wrapper {
        flex-direction: column;
        align-items: stretch;
    }

    .server-link {
        width: 100%;
        min-width: 0;
    }

    .vote__button {
        width: 100%;
    }
}
