/* Frontline — боевой пропуск ------------------------------------------------
   Отдельный файл по той же причине, что player-missions.css и
   player-rating.css: player.css уже большой, а пропуск — самостоятельный
   экран со своей геометрией.

   Главное решение экрана: платной дорожки нет. У обычного боевого пропуска
   половина экрана — вторая дорожка под замком, и вся его графика построена на
   этом замке. Здесь замка нет вообще, поэтому:

     ДОРОГА    — одна. Не «бесплатная» и не «платная», просто дорога сезона.
                 Одна линия, один ряд узлов, один ряд наград.
     ЗАМОК     — не рисуется нигде. Награда, до которой игрок ещё не дошёл,
                 подписана «УР. 24», а не перечёркнутым замком: замок читается
                 как «купи», номер уровня — как «дойди».
     ПЕЧАТЬ    — единственная вещь на экране, которая крутится. Круглый
                 штемпель сезона в шапке. Движение на экране одно, и стоит оно
                 на имени сезона.

   Громкость расставлена как в рейтинге: финал сезона и рубежи (каждый
   десятый уровень) — золото, свечение и уголки; всё остальное — ровные
   карточки. Если подсветить половину дороги, рубеж перестанет быть рубежом.

   Грузится после player.css: --pl-ember, --pl-lift, --pl-glow, --pl-hatch,
   --reset-* уже объявлены там. Новых цветов здесь нет — только редкости из
   каталога (их отдаёт JS через --rarity) и золото сезона из player.css.
   ========================================================================== */

.pl-bp-status {
  min-height: 18rem;
  display: grid;
  place-items: center;
  align-content: center;
  gap: .75rem;
  padding: 2rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius);
  background: var(--reset-panel-raised);
  color: var(--reset-text-muted);
  text-align: center;
}

.pl-bp-status > i {
  color: var(--pl-ember);
  font-size: 1.65rem;
}

.pl-bp-status > b {
  color: var(--reset-text);
  font-size: 1.05rem;
}

.pl-bp-status.is-error > i {
  color: var(--reset-danger, #ff5d69);
}

.pl-bp-status > span {
  max-width: 38rem;
}

.pl-pass-view {
  /* Ширина карточки на дороге объявлена один раз: её читают и сама карточка,
     и узел под ней, и медиазапросы. Рубеж шире обычной ступени ровно
     настолько, чтобы разница читалась боковым зрением при прокрутке. */
  --bp-card: 11.5rem;
  --bp-card-hi: 14.5rem;
  --bp-node: 2.7rem;
  --bp-gap: .55rem;

  max-width: 108rem;
  margin-inline: auto;
}

.pl-bp {
  display: grid;
  gap: 1.5rem;
}

/* ===== Шапка сезона =======================================================
   Слева — кто ты в этом сезоне и сколько до следующего уровня. Справа —
   циферблат уровня и печать. Циферблат круглый специально: во всём кабинете
   прогресс рисуется полосой, и один круг на весь продукт сразу говорит, что
   это другая шкала — не ранг и не задание, а сезон.
   ========================================================================== */

.pl-bp-season {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1.6rem;
  align-items: center;
  overflow: hidden;
  padding: 1.6rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius);
  background: var(--reset-panel-raised);
  background-image: var(--pl-hatch);
  box-shadow: var(--pl-lift);
}

/* Один источник света в левом верхнем углу — как у баннера заданий и шапки
   рейтинга. Пятно, а не градиент во всю ширину: градиент делает поверхность
   плоской. */
.pl-bp-season::before {
  position: absolute;
  top: -15rem;
  left: -9rem;
  width: 36rem;
  height: 28rem;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--pl-ember) 40%, transparent), transparent 72%);
  content: "";
  pointer-events: none;
}

.pl-bp-season > * { position: relative; }

.pl-bp-season__main {
  display: grid;
  gap: .85rem;
  min-width: 0;
}

.pl-bp-season__eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  color: var(--reset-muted);
}

.pl-bp-season__eyebrow b {
  padding: .16rem .55rem;
  border-radius: 999px;
  background: var(--pl-ember-ramp);
  color: var(--pl-ember-ink);
  font-weight: 900;
}

.pl-bp-season__title {
  margin: 0;
  font-size: var(--fs-xl);
  line-height: .95;
}

