/* Движение — часть речи интерфейса, а не украшение.
 *
 * Ролей четыре, и каждая отвечает на вопрос «что случилось»:
 *   появилось     — `rise` и `pop`: панель, всплывающее, сообщение;
 *   выехало сбоку — `slide`: карточка выбранного над картой;
 *   сменилось     — экран целиком, чтобы переход между разделами читался
 *                   как переход, а не как мгновенная подмена содержимого;
 *   держится      — `ping`, **единственная бесконечная анимация в проекте**,
 *                   и только на карте: кольцо у выбранной точки и у той,
 *                   в которой находишься сейчас. Поводы разные — первое
 *                   связывает клик с карточкой, выехавшей в полукадре от него,
 *                   второе показывает то, что меняется само, без действий
 *                   человека. Больше нигде непрерывного движения нет.
 *
 * Своих значений здесь нет: длительности и кривая — из `tokens.css`.
 * Гасится всё одним правилом `prefers-reduced-motion` в `base.css`, и отсюда
 * условие: **анимация не имеет права быть единственным источником вида.**
 * Что появляется — появляется кадром (пропало — и не жалко); у `ping` гаснет
 * кольцо, а сам выбранный пин остаётся виден акцентом и размером.
 *
 * Центрированные элементы (`.toast`, `.quick`, `.results`) держатся
 * на `translateX(-50%)`. Кадры обязаны нести этот сдвиг в себе, иначе
 * анимация на время сбивает центрирование вбок. */

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}

@keyframes rise-centered {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateX(-50%);
  }
}

@keyframes pop {
  from {
    opacity: 0;
    transform: scale(0.97);
  }
}

@keyframes slide {
  from {
    opacity: 0;
    transform: translateX(14px);
  }
}

/* --- смена экрана ---------------------------------------------------------- */

/* Экран пересобирается при смене раздела (`key` по имени маршрута в `main.js`),
 * поэтому кадр проигрывается заново, а не один раз при загрузке. */
.work > * {
  animation: rise var(--dur) var(--ease) both;
}

/* --- появление поверх ------------------------------------------------------ */

.map-detail {
  animation: slide var(--dur) var(--ease) both;
}

.toast,
.quick,
.results {
  animation: rise-centered var(--dur) var(--ease) both;
}

.switch-pop {
  transform-origin: top left;
  animation: pop var(--dur-fast) var(--ease) both;
}

/* --- очередь карточек ------------------------------------------------------ */

/* Ступенькой, а не разом: четыре карточки «прямо сейчас» и сетка поездок
 * читаются слева направо, и порядок появления повторяет порядок чтения.
 * Шаг мелкий — это подсказка глазу, а не представление. */
.now-card,
.trip-grid > * {
  animation: rise var(--dur) var(--ease) both;
}
.now-card:nth-child(2),
.trip-grid > *:nth-child(2) {
  animation-delay: 0.03s;
}
.now-card:nth-child(3),
.trip-grid > *:nth-child(3) {
  animation-delay: 0.06s;
}
.now-card:nth-child(4),
.trip-grid > *:nth-child(4) {
  animation-delay: 0.09s;
}
.trip-grid > *:nth-child(n + 5) {
  animation-delay: 0.12s;
}

/* --- отклик на наведение --------------------------------------------------- */

/* Карточка-ссылка приподнимается: она ведёт куда-то, и это единственное,
 * чем строка списка отличается от строки текста до нажатия. */
.card.is-link,
.trip-grid > .card {
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}
.card.is-link:hover,
.trip-grid > .card:hover {
  transform: translateY(-1px);
}

/* Точка на карте подрастает под курсором: попасть в 28 px мышью легче,
 * когда цель отвечает на приближение. */
.pin {
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
    width var(--dur-fast) var(--ease), height var(--dur-fast) var(--ease);
}
.pin:hover {
  transform: translate(-50%, -50%) scale(1.12);
}

/* Выбранная точка дышит одним кольцом. Это единственное, что на карте
 * повторяется бесконечно, и повод у него есть: карточка выбранного выезжает
 * справа, за полкадра от того места, куда ткнули, — кольцо связывает одно
 * с другим. Кольцом-псевдоэлементом, а не тенью самого пина: пин в этот
 * момент меняет размер, и анимировать его же значит спорить с переходом. */
.pin.is-on::before {
  content: "";
  position: absolute;
  inset: -2px;
  border: 2px solid var(--accent);
  border-radius: var(--r-pill);
  pointer-events: none;
  animation: ping var(--dur-pulse) var(--ease) infinite;
}
@keyframes ping {
  from {
    transform: scale(1);
    opacity: 0.5;
  }
  70%,
  to {
    transform: scale(1.9);
    opacity: 0;
  }
}

/* Текущий город дышит тем же кольцом, что и выбранный, — на обёртке точки,
 * потому что у пина псевдоэлемент уже занят выбором. Оба сразу не пульсируют:
 * выбор — ответ на действие, он важнее и глушит фоновое. */
.map-point.is-now::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 44px;
  height: 44px;
  margin: -22px 0 0 -22px;
  border: 2px solid var(--accent);
  border-radius: var(--r-pill);
  pointer-events: none;
  animation: ping var(--dur-pulse) var(--ease) infinite;
}
.map-point.is-now:has(.pin.is-on)::after {
  display: none;
}

/* Маршрут выкладывается по порядку перегонов, а не появляется разом: порядок
 * появления повторяет порядок движения.
 *
 * Прозрачностью, а не прорисовкой линии по длине: та держится
 * на `stroke-dashoffset`, который перезаписывает `stroke-dasharray`, — и пунктир
 * перегона, которым отличается перелёт от дороги, пропал бы насовсем. */
.leg {
  animation: fade var(--dur) var(--ease) both;
  animation-delay: calc(var(--i, 0) * 0.04s);
}
@keyframes fade {
  from {
    opacity: 0;
  }
}

/* Выезд панели выбранного — справа, откуда она и стоит. Смещение маленькое:
   панель не «прилетает», а подаётся, иначе движение спорит с картой под ней. */
@keyframes detail-in {
  from {
    opacity: 0;
    transform: translateX(var(--s7));
  }
}
