/* Frontline mission path --------------------------------------------------
   Kept separate from the already-large player.css so the progression system
   can evolve without changing the inventory, store or profile surfaces.

   The rule this file follows: this is the screen where a player is paid, so it
   has to *feel* like being paid. Everything here is a sticker — 2px ink border,
   a hard offset shadow, a saturated fill — the same physical idea the rest of
   the cabinet is built on, just turned up. The rank strip is a fairground
   banner, every goal is a colour-coded card, and the two things a player can
   act on right now (a reward that is ready, the level they stand on) are the
   only things that move.

   Colour still carries meaning rather than mood: the category tint is what
   makes a wall of 130 goals scannable, and the accent is reserved for "ready".
   Loud is not the same as noisy. */

.pl-missions-view {
  --mission-accent: var(--reset-sun);
  --mission-hot: var(--reset-coral);
  --mission-good: var(--reset-ok);
  /* Controls that sit on the saturated banner keep a light ground in both
     themes (same rule the core tokens state for tiles), so their ink cannot
     come from the --reset-ink-* twins — those flip light in dark mode and
     vanish on a white pill. The banner carries its own fixed pair instead. */
  --band-ok: #1c7b52;
  --band-bad: #b32626;
  /* One stripe tile, shared by the rank bar and every card meter, so the two
     kinds of progress on this screen crawl at the same pitch. */
  --mission-stripes: linear-gradient(45deg,
    rgba(36, 29, 20, .15) 25%, transparent 25%,
    transparent 50%, rgba(36, 29, 20, .15) 50%,
    rgba(36, 29, 20, .15) 75%, transparent 75%, transparent);
  /* The cabinet is full-bleed, which suits a wall of loot tiles. A reading
     surface is not loot: past this width the goal and its reward end up a
     forearm apart, so the screen stops growing and centres instead. */
  max-width: 108rem;
  margin-inline: auto;
}

/* ===== Rank banner ========================================================
   Level, bar, points, career line and the last verified match in one band.
   It is the loudest object on the page on purpose: it is the running total of
   everything the list below is for. */

/* The banner used to *be* the orange: a full-width sun-to-coral gradient with
   every control on it flipped to dark ink. It was the loudest thing on the
   page and also the flattest — a slab of colour with no light in it, and one
   that forced a second colour system on every pill it carried.

   It is now a panel like the rest of the cabinet, lit from its top-left corner
   by the same ember the rail burns. The colour is spent on the two things that
   are actually the player's score — the level badge and the XP fill — and the
   band around them stays dark so those two read as lit objects rather than as
   pale patches on an orange field. */
.pl-mission-rank {
  /* Every control inside used to be told, individually, to paint itself for a
     saturated ground. Redefining the two tokens they all read flips the whole
     band at once and keeps it correct in both themes. */
  --reset-tile-ink: var(--reset-cream);
  --reset-control-surface: var(--reset-panel-raised);
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 1.7rem);
  overflow: hidden;
  margin-bottom: 1rem;
  padding: clamp(1.15rem, 2.4vw, 1.7rem);
  border: var(--pl-edge);
  border-radius: var(--pl-radius);
  background:
    radial-gradient(58% 150% at 4% -20%, color-mix(in srgb, var(--pl-ember) 34%, transparent), transparent 68%),
    radial-gradient(46% 130% at 96% 120%, color-mix(in srgb, var(--pl-flare) 20%, transparent), transparent 70%),
    linear-gradient(150deg, var(--reset-panel-raised) 0%, var(--reset-panel) 100%);
  color: var(--reset-cream);
  box-shadow: var(--pl-lift), var(--pl-glow-soft);
}

/* The hatch, angled with everything else in the cabinet. It is enough to stop
   a 1400px-wide panel from reading as a flat slab and cheap enough to cost
   nothing. */
.pl-mission-rank::before {
  position: absolute;
  inset: 0;
  background-image: var(--pl-hatch);
  opacity: .8;
  pointer-events: none;
  content: "";
}

.pl-mission-rank > * { position: relative; z-index: 1; }

/* The rank plate. An octagon rather than a rounded square, because every badge
   a player has earned in every shooter they have played is one — and it is the
   only object on the band allowed to carry the full ember ramp, which is what
   makes the level, and not the panel, the thing the eye lands on. */