/* Название сезона — единственный текст на экране с полной заливкой золотом.
   Обводка под ним нужна светлой теме: золото на кремовом без ink-контура
   растворяется. */
.pl-bp-season__title span {
  background: linear-gradient(100deg, var(--pl-gold-hi) 0%, var(--pl-gold) 42%, var(--pl-gold-deep) 100%);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  -webkit-text-stroke: 1px color-mix(in srgb, var(--reset-cream) 45%, transparent);
  paint-order: stroke fill;
}

.pl-bp-season__lead {
  max-width: 46ch;
  margin: 0;
  color: var(--reset-muted);
  font-size: var(--fs-sm);
}

/* ===== Полоса опыта ======================================================= */

.pl-bp-meter {
  display: grid;
  gap: .4rem;
  max-width: 34rem;
}

.pl-bp-meter__bar {
  position: relative;
  overflow: hidden;
  height: 1.1rem;
  border: var(--pl-border);
  border-radius: 999px;
  background: color-mix(in srgb, var(--reset-cream) 8%, var(--reset-panel));
  box-shadow: inset 0 2px 6px -3px var(--reset-shadow);
}

.pl-bp-meter__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--pl-ember-ramp);
  box-shadow: var(--pl-glow-soft);
  transition: width .6s cubic-bezier(.2, .8, .2, 1);
}

/* Диагональная штриховка поверх заливки — та же, что на любом «идёт процесс»
   в играх, и она же отличает полосу от плоской плашки. */
.pl-bp-meter__fill::after {
  display: block;
  height: 100%;
  background: repeating-linear-gradient(115deg,
    rgba(255, 255, 255, .28) 0 6px, transparent 6px 14px);
  content: "";
  animation: pl-bp-flow 1.1s linear infinite;
}

@keyframes pl-bp-flow {
  from { transform: translateX(0); }
  to { transform: translateX(15.4px); }
}

.pl-bp-meter__text {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  color: var(--reset-muted);
}

.pl-bp-meter__text b { color: var(--reset-cream); }

/* Что именно лежит на следующем уровне. Игрок, открывший экран, должен
   увидеть цель раньше, чем начнёт листать дорогу. */
.pl-bp-next {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  justify-self: start;
  max-width: 100%;
  padding: .45rem .9rem .45rem .5rem;
  border: 2px solid var(--reset-line-strong);
  border-radius: 999px;
  background: var(--reset-panel);
}

.pl-bp-next__art {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 2.1rem;
  height: 2.1rem;
  overflow: hidden;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 40%,
    color-mix(in srgb, var(--rarity) 60%, transparent), var(--pl-plate));
  color: #fff;
  font-size: .95rem;
}

.pl-bp-next__art img { width: 100%; height: 100%; object-fit: cover; }

.pl-bp-next__text { display: grid; min-width: 0; gap: .05rem; }
.pl-bp-next__text small { color: var(--reset-muted); font-family: "JetBrains Mono", monospace; font-size: .66rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; }

.pl-bp-next__text b {
  overflow: hidden;
  font-family: var(--heading-font-family);
  font-size: var(--fs-sm);
  font-weight: 900;
  text-overflow: ellipsis;
  white-space: nowrap;
}

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

.pl-bp-clock {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  color: var(--reset-muted);
}

.pl-bp-clock b { color: var(--reset-cream); }

.pl-bp-clock__bar {
  flex: 1 1 8rem;
  overflow: hidden;
  min-width: 6rem;
  max-width: 16rem;
  height: 4px;
  border-radius: 999px;
  background: var(--reset-line);
}

.pl-bp-clock__bar span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--reset-muted);
}

/* ===== Циферблат и печать ================================================= */

.pl-bp-season__side {
  display: flex;
  align-items: center;
  gap: 1.1rem;
}

.pl-bp-dial {
  position: relative;
  display: grid;
  place-items: center;
  width: 10.5rem;
  height: 10.5rem;
  flex: 0 0 auto;
}

/* Кольцо — conic-gradient под маской. Так шкала рисуется одним элементом без
   SVG, и её толщина живёт в одном месте. */
