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

   Общие компоненты (заголовки страницы, .segmented, .chip, .menu,
   .select-toggle, .empty-state, кнопки, карточки) берутся из global.css.
   Здесь остаётся только то, что специфично для каталога.

   Файл подключают также профиль и карточка сервера — они переиспользуют
   .server-card и .detail-*, поэтому эти атомы живут здесь.

   Значения — только из tokens.css. Адаптив внизу файла, четыре
   брейкпоинта: 1200 / 1024 / 768 / 480.
   =================================================================== */

/* ------------------------------------------------------------------
   Блок посадочной страницы (/tag/<slug>/, /version/<slug>/)

   На главной его нет — контекста, который он объясняет, там тоже нет.
   ------------------------------------------------------------------ */

.servers__landing {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--sp-2) var(--sp-4);
    margin: calc(-1 * var(--sp-3)) 0 var(--stack);
}

.servers__landing-text {
    max-width: var(--measure);
    margin: 0;
    font-size: var(--fs-body);
    line-height: var(--lh-base);
    color: var(--text-3);
}

.servers__landing-count {
    margin: 0;
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    color: var(--text-4);
    white-space: nowrap;
}

.servers__landing-count strong {
    color: var(--text);
    font-weight: var(--fw-semibold);
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------
   Панель каталога

   Две строки с ясным разделением: первая отвечает на вопрос «какая
   выдача» (режим и платформа), вторая — «чем её сузить» (поиск, теги,
   версия). Соцсети отсюда убраны: те же две ссылки стоят в подвале,
   и в строке фильтров они ничего не фильтровали.
   ------------------------------------------------------------------ */

.servers-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-3);
    margin-bottom: var(--sp-5);
}

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

.servers-header > search {
    flex: 1 1 100%;
    min-width: 0;
}

.servers__categories {
    margin-right: auto;
}

.servers__search {
    display: flex;
    align-items: center;
    width: 100%;
    position: relative;
    z-index: 10;
}

.servers__inline-elements {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    width: 100%;
    position: relative;
}

.servers__search-bar {
    display: flex;
    align-items: center;
    position: relative;
    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);
    /* Граница поля должна быть видна: порог WCAG 1.4.11 — 3:1. */
    border: 1px solid var(--border-input);
    border-radius: var(--r-md);
    background-color: var(--surface-3);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-body);
    transition: border-color 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);
    border-radius: var(--r-md);
}

.servers__search-icon {
    position: absolute;
    left: var(--sp-4);
    top: 50%;
    transform: translateY(-50%);
    width: var(--icon-sm);
    height: var(--icon-sm);
    opacity: 0.4;
    pointer-events: none;
}

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

.servers__version {
    display: flex;
    align-items: center;
}

/* ------------------------------------------------------------------
   Пикеры версии и тегов

   Кнопка и панель — общие .select-toggle и .menu из global.css.
   Здесь только позиционирование и содержимое панели тегов.
   ------------------------------------------------------------------ */

.version-picker {
    position: relative;
}

/* Панель тегов шире кнопки и прижата к правому краю строки фильтров,
   поэтому её якорь — вся строка, а не сама кнопка. */
.tag-picker {
    position: static;
}

.tag-picker__dropdown {
    right: 0;
    left: auto;
    /* min-width из .menu — «не уже кнопки». Здесь якорь не кнопка, а вся
       строка фильтров (.tag-picker position:static), и 100% растягивало
       панель на всю ширину каталога. */
    min-width: 0;
    width: 560px;
    max-width: calc(100vw - var(--sp-8));
    max-height: none;
    z-index: 200;
    padding: var(--sp-4);
    transform-origin: top right;
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}

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

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

/* ------------------------------------------------------------------
   Список серверов
   ------------------------------------------------------------------ */

/* Контейнер выдачи: сюда filters.js подменяет разметку целиком, и лежат в нём
   список карточек, пустое состояние и пагинация. Ритм между ними — здесь,
   расстояние между самими карточками — на .servers__items. */