.pl-mission-rank__badge {
  position: relative;
  display: grid;
  place-items: center;
  align-content: center;
  align-self: center;
  gap: .05rem;
  width: clamp(5.6rem, 9vw, 6.8rem);
  height: clamp(5.6rem, 9vw, 6.8rem);
  border: 0;
  /* The outer octagon is the rim; ::before cuts the plate out of the middle of
     it. Two stops of the same ramp, lit from opposite corners, is enough to
     read as pressed metal — which is all a rank plate has ever been. */
  background: linear-gradient(150deg, #ffd98c 0%, var(--pl-ember) 55%, #c93c05 100%);
  clip-path: polygon(29% 0, 71% 0, 100% 29%, 100% 71%, 71% 100%, 29% 100%, 0 71%, 0 29%);
  color: #fff;
  transform: none;
  filter: drop-shadow(0 12px 26px color-mix(in srgb, var(--pl-ember) 60%, transparent));
}

.pl-mission-rank__badge::before {
  position: absolute;
  inset: 6px;
  background: linear-gradient(330deg, #ffb43d 0%, #ff7a12 48%, #ff5c00 100%);
  clip-path: polygon(29% 0, 71% 0, 100% 29%, 100% 71%, 71% 100%, 29% 100%, 0 71%, 0 29%);
  content: "";
}

.pl-mission-rank__badge > * { position: relative; }

.pl-mission-rank__badge b {
  font-family: var(--heading-font-family);
  font-size: clamp(2.7rem, 5vw, 3.5rem);
  font-weight: 900;
  line-height: .9;
  letter-spacing: -.05em;
  text-shadow: 0 3px 0 rgba(120, 40, 0, .38);
}

.pl-mission-rank__badge small {
  font-family: "JetBrains Mono", monospace;
  font-size: .62rem;
  font-weight: 900;
  letter-spacing: .3em;
  text-indent: .3em;
  text-shadow: 0 1px 0 rgba(120, 40, 0, .35);
  opacity: .85;
}

.pl-mission-rank__body { min-width: 0; }

.pl-mission-rank__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: .55rem;
}

.pl-mission-rank__head h2 {
  margin: 0;
  font-size: var(--fs-xl);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.02em;
}

/* An XP bar you can actually see from across a desk: bordered like everything
   else here, with the stripes crawling so a half-full bar reads as *filling*
   rather than as a static measurement. */
.pl-mission-progress {
  position: relative;
  overflow: hidden;
  height: 1.35rem;
  border: 0;
  border-radius: 999px;
  /* A groove cut into the panel rather than a pill laid on top of it: the
     track is the page showing through, the fill is the only lit thing in the
     channel. */
  background: color-mix(in srgb, var(--reset-cream) 10%, transparent);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, .35);
}

/* Two layers: the crawling stripe on top, the ember ramp underneath. Only the
   stripe moves — sliding the ramp too would drag its own gradient across the
   fill and put a seam in it once per cycle. */
.pl-mission-progress > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background-color: var(--pl-ember);
  background-image: var(--mission-stripes), var(--pl-ember-ramp);
  background-size: 14px 14px, auto;
  background-position: 0 0, 0 0;
  box-shadow: 0 0 18px -2px color-mix(in srgb, var(--pl-ember) 80%, transparent);
  animation: pl-mission-fill 1.1s linear infinite;
  transition: width .5s cubic-bezier(.2, .8, .2, 1);
}

@keyframes pl-mission-fill { to { background-position: 14px 0, 0 0; } }

/* A 45° stripe inside a square tile is the one combination that still lines up
   across tile edges, which is what lets the crawl loop on a single shift of
   exactly one tile — any other angle visibly jumps once per cycle. */
@keyframes pl-mission-stripes { to { background-position: 14px 0; } }

/* Sits over both halves of the bar. The unfilled track is the banner darkened,
   not a grey, so one ink colour reads on either side of the fill edge. */
.pl-mission-progress__value {
  position: absolute;
  top: 50%;
  right: .7rem;
  color: var(--reset-cream);
  font-family: "JetBrains Mono", monospace;
  font-size: .72rem;
  font-weight: 900;
  letter-spacing: .04em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .6);
  transform: translateY(-50%);
}

.pl-mission-rank__numbers {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin: .5rem 0 0;
  color: color-mix(in srgb, var(--reset-tile-ink) 74%, transparent);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-2xs);
  font-weight: 700;
}

/* The score, as its own sticker. The same star the cards pay out in, so the
   symbol on a reward mark and the number it lands in are visibly one currency. */
.pl-mission-rank__numbers b {
  display: inline-flex;
  align-items: center;
  gap: .32rem;
  padding: .22rem .6rem;
  border: 1.5px solid var(--reset-tile-ink);
  border-radius: 999px;
  background: var(--reset-control-surface);
  color: var(--reset-tile-ink);
  font-size: var(--fs-sm);
  font-weight: 900;
  box-shadow: 2px 2px 0 rgba(36, 29, 20, .55);
}

.pl-mission-rank__numbers b i { color: var(--reset-ink-sun); font-size: .8rem; }
.pl-mission-rank__numbers small { color: inherit; font-weight: 700; }

/* Career totals as a sentence, not as four bordered tiles. */
.pl-mission-rank__stats {
  display: flex;
  flex-wrap: wrap;
  gap: .1rem 1.5rem;
  margin: .7rem 0 0;
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.pl-mission-rank__stats b {
  color: var(--reset-cream);
  font-size: var(--fs-sm);
  font-weight: 900;
}

.pl-mission-last {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: .6rem;
  width: min(22rem, 100%);
  padding: .6rem .7rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius-sm);
  background: var(--reset-control-surface);
  color: var(--reset-tile-ink);
  box-shadow: var(--pl-lift-sm);
}

.pl-mission-last__result {
  display: grid;
  place-items: center;
  width: 2.2rem;
  height: 2.2rem;
  border: 1.5px solid var(--reset-tile-ink);
  border-radius: 50%;
  background: var(--reset-line-strong);
  color: var(--reset-tile-ink);
  font-size: .8rem;
}