.pl-bp-dial__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(var(--pl-ember) calc(var(--bp-p, 0) * 1%),
    color-mix(in srgb, var(--pl-ember) 20%,
      color-mix(in srgb, var(--reset-cream) 14%, transparent)) 0);
  mask: radial-gradient(farthest-side, transparent calc(100% - 11px), #000 calc(100% - 10px));
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 11px), #000 calc(100% - 10px));
  box-shadow: var(--pl-glow-soft);
}

.pl-bp-dial__face {
  position: relative;
  display: grid;
  place-items: center;
  container-type: size;
  width: calc(100% - 26px);
  height: calc(100% - 26px);
  border: var(--pl-border);
  border-radius: 50%;
  background: var(--reset-panel);
  box-shadow: var(--pl-lift-sm);
  text-align: center;
}

/* Цифра — единственный герой циферблата, поэтому она и есть его центр: подпись
   вынута из потока и висит снизу, иначе пара «число + УРОВЕНЬ» центровалась
   как блок и число всегда сидело выше середины круга. Сдвиг на .06em правит
   вторую половину той же беды — у цифр нет выносного элемента, и коробка
   строки центрует их чуть выше оптического центра.
   Табличные цифры держат «1» такой же ширины, как «40»: иначе единица
   болталась в круге узкой щепкой. */
.pl-bp-dial__face b {
  font-family: var(--heading-font-family);
  font-size: clamp(2.6rem, 1.8rem + 2.2vw, 3.8rem);
  font-size: 40cqmin;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.045em;
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
  transform: translateY(.06em);
}

.pl-bp-dial__face small {
  position: absolute;
  left: 10%;
  right: 10%;
  bottom: 10%;
  bottom: 10cqmin;
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: .6rem;
  font-size: max(.52rem, 7cqmin);
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* Штемпель сезона. Единственное, что крутится на экране: имя сезона по
   окружности вокруг числа уровней. */
/* Штемпель берёт золото через --reset-ink-sun: в тёмной теме этот токен и есть
   флейр, а на кремовой странице — его тёмный близнец, иначе жёлтое по жёлтому
   на светлой теме просто пропадало. Свечение тоже только тёмное: на кремовом
   оно превращается в грязное пятно. */
.pl-bp-seal {
  width: 8.6rem;
  height: 8.6rem;
  flex: 0 0 auto;
  overflow: visible;
  color: var(--reset-ink-sun);
}

:root[data-home-theme="dark"] .pl-bp-seal {
  filter: drop-shadow(0 0 14px color-mix(in srgb, var(--pl-flare) 40%, transparent));
}

.pl-bp-seal__ring { fill: none; stroke: currentColor; stroke-width: 2; opacity: .7; }
.pl-bp-seal__ring--in { stroke-dasharray: 3 5; opacity: .55; }

.pl-bp-seal__type {
  fill: currentColor;
  font-family: "JetBrains Mono", monospace;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 1.15px;
  transform-box: view-box;
  transform-origin: 60px 60px;
  animation: pl-bp-seal-spin 26s linear infinite;
}

.pl-bp-seal__core {
  fill: currentColor;
  font-family: var(--heading-font-family);
  font-size: 22px;
  font-weight: 900;
  letter-spacing: -.5px;
  text-anchor: middle;
}

.pl-bp-seal__sub {
  fill: currentColor;
  font-family: "JetBrains Mono", monospace;
  font-size: 7px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-anchor: middle;
  opacity: .75;
}

@keyframes pl-bp-seal-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

/* ===== Дорога =============================================================
   Горизонтальная лента с прокруткой. Линия дороги — не один элемент на всю
   ленту, а отрезок внутри каждой ступени: тогда пройденная часть красится
   сама собой, ровно по границе текущего уровня, и ничего не нужно считать в
   пикселях.
   ========================================================================== */

.pl-bp-track {
  overflow: hidden;
  border: var(--pl-border);
  border-radius: var(--pl-radius);
  background: var(--reset-panel-raised);
  box-shadow: var(--pl-lift);
}

.pl-bp-track__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .7rem;
  padding: .95rem 1.1rem;
  border-bottom: var(--pl-border);
  background: color-mix(in srgb, var(--pl-ember) 8%, var(--reset-panel));
}

.pl-bp-track__head h3 {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin: 0 auto 0 0;
  font-family: var(--heading-font-family);
  font-size: var(--fs-md);
  font-weight: 900;
  text-transform: uppercase;
}

.pl-bp-track__head h3 > i { color: var(--pl-ember); }