.servers__list {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

/* Обёртка с role="list" (см. servers_list.html). Раскладка карточек переехала
   с .servers__list сюда: без этого все карточки стали бы одним flex-ребёнком
   и слиплись бы вплотную. */
.servers__items {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

/* Полоса слева задаётся одной переменной --card-spine, а не тремя
   конкурирующими правилами. Порядок приоритета получается сам собой:
   прозрачная по умолчанию → жёлтая у топ-3 → цвет купленного выделения,
   который server-cards.js ставит инлайном и который перебивает оба.

   Рисуется псевдоэлементом, а НЕ inset box-shadow: box-shadow участвует
   в transition ради тени при наведении, и Chrome залипает на прежнем
   цвете, когда значение приходит из var(). */
.server-card {
    --card-spine: transparent;

    position: relative;
    overflow: hidden;
    padding: var(--pad-card);
    background-color: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    color: var(--text);
    cursor: pointer;
    transition: border-color var(--dur) var(--ease),
                background-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

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

.server-card--top {
    --card-spine: var(--accent);
}

/* Лёгкая заливка в цвет выделения — только слева, чтобы не спорить с
   текстом. Не поддержан color-mix — просто не будет подложки. */
.server-card[data-highlight] {
    background-image: linear-gradient(
        90deg,
        color-mix(in srgb, var(--card-spine) 9%, transparent),
        transparent 320px
    );
}

.server-card__stretched-link {
    position: absolute;
    inset: 0;
    z-index: 0;
    border-radius: var(--r-lg);
}

/* Раскладка карточки. display:contents у двух обёрток нужен, чтобы
   баннер и кнопка IP встали в правую колонку, а название и цифры —
   в левую, не трогая разметку (её же отдаёт AJAX-обновление списка). */
.server-card__content {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--sp-6);
    row-gap: var(--sp-4);
    align-items: start;
}

.server-card__header {
    grid-column: 1;
    grid-row: 1;
    min-width: 0;
}

.server-card__info,
.server-details__wrapper {
    display: contents;
}

.server-card__headerwrapper {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
}

.server-card__rank {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    box-sizing: border-box;
    min-width: var(--box-md);
    height: var(--box-md);
    padding: 0 var(--sp-2);
    border-radius: var(--r-sm);
    background: var(--surface-4);
    color: var(--text-3);
    font-size: var(--fs-small);
    font-weight: var(--fw-semibold);
}

.server-card--top .server-card__rank {
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: var(--fw-bold);
}

.rank-number {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.server-name {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    margin: 0;
    min-width: 0;
    font-size: var(--fs-lead);
    /* 700, а не 600. Метрики рядом набраны тем же кеглем 15 и тем же 600,
       и от названия их отделяли два пикселя размера — на карточке было пять
       белых полужирных объектов, из которых глазу нечего было выбрать.
       Разрыв должен быть виден: имя — самое тяжёлое, значения — легче. */
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-snug);
    line-height: var(--lh-tight);
    color: var(--text);
}

.server-name a {
    color: inherit;
}

/* Слоган — не служебная подпись, а то, чем сервер себя продаёт: --text-3,
   а не самый тихий --text-4, который отведён под меты и подписи к числам. */
.server-description {
    margin: var(--sp-2) 0 0;
    max-width: var(--measure-sm);
    font-size: var(--fs-small);
    font-weight: var(--fw-regular);
    line-height: var(--lh-snug);
    color: var(--text-3);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

/* ---- Баннер 468×60 ----
   Размер задан только здесь: HTML-атрибутов width/height у <img> нет,
   иначе фильтры adblock вида img[width="468"][height="60"] режут его
   как стандартный IAB Full Banner. */
.server-placeholder {
    grid-column: 2;
    grid-row: 1;
    flex-shrink: 0;
    width: var(--banner-w);
    height: var(--banner-h);
    border-radius: var(--r-sm);
    object-fit: cover;
    border: 1px solid var(--border);
}

/* Заглушки больше нет. Она прошла путь от светлой плашки «БЕЗ ИЗОБРАЖЕНИЯ»
   (ярче названия сервера — самым заметным на карточке было то, чего у
   сервера НЕТ) до погашенной до 0.22 — и всё равно оставалась крупнейшим
   объектом карточки, пустым у большинства серверов. Теперь при отсутствии
   баннера <img> не рендерится вовсе (servers_list.html), а карточка
   получает .server-card--nobanner. */

/* ------------------------------------------------------------------
   Строка характеристик

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

.server-details {
    grid-column: 1;
    grid-row: 2;
    align-self: center;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-5);
    min-width: 0;
}

.detail-item {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    color: var(--text-4);
}

.detail-icon {
    flex-shrink: 0;
    fill: none;
    align-self: center;
    color: var(--text-4);
}

.detail-text {
    font-size: var(--fs-body);
    /* 500: значение должно читаться как значение, а не спорить с названием
       сервера. По всей главной 600 встречалось 72 раза против 56 у 400 —
       полужирным была набрана больше чем половина страницы, и выделять им
       стало нечего. Та же ловушка, из которой в своё время выбирались,
       когда по всему сайту стояло 500. */
    font-weight: var(--fw-medium);
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

.detail-label {
    font-size: var(--fs-caption);
    font-weight: var(--fw-regular);
    color: var(--text-4);
}

/* Баллы подсвечиваются, только когда они есть: ноль жёлтым — обещание,
   которого нет.

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

/* ------------------------------------------------------------------
   Кнопка с адресом

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

.server-link-wrapper {
    grid-column: 2;
    grid-row: 2;
    justify-self: stretch;
    position: relative;
    display: flex;
    z-index: 1;
}

.server-link {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    box-sizing: border-box;
    width: 100%;
    height: var(--control-h);
    padding: 0 var(--sp-4);
    border: 1px solid var(--border-input);
    border-radius: var(--r-md);
    background-color: var(--surface-3);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-body);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-snug);
    cursor: pointer;
    transition: background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease);
}

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

/* Иконка нейтральная. Жёлтой она была «ровно действием», но кнопка с
   адресом стоит на каждой карточке, и два десятка одинаковых жёлтых меток
   снова складывались в колонку — ту самую, ради которой с этих кнопок в
   своё время сняли сплошную заливку. Копирование адреса не входит в три
   роли акцента: это не главное действие страницы, не текущее состояние и
   не купленное место. */
.server-link .copy-icon,
.server-link--launcher .copy-icon {
    color: var(--text-3);
}

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

.copy-icon {
    flex-shrink: 0;
    fill: none;
}

.screenshot-clickable {
    cursor: zoom-in;
    transition: opacity var(--dur-fast) var(--ease);
}

.screenshot-clickable:hover {
    opacity: 0.88;
}

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

   Разметка одна на каталог и карточку сервера (app/vote-button.html),
   поэтому и стили лежат здесь — его подключают обе страницы.
   ------------------------------------------------------------------ */

.vote__button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    box-sizing: border-box;
    height: var(--control-h);
    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);
    white-space: nowrap;
    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);
}

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

   Именно [aria-disabled], а не :disabled: отключённая кнопка вылетает из
   порядка табуляции, и браузер сбрасывает с неё фокус в <body>. В народном
   топе тридцать серверов, и проголосовавший с клавиатуры улетал в начало
   документа. Теперь кнопка остаётся фокусируемой, а повторное нажатие
   отсекает проверка в vote.js. */