.pl-mission-last.is-win .pl-mission-last__result { background: var(--mission-good); }
.pl-mission-last.is-loss .pl-mission-last__result { background: var(--reset-danger); }

.pl-mission-last__copy { display: grid; min-width: 0; gap: .1rem; }
.pl-mission-last__copy b { overflow: hidden; font-size: var(--fs-xs); text-overflow: ellipsis; white-space: nowrap; }

.pl-mission-last__copy span,
.pl-mission-last__kda {
  color: color-mix(in srgb, var(--reset-tile-ink) 68%, transparent);
  font-size: var(--fs-2xs);
}

.pl-mission-last__kda {
  font-family: "JetBrains Mono", monospace;
  white-space: nowrap;
}

.pl-mission-last__kda b { color: var(--reset-tile-ink); }
.pl-mission-last .pl-btn { min-height: 2.2rem; padding: .3rem .65rem; font-size: var(--fs-2xs); }

/* ===== Daily ==============================================================
   The store tab's card, glow and all. It used to be muted here on the grounds
   that one pulsing box next to a claimable list is one too many — but a free
   reward that has been sitting unclaimed for a day *should* nag. */

.pl-mission-daily {
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  margin-bottom: 1rem;
  padding: 1.05rem 1.2rem;
}

.pl-mission-daily .pl-daily__gift { width: 3.4rem; height: 3.4rem; font-size: 1.3rem; }
.pl-mission-daily__copy { min-width: 0; }
.pl-mission-daily__copy h3 { font-size: var(--fs-lg); font-weight: 900; }

.pl-mission-daily__rewards {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: .35rem;
}

/* Seven pips, one per day of the cycle. Bordered like everything else so an
   unlit pip is still an object rather than a smudge. */
.pl-mission-streak {
  display: flex;
  gap: .3rem;
  margin-top: .5rem;
}

.pl-mission-streak i {
  display: block;
  width: clamp(1.1rem, 3.4vw, 2rem);
  height: .55rem;
  border: 1.5px solid var(--reset-line-strong);
  border-radius: 999px;
  background: var(--reset-panel);
}

.pl-mission-streak i.is-on {
  border-color: transparent;
  background: var(--pl-ember-ramp);
  box-shadow: 0 0 12px -2px color-mix(in srgb, var(--pl-ember) 85%, transparent);
}

/* ===== Board ============================================================== */

.pl-mission-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(19rem, 23rem);
  align-items: start;
  gap: 1rem;
}

.pl-mission-board,
.pl-level-path {
  border: var(--pl-border);
  border-radius: var(--pl-radius);
  background: var(--reset-panel-raised);
  box-shadow: var(--pl-lift);
}

.pl-mission-board {
  min-width: 0;
  padding: clamp(.9rem, 2vw, 1.25rem);
}

.pl-mission-board__head {
  align-items: center;
  margin-bottom: .85rem;
}

.pl-mission-board__head h2,
.pl-level-path__head h2 {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  margin: 0;
  font-size: var(--fs-lg);
  font-weight: 900;
}

/* The heading used to carry a marker-pen swipe behind the words. Under caps at
   900 it stopped reading as a highlight and started reading as a redaction, so
   the emphasis moved off the text and onto a lit bar in front of it — the same
   mark the masthead uses, one size down. */
.pl-mission-board__head h2,
.pl-level-path__head h2 { position: relative; }

.pl-mission-board__head h2::before,
.pl-level-path__head h2::before {
  content: "";
  flex: 0 0 auto;
  width: .34rem;
  height: 1.15em;
  margin-right: .15rem;
  border-radius: 2px;
  background: var(--reset-ink-sun);
  box-shadow: 0 0 14px -1px color-mix(in srgb, var(--reset-ink-sun) 80%, transparent);
}

.pl-level-path__head h2::before {
  background: var(--reset-ink-sky);
  box-shadow: 0 0 14px -1px color-mix(in srgb, var(--reset-ink-sky) 80%, transparent);
}

.pl-mission-board__head h2 > i,
.pl-level-path__head h2 > i {
  color: var(--reset-ink-sun);
  font-size: .9em;
}

.pl-level-path__head h2 > i { color: var(--reset-ink-sky); }

.pl-count {
  flex: 0 0 auto;
  padding: .42rem .8rem;
  border: 0;
  border-radius: 999px;
  background: var(--pl-ember-ramp);
  color: var(--pl-ember-ink);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-2xs);
  font-weight: 900;
  box-shadow: var(--pl-glow-soft);
}

.pl-mission-filters {
  display: grid;
  justify-items: start;
  gap: .5rem;
  margin-bottom: .8rem;
}

/* State is a segmented switch — one track, four positions — instead of five
   free-floating outlined pills competing with the category row under it. */
.pl-mission-state-filters {
  flex-wrap: nowrap;
  max-width: 100%;
  gap: .15rem;
  overflow-x: auto;
  padding: .25rem;
  border: 1.5px solid var(--reset-cream);
  border-radius: 999px;
  background: var(--reset-panel);
  scrollbar-width: none;
}

.pl-mission-state-filters::-webkit-scrollbar { display: none; }

