/*
  ── Состояния ожидания ──

  Переход между вкладками занимает 300–550 мс, обновление фрейма фильтров —
  до 550 мс. Всё это время интерфейс раньше не менялся никак: полоса Turbo
  включалась через 500 мс, то есть ровно тогда, когда содержимое уже пришло,
  а на фреймах она не показывается принципиально.

  Крючки при этом Turbo расставляет сам — здесь к ним привязаны стили:
    html[data-turbo-preview] — на экране снимок из кэша, свежий ответ ещё едет;
    turbo-frame[busy]        — фрейм перезагружается (фильтры, таблицы, превью).

  Пульс — тот же, что у пустых состояний статистики (stats-empty-breathe),
  чтобы «ждём данные» выглядело одинаково во всём приложении.
*/

/* Полоса Turbo: своя вместо стандартной синей #0076FF. Задержку до появления
   снимает application.js, иначе полоса включается к самому концу ожидания. */
.turbo-progress-bar {
  height: 2px;
  background: linear-gradient(90deg, var(--color-c-chart-up), var(--color-c-chart-up-soft));
  box-shadow: 0 0 8px rgba(94, 234, 212, 0.55);
}

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

  Приглушается только рабочая область: меню и переключатель игрока остаются
  живыми, из превью должно быть видно, куда идёшь дальше.
*/
html[data-turbo-preview] main {
  opacity: 0.5;
  pointer-events: none;
  animation: stats-empty-breathe 3s ease-in-out infinite;
}

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

  Правило намеренно общее: фреймов в приложении дюжина, и заводить каждому
  свою заглушку значило бы двенадцать раз повторить одно и то же.
*/
turbo-frame[busy] {
  display: block;
  position: relative;
  opacity: 0.5;
  pointer-events: none;
  animation: stats-empty-breathe 3s ease-in-out infinite;
}

/*
  Крутящийся индикатор поверх занятого фрейма. Одного приглушения мало:
  запрос за статистикой уходит на 5–8 секунд (ClickHouse считает срезы по
  миллионам раздач), и всё это время «чуть более тусклый экран» читался как
  «ничего не произошло». Индикатор виден сразу и говорит, что ответ едет.
*/
turbo-frame[busy]::after {
  content: "";
  position: absolute;
  top: 28px;
  left: 50%;
  width: 26px;
  height: 26px;
  margin-left: -13px;
  border-radius: 999px;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--color-c-chart-up);
  animation: frame-busy-spin 0.8s linear infinite;
  z-index: 30;
}

@keyframes frame-busy-spin {
  to { transform: rotate(360deg); }
}

/* Фрейм-обёртка без собственной геометрии (class: "contents") приглушается
   вместе с содержимым, но display: block из правила выше сломал бы сетку. */
turbo-frame.contents[busy] {
  display: contents;
}

/*
  Ожидание разбора стата. Правило выше оставляет фрейм панели без бокса, то
  есть общий спиннер turbo-frame[busy]::after на нём не появится. Поэтому
  h2n-panel-swap#requested кладёт внутрь фрейма вот такую заглушку — у неё
  бокс есть, и место она занимает ровно на время запроса.
*/
.h2n-panel-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 16rem;
  min-height: 12rem;
  border-radius: 0.5rem;
  border: 1px solid color-mix(in srgb, var(--color-c-gray-4) 40%, transparent);
  background: var(--color-c-gray-2);
}

.h2n-panel-loading__spinner {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--color-c-chart-up);
  animation: frame-busy-spin 0.8s linear infinite;
}

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

/*
  Нажатый пункт меню. Активное состояние приходит с сервера, то есть только
  вместе с новой страницей — до этого клик выглядел непроваленным. Метка
  ставится на клиенте сразу и снимается, когда страница отрисована.
*/
.nav-pending {
  background-color: var(--color-c-gray-4);
  color: var(--color-c-foreground);
}

.nav-pending::after {
  content: "";
  width: 6px;
  height: 6px;
  margin-left: auto;
  border-radius: 999px;
  background: var(--color-c-chart-up-soft);
  animation: nav-pending-blink 1s ease-in-out infinite;
}

@keyframes nav-pending-blink {
  0%, 100% { opacity: 0.25; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1); }
}

/* Системная настройка «меньше движения»: приглушение остаётся — это
   информация о состоянии, а не украшение; убирается только движение. */
@media (prefers-reduced-motion: reduce) {
  html[data-turbo-preview] main,
  turbo-frame[busy],
  .nav-pending::after {
    animation: none;
  }

  /* Индикатор остаётся видимым, но перестаёт вращаться */
  turbo-frame[busy]::after {
    animation: none;
    border-top-color: var(--color-c-chart-up);
  }
}