/* Ряд фильтров обязан сжиматься до переноса. Без min-width:0 flex-элемент не
   опускается ниже своей max-content ширины, и третий чип на телефоне уезжал
   за край ленты, где его срезал overflow. */
.pl-bp-track__head .pl-chips {
  flex: 1 1 14rem;
  min-width: 0;
}

.pl-bp-track__tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .45rem;
}

.pl-bp-track__tools .pl-btn { padding: .5rem 1rem; font-size: var(--fs-xs); }

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

.pl-bp-road { position: relative; }

/* Растушёвка уходит в фон самой ленты, поэтому карточка не обрывается по
   линейке, а угасает — и под кнопкой не образуется каши из двух картинок. */
.pl-bp-road::before,
.pl-bp-road::after {
  position: absolute;
  z-index: 2;
  top: 0;
  bottom: 0;
  width: 4.6rem;
  content: "";
  pointer-events: none;
  transition: opacity .2s ease;
}

.pl-bp-road::before {
  left: 0;
  background: linear-gradient(90deg, var(--reset-panel-raised) 12%, transparent);
}

.pl-bp-road::after {
  right: 0;
  background: linear-gradient(270deg, var(--reset-panel-raised) 12%, transparent);
}

.pl-bp-road.is-start::before,
.pl-bp-road.is-end::after,
.pl-bp-road.is-static::before,
.pl-bp-road.is-static::after { opacity: 0; }

.pl-bp-road__nav {
  position: absolute;
  z-index: 3;
  /* Центрируется отступом, а не transform: у .pl-icon-btn наведение и нажатие
     уже занимают transform, и центровка через него слетала бы под курсором. */
  top: 50%;
  width: 2.9rem;
  height: 2.9rem;
  margin-top: -1.45rem;
  border-radius: 50%;
}

.pl-bp-road__nav--prev { left: .55rem; }
.pl-bp-road__nav--next { right: .55rem; }

/* Кнопка на своём конце дороги не гаснет до половины, как обычная выключенная
   — она уезжает совсем: серый кружок у края читается как «сломалось». */
.pl-bp-road__nav:disabled {
  opacity: 0;
  pointer-events: none;
}

/* Мышью листают кнопками, пальцем — самой лентой. На тач-экране кнопки только
   закрывают собой карточки. */
@media (hover: none) {
  .pl-bp-road__nav { display: none; }
  .pl-bp-road::before,
  .pl-bp-road::after { display: none; }
}

.pl-bp-scroller {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  /* Три ряда: карточка тянется вверх от общей базовой линии, узел стоит в
     своей полосе, действие — под ним. Ряды общие на всю ленту, поэтому узлы
     всех ступеней физически не могут разъехаться по вертикали. */
  grid-template-rows: 1fr auto auto;
  /* Зазор рядов живёт на ленте, а не на ступени: subgrid берёт промежутки у
     родителя, и объявленный на ступени gap разъехался бы с треками ленты. */
  row-gap: .55rem;
  column-gap: 0;
  overflow-x: auto;
  overflow-y: hidden;
  margin: 0;
  padding: 1.4rem .9rem 1.5rem;
  list-style: none;
  scroll-padding-inline: 3rem;
  scroll-snap-type: x proximity;
  scrollbar-color: var(--reset-line-strong) transparent;
  scrollbar-width: thin;
  overscroll-behavior-x: contain;
}

.pl-bp-scroller:focus-visible {
  outline: 3px solid var(--pl-ember);
  outline-offset: -3px;
  border-radius: var(--pl-radius-sm);
}

.pl-bp-scroller::-webkit-scrollbar { height: 10px; }
.pl-bp-scroller::-webkit-scrollbar-thumb {
  border: 3px solid transparent;
  border-radius: 999px;
  background: var(--reset-line-strong);
  background-clip: content-box;
}

.pl-bp-tier {
  display: grid;
  grid-row: 1 / -1;
  grid-template-rows: subgrid;
  width: var(--bp-card);
  padding-inline: calc(var(--bp-gap) / 2);
  scroll-snap-align: center;
}

/* Subgrid держит три ряда выровненными там, где он есть. Где его нет,
   раскладка не ломается: ступень остаётся колонкой из трёх блоков, просто
   базовые линии карточек выравниваются по своей ступени, а не по ленте. */