.pl-mission-state-filters .pl-chip {
  flex: 0 0 auto;
  min-height: 2.05rem;
  padding: .3rem .85rem;
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  font-size: var(--fs-2xs);
  font-weight: 800;
  white-space: nowrap;
}

.pl-mission-state-filters .pl-chip:hover {
  color: var(--reset-cream);
  background: var(--reset-line);
  transform: none;
  box-shadow: none;
}

/* The selected position is the ember, the same fill the rail and the view tabs
   use. Gold on this screen means *reward*; every "you are here" mark in the
   cabinet is orange, and a filter is a where, not a prize. */
.pl-mission-state-filters .pl-chip.is-active {
  border-color: transparent;
  background: var(--pl-ember-ramp);
  color: var(--pl-ember-ink);
  box-shadow: var(--pl-glow-soft);
}

/* Nine types, one row, no scrollbar drawn under it: the row is cut off mid
   chip at the edge, which says "there is more" without a grey rail saying it
   a second time. Each chip wears the hue of the goals it filters to, so the
   row is a legend for the card colours under it. */
.pl-mission-category-filters {
  flex-wrap: nowrap;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}

/* A cut chip reads as "swipe me" on a phone and as a rendering fault on a
   desktop, where nothing about the page suggests a horizontal swipe and there
   is room for the whole legend anyway. Past this width the row wraps. */
@media (min-width: 1100px) {
  .pl-mission-category-filters {
    flex-wrap: wrap;
    overflow-x: visible;
  }
}

.pl-mission-category-filters::-webkit-scrollbar { display: none; }

.pl-mission-category-filters .pl-chip {
  flex: 0 0 auto;
  min-height: 2.05rem;
  padding: .3rem .75rem;
  border-width: 1.5px;
  font-size: var(--fs-2xs);
  font-weight: 800;
  white-space: nowrap;
}

.pl-mission-category-filters .pl-chip i {
  color: var(--task-ink, var(--reset-ink-sun));
  font-size: .75rem;
}

.pl-mission-category-filters .pl-chip.is-active {
  border-color: var(--reset-cream);
  background: var(--task-tint, var(--mission-accent));
  color: var(--reset-tile-ink);
  box-shadow: 2px 2px 0 var(--reset-shadow);
}

.pl-mission-category-filters .pl-chip.is-active i { color: var(--reset-tile-ink); }

.pl-chip__count {
  display: inline-grid;
  place-items: center;
  min-width: 1.25rem;
  padding: 0 .25rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--reset-cream) 14%, transparent);
  color: inherit;
  font-family: "JetBrains Mono", monospace;
  font-size: .6rem;
  font-style: normal;
  font-weight: 900;
}

.pl-mission-state-filters .pl-chip.is-active .pl-chip__count {
  background: color-mix(in srgb, var(--reset-tile-ink) 22%, transparent);
}

/* ===== Task cards =========================================================
   A goal has to answer three things at a glance: what to do, how far along I
   am, what I get. So the card carries the name, one line of instruction, a bar
   and one reward mark — and it is a card, not a row, because a row stretched
   across a 2560px monitor puts the goal and its reward a forearm apart.

   Every card is a sticker: ink border, hard shadow, a colour strip along the
   top and a glyph badge that sits a few degrees off square. The strip is the
   category, and it is doing real work — it is what lets an eye find the three
   "Точность" cards in a grid of twenty-four without reading a word.

   The list fills the board with as many columns as fit: one on a phone, two on
   a laptop, four on a wide desktop. */

.pl-mission-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr));
  gap: .7rem;
}

.pl-mission-task {
  position: relative;
  display: grid;
  grid-template-areas:
    "icon title reward"
    "icon copy  copy"
    "foot foot  foot";
  grid-template-columns: auto minmax(0, 1fr) auto;
  /* Cards in a row stretch to the tallest — usually one with a Claim button —
     so the last row takes the slack and the bar stays pinned to the bottom
     instead of leaving a hole under it. */
  grid-template-rows: auto auto 1fr;
  gap: .35rem .8rem;
  overflow: hidden;
  padding: .95rem 1rem .95rem 1.35rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius);
  /* The tint used to be mixed *into* the card ground. On the dark page that
     turns every warm category into olive — a yellow at 9% over a blue-black
     panel is mud, not a hint. It is a wash bleeding in from the strip instead,
     so the card keeps the panel colour the rest of the cabinet uses and the
     category still reads from the corner of an eye. */
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--task-tint, var(--mission-accent)) 20%, transparent), transparent 42%),
    var(--reset-panel-raised);
  box-shadow: var(--pl-lift-sm);
  transition: transform .16s ease, box-shadow .16s ease, background-color .16s ease;
}

/* The category strip, down the leading edge rather than across the top. Same
   job — a column of cards reads as a colour-sorted stack — but on the edge the
   eye already travels down, and it leaves the card's top rule to the panel. */
.pl-mission-task::before {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: .4rem;
  background: var(--task-tint, var(--mission-accent));
  box-shadow: 0 0 16px -1px color-mix(in srgb, var(--task-tint, var(--mission-accent)) 70%, transparent);
  content: "";
}

/* The same shine the loot grid sweeps across an item card on hover — the two
   screens are the same cabinet, so they get the same gesture. */