.vote__button[aria-disabled="true"],
.vote__button.is-voted {
    background: var(--surface-3);
    border-color: var(--border);
    color: var(--text-3);
    cursor: default;
}

.vote__icon {
    flex-shrink: 0;
    fill: none;
}

/* В народном топе в ячейку с адресом приезжает ещё и кнопка голоса. Рядом они
   делят ширину и оба становятся нечитаемы, поэтому раскладываем столбиком.
   :has() — чтобы не заводить модификатор, который пришлось бы протаскивать
   через два условия в шаблоне списка. */
.server-link-wrapper:has(.vote__button) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--sp-2);
}

.server-link-wrapper:has(.vote__button) .vote__button {
    width: 100%;
}

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

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-1);
    padding-top: var(--sp-5);
}

.pagination__page {
    display: 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-2);
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text-3);
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    font-variant-numeric: tabular-nums;
    transition: background-color var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease);
}

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

.pagination__page--current {
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: var(--fw-semibold);
    pointer-events: none;
}

.pagination__ellipsis {
    padding: 0 var(--sp-1);
    color: var(--text-4);
    font-size: var(--fs-small);
}

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

.servers__list.is-loading {
    opacity: 0.4;
    pointer-events: none;
    transition: opacity var(--dur) var(--ease);
}

/* Появление карточек: одна короткая ступень на всю пачку вместо десяти
   по 40мс. Каскад до 360мс заметен как задержка отрисовки, а не как
   аккуратность, и повторяется при каждом применении фильтра. */
@keyframes serverCardIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

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

/* ==================================================================
   Адаптив
   ================================================================== */

/* Баннер 468px + колонка названия перестают помещаться раньше, чем
   контейнер упирается в край, — карточка переезжает в одну колонку. */
@media (max-width: 1024px) {
    .server-card__content {
        grid-template-columns: minmax(0, 1fr);
        row-gap: var(--sp-4);
    }

    .server-card__header  { grid-column: 1; grid-row: 1; }
    .server-details       { grid-column: 1; grid-row: 2; }
    .server-placeholder   { grid-column: 1; grid-row: 3; max-width: 100%; }
    .server-link-wrapper  { grid-column: 1; grid-row: 4; }

    /* Без баннера третий ряд пуст, но по-прежнему объявлен — и остаётся
       двойной row-gap там, где ничего нет. Кнопка поднимается на его место. */
    .server-card--nobanner .server-link-wrapper { grid-row: 3; }

    .server-link {
        max-width: var(--banner-w);
    }
}

@media (max-width: 768px) {
    .servers__categories,
    .platform-switch {
        width: 100%;
    }

    .servers__inline-elements {
        flex-wrap: wrap;
        row-gap: var(--sp-2);
    }

    .servers__search-bar {
        flex: 1 1 100%;
    }

    fieldset.servers__filters {
        flex: 1 1 auto;
    }

    fieldset.servers__filters > * {
        flex: 1;
    }

    /* Каждый уровень обёртки должен растянуться, иначе flex:1 на .servers__version
       ничего не даёт: кнопка сидит через .tag-picker / .version-picker. */
    .servers__version,
    .servers__version > *,
    .servers__version .select-toggle {
        width: 100%;
    }

    .tag-picker__dropdown {
        left: 0;
        right: 0;
        width: auto;
        max-width: none;
        transform-origin: top center;
    }
}

@media (max-width: 480px) {
    /* При 320px пара «Все сервера / Новые сервера» не влезала и вторую
       кнопку срезало (WCAG 1.4.10). Иконки декоративные — alt="" — и
       уходят первыми, текст важнее. */
    .segmented__icon {
        display: none;
    }

    .segmented__item {
        padding: 0 var(--sp-3);
    }

    .server-placeholder,
    .server-link {
        width: 100%;
        max-width: none;
    }

    .server-details {
        gap: var(--sp-2) var(--sp-4);
    }
}