@supports not (grid-template-rows: subgrid) {
  .pl-bp-tier { grid-row: auto; grid-template-rows: 1fr auto auto; gap: .55rem; }
  .pl-bp-scroller { grid-template-rows: auto; align-items: end; }
}

.pl-bp-tier--hi { width: var(--bp-card-hi); }

/* ===== Карточка награды =================================================== */

.pl-bp-tier__card {
  position: relative;
  display: grid;
  align-content: center;
  gap: .35rem;
  overflow: hidden;
  padding: .9rem .75rem .8rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius-sm);
  background: var(--reset-panel);
  color: inherit;
  font: inherit;
  text-align: center;
  box-shadow: var(--pl-lift-sm);
  cursor: pointer;
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}

.pl-bp-tier__card:hover {
  transform: translate(-1px, -3px);
  box-shadow: var(--pl-lift), 0 0 26px -8px color-mix(in srgb, var(--rarity) 70%, transparent);
}

.pl-bp-tier__card:active { transform: translate(1px, 1px); box-shadow: var(--pl-lift-sm); }

/* Аура редкости под артом. Тот же приём, что на карточке инвентаря, — игрок
   уже читает этот цвет как «насколько ценно», и на дороге он экономит целую
   строку подписи. */
.pl-bp-tier__card::before {
  position: absolute;
  top: -30%;
  left: 50%;
  width: 130%;
  height: 90%;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--rarity) 42%, transparent), transparent 72%);
  content: "";
  transform: translateX(-50%);
  pointer-events: none;
}

.pl-bp-tier__art {
  position: relative;
  display: grid;
  place-items: center;
  justify-self: center;
  width: 4.6rem;
  height: 4.6rem;
  border: 2px solid color-mix(in srgb, var(--rarity) 55%, transparent);
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--rarity) 45%, transparent), transparent 70%),
    var(--pl-plate);
  color: color-mix(in srgb, var(--rarity) 65%, #ffffff);
  font-size: 1.7rem;
}

.pl-bp-tier__art img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.pl-bp-tier__art .pl-coin { margin: 0; }

.pl-bp-tier--hi .pl-bp-tier__art { width: 6.2rem; height: 6.2rem; font-size: 2.3rem; }

.pl-bp-tier__name {
  overflow: hidden;
  font-family: var(--heading-font-family);
  font-size: var(--fs-xs);
  font-weight: 900;
  line-height: 1.15;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.pl-bp-tier__kind {
  min-height: 1em;
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}

/* Полоса редкости под названием — та самая метка, которую игрок читает
   первой на любой карточке лута. */
.pl-bp-tier__band {
  justify-self: center;
  width: 2.6rem;
  height: 3px;
  border-radius: 999px;
  background: var(--rarity);
}

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

.pl-bp-tier--hi .pl-bp-tier__card {
  border-color: color-mix(in srgb, var(--rarity) 70%, var(--reset-cream));
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--rarity) 14%, transparent), transparent 55%),
    var(--reset-panel-raised);
  box-shadow: var(--pl-lift), 0 0 30px -12px color-mix(in srgb, var(--rarity) 80%, transparent);
}

.pl-bp-tier__flag {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  justify-self: center;
  margin-bottom: .1rem;
  padding: .18rem .55rem;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--pl-gold-hi), var(--pl-gold) 55%, var(--pl-gold-deep));
  color: var(--pl-gold-ink);
  font-family: "JetBrains Mono", monospace;
  font-size: .58rem;
  font-weight: 900;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* Уголки — из той же семьи, что рамка карточки инвентаря. Только у рубежей,
   иначе они перестают что-либо означать. */
.pl-bp-tier--hi .pl-bp-tier__card::after {
  --bp-corner: color-mix(in srgb, var(--rarity) 55%, transparent);
  position: absolute;
  inset: .4rem;
  background:
    linear-gradient(var(--bp-corner), var(--bp-corner)) 0 0 / 14px 2px no-repeat,
    linear-gradient(var(--bp-corner), var(--bp-corner)) 0 0 / 2px 14px no-repeat,
    linear-gradient(var(--bp-corner), var(--bp-corner)) 100% 0 / 14px 2px no-repeat,
    linear-gradient(var(--bp-corner), var(--bp-corner)) 100% 0 / 2px 14px no-repeat,
    linear-gradient(var(--bp-corner), var(--bp-corner)) 0 100% / 14px 2px no-repeat,
    linear-gradient(var(--bp-corner), var(--bp-corner)) 0 100% / 2px 14px no-repeat,
    linear-gradient(var(--bp-corner), var(--bp-corner)) 100% 100% / 14px 2px no-repeat,
    linear-gradient(var(--bp-corner), var(--bp-corner)) 100% 100% / 2px 14px no-repeat;
  content: "";
  pointer-events: none;
}