.pl-mission-task::after {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .28), transparent);
  pointer-events: none;
  content: "";
}

.pl-mission-task:hover {
  transform: translate(-2px, -3px);
  box-shadow: var(--pl-lift-hi);
}

.pl-mission-task:hover::after { animation: pl-mission-shine .55s ease-out; }

@keyframes pl-mission-shine {
  from { left: -60%; }
  to { left: 130%; }
}

.pl-mission-task:hover .pl-mission-task__icon { animation: pl-mission-wiggle .5s ease-in-out; }

@keyframes pl-mission-wiggle {
  0%, 100% { transform: rotate(-5deg); }
  35% { transform: rotate(6deg) scale(1.06); }
  70% { transform: rotate(-8deg) scale(1.03); }
}

/* Ready to claim: a filled accent card inside a ring of the same colour. A
   good session can finish a dozen goals at once, so this state is loud but
   still — twelve cards breathing in unison is a strobe, not a celebration.
   The one thing that moves is the button that gets pressed. */
.pl-mission-task.is-claimable {
  border-color: color-mix(in srgb, var(--mission-hot) 70%, transparent);
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--mission-hot) 26%, transparent), transparent 55%),
    var(--reset-panel-raised);
  box-shadow: var(--pl-lift-sm),
    0 0 0 1px color-mix(in srgb, var(--mission-hot) 45%, transparent),
    0 0 28px -6px color-mix(in srgb, var(--mission-hot) 70%, transparent);
}

.pl-mission-task.is-claimable::before {
  background: var(--mission-hot);
  box-shadow: 0 0 20px 0 color-mix(in srgb, var(--mission-hot) 85%, transparent);
}

.pl-mission-task.is-claimable:hover {
  box-shadow: var(--pl-lift-hi),
    0 0 0 1px color-mix(in srgb, var(--mission-hot) 60%, transparent),
    0 0 38px -4px color-mix(in srgb, var(--mission-hot) 80%, transparent);
}

.pl-mission-task.is-claimed {
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--mission-good) 16%, transparent), transparent 45%),
    var(--reset-panel-raised);
  opacity: .82;
}
.pl-mission-task.is-claimed::before { background: var(--mission-good); box-shadow: none; }
.pl-mission-task.is-locked { background: var(--reset-panel); opacity: .55; }
.pl-mission-task.is-locked::before { background: var(--reset-line-strong); box-shadow: none; }

/* The glyph badge. Eight kinds of goal against five hues means a couple share
   a tint; the glyph is what tells them apart, the fill is what makes the list
   scannable at arm's length. */
.pl-mission-task__icon {
  display: grid;
  grid-area: icon;
  place-items: center;
  align-self: start;
  width: 2.85rem;
  height: 2.85rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius-sm);
  background: var(--task-tint, var(--mission-accent));
  color: var(--reset-tile-ink);
  font-size: 1.05rem;
  transform: rotate(-5deg);
  box-shadow: var(--pl-lift-sm);
}

.pl-mission-task.is-claimable .pl-mission-task__icon { background: var(--mission-accent); }

/* Reserved for the level rail, where exactly one node is ever claimable at a
   time and a nudge reads as an invitation rather than as a twitching grid. */
@keyframes pl-mission-nudge {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}

.pl-mission-task.is-claimed .pl-mission-task__icon { background: var(--mission-good); }

.pl-mission-task.is-locked .pl-mission-task__icon {
  background: var(--reset-panel-raised);
  color: var(--reset-muted);
  box-shadow: none;
}

.pl-mission-task h3 {
  grid-area: title;
  align-self: center;
  margin: 0;
  font-family: var(--heading-font-family);
  font-size: var(--fs-md);
  font-weight: 900;
  line-height: 1.1;
  letter-spacing: -.01em;
  text-transform: uppercase;
}

/* The instruction. Two lines is enough for every line in the catalogue and
   keeps every card in a row the same height. */
.pl-mission-task > p {
  display: -webkit-box;
  grid-area: copy;
  overflow: hidden;
  margin: 0;
  color: var(--reset-muted);
  font-size: var(--fs-xs);
  line-height: 1.4;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.pl-mission-task__foot {
  display: flex;
  grid-area: foot;
  align-items: center;
  align-self: end;
  gap: .6rem;
  margin-top: .6rem;
}

.pl-mission-task__meter {
  flex: 1 1 auto;
  overflow: hidden;
  height: .6rem;
  border: 1.5px solid var(--reset-cream);
  border-radius: 999px;
  background: var(--reset-panel);
}

.pl-mission-task__meter span {
  display: block;
  width: var(--mission-progress);
  height: 100%;
  border-radius: 999px;
  background-color: var(--mission-accent);
  background-image: var(--mission-stripes);
  background-size: 14px 14px;
  transition: width .4s cubic-bezier(.2, .8, .2, 1);
}

/* Past three quarters the bar switches to the hot end of the ramp and starts
   crawling. It is the one nudge on the card that a player has not already been
   told in words — "you are nearly there" is worth an animation. */
.pl-mission-task.is-close .pl-mission-task__meter span {
  background-color: var(--mission-hot);
  animation: pl-mission-stripes .8s linear infinite;
}

/* A full accent bar on an accent card is a bar you cannot see. It goes green
   instead, which also splits the two things the card is saying: the goal is
   finished (green), the reward is not collected yet (yellow). */
.pl-mission-task.is-claimable :is(.pl-mission-task__meter, .pl-mission-reward) { border-color: var(--reset-cream); }
.pl-mission-task.is-claimable .pl-mission-task__meter { background: var(--reset-panel-raised); }
.pl-mission-task.is-claimable .pl-mission-task__meter span { background-color: var(--mission-good); }
.pl-mission-task.is-claimable .pl-mission-task__note { color: var(--reset-cream); }
.pl-mission-task.is-claimable > p { color: color-mix(in srgb, var(--reset-cream) 78%, transparent); }

.pl-mission-task__note {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: .7rem;
  font-weight: 800;
  white-space: nowrap;
}

.pl-mission-task.is-close .pl-mission-task__note { color: var(--reset-ink-coral); }
.pl-mission-task__note.is-done { color: var(--reset-ink-ok); }

/* The prize, priced like a price tag: the payout is the reason to read the
   card, so it is filled rather than outlined. */
.pl-mission-reward {
  grid-area: reward;
  align-self: center;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  flex: 0 0 auto;
  padding: .3rem .6rem;
  border: 1.5px solid var(--reset-cream);
  border-radius: 999px;
  background: var(--mission-accent);
  color: var(--reset-tile-ink);
  font-family: "JetBrains Mono", monospace;
  font-size: .7rem;
  font-weight: 900;
  white-space: nowrap;
  box-shadow: 2px 2px 0 var(--reset-shadow);
}

.pl-mission-reward b {
  display: inline-flex;
  align-items: center;
  gap: .22rem;
  font-weight: 900;
}

.pl-mission-reward b + b {
  padding-left: .45rem;
  border-left: 1.5px solid color-mix(in srgb, var(--reset-tile-ink) 35%, transparent);
}

.pl-mission-reward i { color: var(--reset-tile-ink); font-size: .68rem; }

/* The shared coin is sized for a price tag; inside a row-sized reward mark it
   would outweigh the number it belongs to. */
.pl-mission-reward .pl-coin { width: 1.05rem; height: 1.05rem; }

/* The card behind it is dark in every state now, so the gold tag keeps its own
   ground and only gains the ring the rest of a claimable card wears. */
.pl-mission-task.is-claimable .pl-mission-reward {
  box-shadow: 2px 2px 0 var(--reset-shadow), 0 0 18px -4px color-mix(in srgb, var(--mission-accent) 75%, transparent);
}

.pl-mission-task.is-claimed .pl-mission-reward { background: var(--mission-good); }

.pl-mission-task.is-locked .pl-mission-reward {
  background: var(--reset-panel-raised);
  color: var(--reset-muted);
  border-color: var(--reset-line-strong);
  box-shadow: none;
}

.pl-mission-task.is-locked .pl-mission-reward i { color: var(--reset-muted); }

.pl-mission-task__claim {
  min-height: 2.25rem;
  padding: .32rem .8rem;
  font-size: var(--fs-2xs);
  font-weight: 900;
  animation: pl-mission-beat 1.5s ease-in-out infinite;
}

@keyframes pl-mission-beat {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.045); }
}

.pl-mission-task__claim:hover:not(:disabled) { animation: none; }

/* ===== List furniture ===================================================== */

.pl-mission-status {
  margin: .55rem 0 0;
  color: var(--reset-ink-danger);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-2xs);
  font-weight: 700;
  text-align: center;
}

.pl-mission-status:empty { margin: 0; }

.pl-mission-more { display: flex; justify-content: center; padding-top: .9rem; }
.pl-mission-more[hidden] { display: none; }
.pl-mission-more em { font-family: "JetBrains Mono", monospace; font-style: normal; font-weight: 900; opacity: .7; }

.pl-mission-empty {
  display: grid;
  grid-column: 1 / -1;
  justify-items: center;
  padding: 2.2rem 1rem;
  border: 3px dashed var(--reset-line-strong);
  border-radius: var(--pl-radius);
  color: var(--reset-muted);
  text-align: center;
}

.pl-mission-empty > i {
  display: grid;
  place-items: center;
  width: 3.4rem;
  height: 3.4rem;
  margin-bottom: .7rem;
  border: var(--pl-border);
  border-radius: 50%;
  background: var(--mission-accent);
  color: var(--reset-tile-ink);
  font-size: 1.4rem;
  transform: rotate(-6deg);
  box-shadow: var(--pl-lift-sm);
}

.pl-mission-empty h3 { margin: 0; color: var(--reset-cream); font-size: var(--fs-md); font-weight: 800; }
.pl-mission-empty p { max-width: 33rem; margin: .35rem 0 .8rem; font-size: var(--fs-xs); }
.pl-mission-empty.is-error > i { background: var(--reset-danger); }

.pl-mission-skeleton {
  display: grid;
  grid-template-columns: 2.85rem minmax(0, 1fr);
  align-items: start;
  gap: .7rem;
  min-height: 6.8rem;
  padding: 1.05rem .85rem .85rem;
  border: var(--pl-border);
  border-radius: var(--pl-radius);
  background: var(--reset-panel-raised);
  box-shadow: var(--pl-lift-sm);
}