/* ===== Линия дороги и узел ================================================ */

.pl-bp-tier__rail {
  position: relative;
  display: grid;
  place-items: center;
  height: 3.4rem;
}

/* Отрезок дороги. Вылезает за края ступени ровно на половину зазора, так что
   отрезки соседей стыкуются без шва. */
.pl-bp-tier__rail::before {
  position: absolute;
  top: 50%;
  right: calc(var(--bp-gap) / -2);
  left: calc(var(--bp-gap) / -2);
  height: 0;
  border-top: 4px dashed var(--reset-line-strong);
  content: "";
  transform: translateY(-50%);
}

/* Дорога начинается и кончается на узле, а не улетает за край ленты: пунктир,
   уходящий вправо от последнего уровня, обещает уровни, которых нет. */
.pl-bp-tier:first-child .pl-bp-tier__rail::before { left: 50%; }
.pl-bp-tier:last-child .pl-bp-tier__rail::before { right: 50%; }

/* Пройденная часть дороги — сплошная и горячая. Граница между сплошным и
   пунктиром и есть «ты здесь». */
.pl-bp-tier.is-done .pl-bp-tier__rail::before {
  border-top: 4px solid var(--pl-ember);
  box-shadow: var(--pl-glow-soft);
}

.pl-bp-tier__node {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: var(--bp-node);
  height: var(--bp-node);
  border: var(--pl-border);
  border-radius: 50%;
  background: var(--reset-panel);
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: .8rem;
  font-weight: 900;
  /* Первое кольцо прячет за узлом линию дороги, второе — обычная тень
     кабинета: один слот box-shadow оба не унесёт. */
  box-shadow: 0 0 0 .32rem var(--reset-panel-raised), var(--pl-lift-sm);
}

.pl-bp-tier.is-done .pl-bp-tier__node {
  border-color: transparent;
  background: var(--pl-ember-ramp);
  color: var(--pl-ember-ink);
}

.pl-bp-tier.is-ready .pl-bp-tier__node {
  border-color: transparent;
  background: linear-gradient(135deg, var(--pl-gold-hi), var(--pl-gold) 55%, var(--pl-gold-deep));
  color: var(--pl-gold-ink);
}

/* Где игрок стоит прямо сейчас. Пульсирующее кольцо, а не заливка: уровень,
   на котором ты стоишь, обычно ещё и уровень с невзятой наградой, и заливка
   стёрла бы более полезный из двух фактов. */
.pl-bp-tier.is-current .pl-bp-tier__node::after {
  position: absolute;
  inset: -.32rem;
  border-radius: 50%;
  content: "";
  animation: pl-bp-here 2.1s ease-in-out infinite;
}

@keyframes pl-bp-here {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--pl-ember) 70%, transparent); }
  50% { box-shadow: 0 0 0 .55rem color-mix(in srgb, var(--pl-ember) 0%, transparent); }
}

/* ===== Действие под узлом ================================================= */

.pl-bp-tier__act { display: grid; justify-items: center; min-height: 2.3rem; }

.pl-bp-tier__claim {
  width: 100%;
  min-height: 2.3rem;
  padding: .35rem .6rem;
  border-radius: 999px;
  font-size: .72rem;
  line-height: 1;
  animation: pl-bp-beat 1.5s ease-in-out infinite;
}

.pl-bp-tier__claim:hover:not(:disabled) { animation: none; }

@keyframes pl-bp-beat {
  0%, 100% { transform: none; }
  50% { transform: translateY(-2px); }
}