.pl-mission-skeleton > i,
.pl-mission-skeleton b,
.pl-mission-skeleton em {
  display: block;
  border-radius: .45rem;
  background: linear-gradient(90deg, var(--reset-line), color-mix(in srgb, var(--mission-accent) 34%, var(--reset-line)), var(--reset-line));
  background-size: 200% 100%;
  animation: pl-mission-shimmer 1.3s linear infinite;
}

.pl-mission-skeleton > i { width: 2.85rem; height: 2.85rem; border-radius: var(--pl-radius-sm); }
.pl-mission-skeleton > span { display: grid; align-content: start; gap: .5rem; }
.pl-mission-skeleton b { width: 52%; height: 1rem; }
.pl-mission-skeleton em { width: 88%; height: .55rem; }
.pl-mission-skeleton em:last-child { width: 60%; }

@keyframes pl-mission-shimmer { to { background-position: -200% 0; } }

/* ===== Level rail =========================================================
   The reward ladder, drawn with the parts the cabinet already owns and on the
   same panel as the board, so the screen reads as one material rather than as
   a widget bolted onto it. The track is dashed like a board-game path, because
   that is exactly what it is. */

/* Липнет под мачтой, а не на выдуманные 6.6rem: мачта на этом экране бывает
   и выше, и лестница заезжала под неё верхней ступенью. Высоту меряет
   player.js и кладёт в --pl-top-h — то же число, по которому встают все
   остальные полосы кабинета. */
.pl-level-path {
  position: sticky;
  top: calc(var(--pl-top-h, 6.6rem) + .8rem);
  min-width: 0;
  max-height: calc(100vh - var(--pl-top-h, 6.6rem) - 2.4rem);
  overflow: hidden;
}

.pl-level-path__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .7rem;
  padding: .9rem 1rem;
  border-bottom: var(--pl-border);
  background: color-mix(in srgb, var(--reset-sky) 16%, var(--reset-panel-raised));
}

.pl-level-path__head h2 { font-size: var(--fs-md); }

.pl-level-path__rail {
  position: relative;
  display: grid;
  gap: 0;
  max-height: calc(100vh - 13rem);
  overflow-y: auto;
  padding: .8rem .9rem 2rem;
  scroll-padding-block: 6rem;
  scrollbar-color: var(--reset-line-strong) transparent;
  scrollbar-width: thin;
}

.pl-level-path__rail::before {
  position: absolute;
  top: 1.6rem;
  bottom: 2.6rem;
  left: 2.05rem;
  width: 0;
  border-left: 3px dashed var(--reset-line-strong);
  content: "";
}

.pl-level-step {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr) auto;
  align-items: center;
  gap: .65rem;
  min-height: 3.7rem;
  padding: .35rem 0;
}

.pl-level-step__node {
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border: var(--pl-border);
  border-radius: 50%;
  background: var(--reset-panel);
  color: var(--reset-muted);
  font-family: "JetBrains Mono", monospace;
  font-size: .78rem;
  font-weight: 900;
  /* The first ring hides the dashed track behind the node; the second is the
     cabinet's hard shadow, which a single box-shadow slot cannot carry alone. */
  box-shadow: 0 0 0 .3rem var(--reset-panel-raised), var(--pl-lift-sm);
}

.pl-level-step.is-claimed .pl-level-step__node,
.pl-level-step.is-reached .pl-level-step__node {
  background: var(--mission-good);
  color: var(--reset-tile-ink);
}

.pl-level-step.is-claimable .pl-level-step__node {
  background: var(--mission-accent);
  color: var(--reset-tile-ink);
  animation: pl-mission-nudge 1.5s ease-in-out infinite;
}

/* Where the player is standing. A pulsing ring rather than a fill, because the
   level you are on is usually also the level with a reward waiting on it — and
   repainting the node would erase the more useful of the two facts. One moving
   thing in a column of twenty. */
.pl-level-step.is-current .pl-level-step__node {
  animation: pl-mission-here 2.2s ease-in-out infinite;
}

@keyframes pl-mission-here {
  0%, 100% { box-shadow: 0 0 0 .3rem var(--reset-panel-raised), 0 0 0 .3rem color-mix(in srgb, var(--mission-hot) 70%, transparent); }
  50% { box-shadow: 0 0 0 .3rem var(--reset-panel-raised), 0 0 0 .62rem color-mix(in srgb, var(--mission-hot) 0%, transparent); }
}

.pl-level-step.is-locked { opacity: .5; }

.pl-level-step__copy { display: grid; min-width: 0; gap: .12rem; }

.pl-level-step__copy b {
  overflow: hidden;
  font-family: var(--heading-font-family);
  font-size: var(--fs-sm);
  font-weight: 900;
  letter-spacing: -.01em;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.pl-level-step__copy span { color: var(--reset-muted); font-family: "JetBrains Mono", monospace; font-size: .68rem; font-weight: 700; }

.pl-level-step__reward,
.pl-level-step__start {
  display: inline-flex;
  align-items: center;
  gap: .22rem;
  padding: .2rem .5rem;
  border: 1.5px solid var(--reset-line-strong);
  border-radius: 999px;
  color: var(--reset-ink-sun);
  font-family: "JetBrains Mono", monospace;
  font-size: .68rem;
  font-weight: 900;
}

.pl-level-step__reward.is-claimed {
  border-color: var(--reset-cream);
  background: var(--mission-good);
  color: var(--reset-tile-ink);
}

.pl-level-step__start { color: var(--reset-muted); }

.pl-level-step__claim {
  min-height: 2.1rem;
  padding: .28rem .65rem;
  border-radius: 999px;
  font-size: .68rem;
  font-weight: 900;
  line-height: 1;
  animation: pl-mission-beat 1.5s ease-in-out infinite;
}

.pl-level-step__claim:hover:not(:disabled) { animation: none; }

/* ===== Responsive ========================================================= */

@media (max-width: 1180px) {
  .pl-mission-rank { grid-template-columns: auto minmax(0, 1fr); }
  .pl-mission-last { grid-column: 1 / -1; width: 100%; }
}

@media (max-width: 1040px) {
  .pl-mission-layout { grid-template-columns: minmax(0, 1fr); }

  .pl-level-path {
    position: relative;
    top: auto;
    max-height: none;
  }

  .pl-level-path__rail {
    grid-auto-flow: column;
    grid-auto-columns: minmax(11rem, 13rem);
    max-height: none;
    overflow-x: auto;
    overflow-y: hidden;
    padding: .9rem 1rem 1.2rem;
    scroll-snap-type: x proximity;
  }

  .pl-level-path__rail::before {
    top: 2.05rem;
    right: 2.5rem;
    bottom: auto;
    left: 2.5rem;
    width: auto;
    height: 0;
    border-top: 3px dashed var(--reset-line-strong);
    border-left: 0;
  }

  .pl-level-step {
    grid-template-columns: 2.4rem minmax(0, 1fr);
    align-content: start;
    min-height: 5.6rem;
    padding-right: .7rem;
    scroll-snap-align: start;
  }

  .pl-level-step > :last-child { grid-column: 2; justify-self: start; }
}

@media (max-width: 720px) {
  .pl-mission-daily { grid-template-columns: auto minmax(0, 1fr); }
  .pl-mission-daily__rewards { grid-column: 1 / -1; justify-content: flex-start; }
  .pl-mission-daily .pl-btn { grid-column: 1 / -1; min-height: 44px; }

  .pl-mission-task__icon { width: 2.5rem; height: 2.5rem; font-size: .95rem; }
  .pl-mission-task__claim { min-height: 44px; }
}

@media (max-width: 520px) {
  .pl-mission-rank {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
    gap: .7rem;
    padding: .85rem;
  }

  /* The banner is tall once it stacks, and a badge centred against all of it
     floats free of the name it belongs to. */
  .pl-mission-rank__badge { align-self: start; width: 3.5rem; height: 3.5rem; }
  .pl-mission-rank__badge b { font-size: 1.5rem; }
  .pl-mission-rank__head { align-items: flex-start; flex-direction: column; gap: .3rem; }
  .pl-mission-rank__head h2 { font-size: var(--fs-lg); }
  .pl-mission-rank__numbers { align-items: flex-start; flex-direction: column; gap: .35rem; }
  .pl-mission-last { grid-template-columns: auto minmax(0, 1fr); }
  .pl-mission-last__kda { grid-column: 2; }
  .pl-mission-last .pl-btn { grid-column: 1 / -1; min-height: 44px; }

  .pl-mission-board { padding: .8rem; }

  /* Core stacks a section head on a phone; a two-word title and a counter fit
     on one line, and stacking them here only pushed the list down. */
  .pl-mission-board__head {
    flex-direction: row;
    align-items: center;
  }

  .pl-mission-board__head .pl-count { align-self: center; }

  /* On a phone the card is ~300px wide and the name shares that row with the
     reward, so the reward gives back what it can. */
  .pl-mission-task { padding: .95rem .75rem .75rem; }
  .pl-mission-task > .pl-mission-reward { gap: .3rem; padding: .26rem .48rem; font-size: .66rem; }
  .pl-mission-task > .pl-mission-reward b + b { padding-left: .3rem; }
  .pl-level-path__rail { grid-auto-columns: minmax(10.5rem, 12rem); }
}

.pl-missions-view :is(button, [tabindex]):focus-visible {
  outline: 3px solid var(--reset-sun);
  outline-offset: 3px;
}

/* The whole screen is animated on purpose, so this block has to switch off
   more than it used to: nothing here carries information that the static
   frame does not. */
@media (prefers-reduced-motion: reduce) {
  .pl-mission-progress > span,
  .pl-mission-task.is-close .pl-mission-task__meter span,
  .pl-mission-task:hover::after,
  .pl-mission-task:hover .pl-mission-task__icon,
  .pl-mission-task__claim,
  .pl-level-step.is-claimable .pl-level-step__node,
  .pl-level-step.is-current .pl-level-step__node,
  .pl-level-step__claim,
  .pl-mission-skeleton :is(i, b, em) { animation: none; }

  .pl-mission-progress > span,
  .pl-mission-task__meter span { transition: none; }

  .pl-mission-task:hover { transform: none; }
}