.pl-bp-tier__state {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  align-self: center;
  padding: .26rem .6rem;
  border: 2px solid var(--reset-line-strong);
  border-radius: 999px;
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.pl-bp-tier__state.is-taken {
  border-color: transparent;
  background: var(--reset-ok);
  color: var(--reset-tile-ink);
}

/* Взятая награда гаснет: на дороге важно, что осталось, а не что уже в
   рюкзаке. Полностью прятать нельзя — тогда пропадает ощущение пути. */
.pl-bp-tier.is-taken .pl-bp-tier__card { opacity: .58; box-shadow: none; }
.pl-bp-tier.is-taken .pl-bp-tier__card:hover { opacity: .85; box-shadow: var(--pl-lift-sm); }

/* Ступень, до которой ещё не дошли, приглушена — но замка на ней нет и не
   будет: «закрыто» здесь значит только «не дошёл». */
.pl-bp-tier.is-ahead .pl-bp-tier__card { opacity: .82; }

/* Момент выдачи. Одно кольцо, разлетающееся от карточки: короткая расписка о
   том, что нажатие сработало. */
.pl-bp-tier.is-popping .pl-bp-tier__card::before {
  animation: pl-bp-pop .7s ease-out;
}

@keyframes pl-bp-pop {
  0% { opacity: .2; transform: translateX(-50%) scale(.6); }
  40% { opacity: 1; }
  100% { opacity: 0; transform: translateX(-50%) scale(1.9); }
}

.pl-bp-track__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  padding: .8rem 1.1rem;
  border-top: var(--pl-border);
  background: var(--reset-panel);
  color: var(--reset-muted);
}

.pl-bp-track__foot b { color: var(--reset-cream); }

.pl-bp-empty {
  display: grid;
  grid-column: 1 / -1;
  gap: .5rem;
  justify-items: center;
  padding: 2rem 1rem;
  color: var(--reset-muted);
  font-size: var(--fs-sm);
  text-align: center;
}

.pl-bp-empty i { font-size: 1.6rem; opacity: .5; }

/* ===== Нижний ряд: откуда опыт и что в сезоне ============================= */

.pl-bp-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}

.pl-bp-panel {
  overflow: hidden;
  border: var(--pl-border);
  border-radius: var(--pl-radius);
  background: var(--reset-panel-raised);
  box-shadow: var(--pl-lift-sm);
}

.pl-bp-panel__head {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .9rem 1.1rem;
  border-bottom: var(--pl-border);
  background: var(--reset-panel);
}

.pl-bp-panel__head h3 {
  margin: 0;
  font-family: var(--heading-font-family);
  font-size: var(--fs-md);
  font-weight: 900;
  text-transform: uppercase;
}

.pl-bp-panel__head i { color: var(--pl-ember); }
.pl-bp-panel__head .pl-count { margin-left: auto; }

.pl-bp-earn { display: grid; }

.pl-bp-earn__row {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr) auto;
  gap: .8rem;
  align-items: center;
  padding: .85rem 1.1rem;
  border-top: var(--pl-border);
}

.pl-bp-earn__row:first-child { border-top: 0; }

.pl-bp-earn__icon {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius-sm);
  background: var(--reset-panel);
  color: var(--pl-ember);
  font-size: 1rem;
  box-shadow: var(--pl-lift-sm);
}

.pl-bp-earn__text { display: grid; min-width: 0; gap: .1rem; }

.pl-bp-earn__text b {
  font-family: var(--heading-font-family);
  font-size: var(--fs-sm);
  font-weight: 900;
}

.pl-bp-earn__text small { color: var(--reset-muted); font-size: var(--fs-xs); }

.pl-bp-earn__xp {
  padding: .3rem .7rem;
  border: 2px solid color-mix(in srgb, var(--pl-ember) 45%, transparent);
  border-radius: 999px;
  color: var(--pl-ember);
  font-family: "JetBrains Mono", monospace;
  font-size: .74rem;
  font-weight: 900;
  white-space: nowrap;
}

.pl-bp-vault {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: .8rem;
  padding: 1.1rem;
}

.pl-bp-vault__cell {
  display: grid;
  gap: .2rem;
  padding: .9rem .8rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius-sm);
  background: var(--reset-panel);
  text-align: center;
  box-shadow: var(--pl-lift-sm);
}

.pl-bp-vault__cell i { color: color-mix(in srgb, var(--rarity) 78%, var(--reset-cream)); font-size: 1.15rem; }

.pl-bp-vault__cell b {
  font-family: var(--heading-font-family);
  font-size: var(--fs-lg);
  font-weight: 900;
  line-height: 1;
}

.pl-bp-vault__cell small {
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.pl-bp-vault__note {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--reset-muted);
  font-size: var(--fs-xs);
  text-align: center;
}

/* ===== Карточка награды в лупе ============================================
   Открывается по клику на ступень. Нужна ровно для одного: показать арт
   крупно и сказать, на каком уровне он лежит. */

.pl-bp-sheet { width: min(26rem, 100%); text-align: center; }

.pl-bp-sheet__art {
  position: relative;
  display: grid;
  place-items: center;
  justify-self: center;
  width: 11rem;
  height: 11rem;
  margin: .4rem auto .9rem;
  overflow: hidden;
  border: 3px solid color-mix(in srgb, var(--rarity) 60%, transparent);
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--rarity) 50%, transparent), transparent 70%),
    var(--pl-plate);
  color: color-mix(in srgb, var(--rarity) 70%, #ffffff);
  font-size: 3.4rem;
  box-shadow: 0 0 40px -10px color-mix(in srgb, var(--rarity) 75%, transparent);
}

.pl-bp-sheet__art img { width: 100%; height: 100%; object-fit: cover; }

.pl-bp-sheet__rarity {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .22rem .7rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--rarity) 20%, transparent);
  color: color-mix(in srgb, var(--rarity) 72%, var(--reset-cream));
  font-family: "JetBrains Mono", monospace;
  font-size: .66rem;
  font-weight: 900;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.pl-bp-sheet__rarity::before {
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  background: var(--rarity);
  content: "";
}

.pl-bp-sheet h2 { margin: .55rem 0 .1rem; font-size: var(--fs-lg); }

.pl-bp-sheet__kind {
  margin: 0;
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-2xs);
  letter-spacing: .1em;
  text-transform: uppercase;
}

.pl-bp-sheet__line {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  margin: 1rem 0 .2rem;
  padding: .7rem .8rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius-sm);
  background: var(--reset-panel);
  color: var(--reset-muted);
  font-size: var(--fs-sm);
}

.pl-bp-sheet__line b { color: var(--reset-cream); }
.pl-bp-sheet__actions { display: grid; gap: .5rem; margin-top: .9rem; }

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

@media (max-width: 1080px) {
  .pl-bp-cols { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 900px) {
  .pl-bp-season { grid-template-columns: minmax(0, 1fr); }
  .pl-bp-season__side { order: -1; justify-content: space-between; }
}

@media (max-width: 620px) {
  .pl-pass-view {
    --bp-card: 9.4rem;
    --bp-card-hi: 11.4rem;
    --bp-node: 2.4rem;
  }

  .pl-bp-season { padding: 1.1rem; }
  .pl-bp-dial { width: 8.4rem; height: 8.4rem; }
  .pl-bp-dial__ring { mask: radial-gradient(farthest-side, transparent calc(100% - 9px), #000 calc(100% - 8px)); -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 9px), #000 calc(100% - 8px)); }
  .pl-bp-dial__face { width: calc(100% - 22px); height: calc(100% - 22px); }
  .pl-bp-seal { width: 7rem; height: 7rem; }
  .pl-bp-tier__art { width: 3.9rem; height: 3.9rem; font-size: 1.45rem; }
  .pl-bp-tier--hi .pl-bp-tier__art { width: 5rem; height: 5rem; font-size: 1.9rem; }
  .pl-bp-track__head h3 { width: 100%; margin-right: 0; }
  .pl-bp-scroller { padding-inline: .6rem; }
  .pl-bp-earn__row { grid-template-columns: 2.2rem minmax(0, 1fr); }
  .pl-bp-earn__xp { grid-column: 2; justify-self: start; }
}

/* Прокрутка ленты пальцем — это уже движение; всё остальное на экране в
   режиме пониженной анимации замирает, включая печать. */
@media (prefers-reduced-motion: reduce) {
  .pl-bp-seal__type,
  .pl-bp-meter__fill::after,
  .pl-bp-tier__claim,
  .pl-bp-tier.is-current .pl-bp-tier__node::after,
  .pl-bp-tier.is-popping .pl-bp-tier__card::before {
    animation: none;
  }

  .pl-bp-meter__fill { transition: none; }
  .pl-bp-tier__card:hover { transform: none; }
}
