/* [CHANGE 2026-09-15 perf] Usuniety render-blocking @import fontow (Oswald +
   IBM Plex Mono). Wczesniej: przegladarka pobierala site.tactical.css, PARSLA
   go, dopiero potem wysyla zapytanie o Google Fonts = serialna zaleznosc
   opozniajaca FCP/LCP. teraz rodziny laczone w jednym <link> w szablonach. */
/* ============================================================================
   MORFACE PORTAL — "TACTICAL BLACK" (wariant B, gleboka przebudowa jezyka
   wizualnego). Osobny plik, nic nie nadpisuje. Aktywowany klasa
   body.theme-tactical — bez niej strona wyglada 100% jak dotad.

   Cel: zeby NIE wygladalo jak "wygenerowane przez AI".
   Usuniete klisze: gradient cyan->violet, glassmorphism na kazdej kartce,
   radius 20px wszedzie, emoji-ikony, symetryczne uosredzone uklady,
   "makaronowe" cienkie linie.

   Jezyk: taktyczny czarny druk (print/tactical-brief).
   - czerń warstwowa (ink / ink2 / panel), ziarno + winieta zamiast glow
   - hairline 1px + narożne klamry (HUD) zamiast zaokraglen
   - typografia: Oswald (scisly, uppercase) + IBM Plex Mono (dane/etykiety)
     + Exo 2 (tresc) — mocny kontrast skali, nie "rowne" naglowki
   - JEDEN kolor sygnalny: amber #ff8a2b. Czerwen tylko dla alertow.
   - obrazy: desaturowane, pelny kolor dopiero na hover (edytorski zabieg)
   ========================================================================== */

body.theme-tactical {
  --ink: #06070a;
  --ink-2: #090b0f;
  --panel: #0d1016;
  --panel-2: #12161e;
  --hair: #222834;
  --hair-2: #2f3746;
  --text: #e7eaf0;
  --muted: #8a92a3;
  --dim: #59617280;
  --signal: #ff8a2b;
  --signal-dim: #b35f16;
  --alert: #e0312f;

  color: var(--text);
  background-color: var(--ink);
  /* winieta + glebia, bez "neonowego glow" */
  background-image:
    radial-gradient(120% 80% at 50% -10%, rgba(255, 138, 43, 0.05), transparent 55%),
    radial-gradient(150% 100% at 50% 120%, rgba(0, 0, 0, 0.6), transparent 60%);
  background-attachment: fixed;
}

/* ---- ziarno filmowe (subtelne, usuwa "plaski wektorowy" look) ---- */
body.theme-tactical::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/></filter><rect width='160' height='160' filter='url(%23n)' opacity='0.6'/></svg>");
}

/* ================= TYPOGRAFIA ================= */
body.theme-tactical h1,
body.theme-tactical h2,
body.theme-tactical h3 {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  font-weight: 600;
  line-height: 1.02;
  color: #fff;
}
body.theme-tactical h3 { font-size: 1.06rem; letter-spacing: 0.02em; }
body.theme-tactical .kicker,
body.theme-tactical .meta,
body.theme-tactical .news-type-badge,
body.theme-tactical .shop-wallet-label,
body.theme-tactical .bp-kicker,
body.theme-tactical .bp-timer-label {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.68rem;
  font-weight: 500;
}
body.theme-tactical .kicker { color: var(--signal); }
body.theme-tactical .meta { color: var(--muted); }

/* ================= TOPBAR: czarny druk, bez szkla ================= */
body.theme-tactical .topbar {
  position: sticky;
  background: rgba(6, 7, 10, 0.96);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: 1px solid var(--hair);
  box-shadow: 0 1px 0 rgba(255, 138, 43, 0.18);
}
body.theme-tactical .topbar-inner { padding: 0; min-height: 62px; }
body.theme-tactical .brand { gap: 12px; }
body.theme-tactical .brand img { filter: none; }
body.theme-tactical .brand span {
  /* oryginalny kroj/rozmiar MORFACE z site.css (Rajdhani 700, 1.45rem) */
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 1.45rem;
  color: #fff;
  background: none;
  -webkit-background-clip: initial;
  background-clip: initial;
}
body.theme-tactical .main-nav {
  gap: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}
body.theme-tactical .main-nav a {
  position: relative;
  padding: 0 15px;
  height: 62px;
  display: flex;
  align-items: center;
  border-radius: 0;
  background: none;
  color: #eaf0fb;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  /* [CHANGE 2026-09-20 BP-R30-NAV-FONT] ZGLOSZENIE (R30, pkt 2): „Zmniejsz
     odrobine czcionke w glownym menu strony. Problem: w niektorych przypadkach
     element Battle Pass zaczyna wyswietlac sie w dwoch liniach".
     "BATTLE PASS" jest NAJDLUZSZA pozycja paska (dwa slowa + spacja), a Rajdhani
     700 z `letter-spacing: 0.08em` jest szeroka — przy 1.08rem etykieta nie
     miescila sie w swoim slocie i lamala sie na dwie linie (nawigacja dziedziczy
     `flex-wrap: wrap` z reguly @media <=1280px, wiec lamanie bylo widoczne
     wlasnie na srednich szerokosciach okna).
     Zmniejszenie do 1rem + `white-space: nowrap` (nizej) usuwa problem
     U ZRODLA: etykieta jest krotsza ORAZ nie wolno jej lamac. */
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* ⚠️ `nowrap` jest tu kluczowe — samo zmniejszenie czcionki nie gwarantuje,
     ze dluga etykieta nie zlamie sie przy waskim slocie. Etykieta nawigacji
     ZAWSZE ma byc jedna linia; jesli zabraknie miejsca, pasek ma sie zawinac
     (`.main-nav { flex-wrap: wrap }`), a nie rozbic POJEDYNCZY link na pol. */
  white-space: nowrap;
  transition: color .18s ease;
}
body.theme-tactical .main-nav a::after {
  content: "";
  position: absolute;
  left: 18px;
  right: 100%;
  bottom: 17px;
  height: 2px;
  background: var(--signal);
  transition: right .22s cubic-bezier(.4,0,.2,1);
}
body.theme-tactical .main-nav a:hover { color: #fff; background: none; }
body.theme-tactical .main-nav a:hover::after { right: 18px; }
/* aktywna strona = kreska (serwer oznacza aria-current="page" w header.ejs) */
body.theme-tactical .main-nav a[aria-current="page"] { color: #fff; background: none; }
body.theme-tactical .main-nav a[aria-current="page"]::after { right: 18px; }
/* cała nawigacja po prawej, logo po lewej — asymetria edytorska */
body.theme-tactical .topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
}
body.theme-tactical .actions { gap: 10px; }

/* ---- pasek statusu (byly info-strip) ---- */
body.theme-tactical .info-strip {
  background: var(--ink-2);
  border-bottom: 1px solid var(--hair);
}
body.theme-tactical .info-strip-inner {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  color: var(--signal);
}
body.theme-tactical .info-strip-text::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 10px;
  background: var(--signal);
  box-shadow: 0 0 0 3px rgba(255, 138, 43, 0.16);
  animation: tb-pulse 2.4s ease-in-out infinite;
}
body.theme-tactical .info-strip-inner a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 6px 8px;
  margin: -6px -8px;
}
/* BP: micro-chipy 8-9px -> czytelne minimum (standard branzy: >=10-11px) */
body.theme-tactical .bp-task-mode-chip,
body.theme-tactical .bp-tasks-col-badge,
body.theme-tactical .bp-kicker-tag,
body.theme-tactical .bp-section-badge,
body.theme-tactical .bp-hero-chip,
body.theme-tactical .bp-globe-title-tag { min-height: 22px; }
body.theme-tactical .bp-task-mode-chip { font-size: 0.66rem !important; }
body.theme-tactical .bp-tasks-col-badge,
body.theme-tactical .bp-kicker-tag,
body.theme-tactical .bp-section-badge,
body.theme-tactical .bp-globe-title-tag { font-size: 0.68rem !important; }
body.theme-tactical .bp-hero-chip { font-size: 0.7rem !important; }
body.theme-tactical .bp-globe-command-k,
body.theme-tactical .bp-route-kicker,
body.theme-tactical .bp-route-node-brief-step,
body.theme-tactical .bp-route-task-kicker,
body.theme-tactical .bp-continent-dossier-tag,
body.theme-tactical .bp-rail-reward-label { font-size: 0.68rem !important; }
body.theme-tactical .bp-route-task-tile-k,
body.theme-tactical .bp-task-track-chip,
body.theme-tactical .bp-task-state-badge { font-size: 0.66rem !important; }
body.theme-tactical .bp-route-task-state,
body.theme-tactical .bp-route-node-brief-state,
body.theme-tactical .bp-continent-status,
body.theme-tactical .bp-access-chip,
body.theme-tactical .bp-route-legend span { font-size: 0.68rem !important; }
body.theme-tactical .bp-page small { font-size: 0.68rem; }
@keyframes tb-pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }

/* ================= PRZYCISKI: sciete naroze, bez pill ================= */
body.theme-tactical .btn,
body.theme-tactical .btn-red {
  border-radius: 0;
  border: 1px solid var(--signal);
  background: transparent;
  color: var(--signal);
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 11px 20px;
  box-shadow: none;
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
  transition: background .16s ease, color .16s ease;
}
body.theme-tactical .btn:hover,
body.theme-tactical .btn-red:hover {
  transform: none;
  filter: none;
  background: var(--signal);
  color: #06070a;
  box-shadow: none;
}
body.theme-tactical .btn.ghost,
body.theme-tactical .btn.btn-ghost {
  border-color: var(--hair-2);
  background: transparent;
  color: var(--text);
}
body.theme-tactical .btn.ghost:hover,
body.theme-tactical .btn.btn-ghost:hover {
  background: #fff;
  border-color: #fff;
  color: #06070a;
}
body.theme-tactical .discord-widget-join {
  display: inline-block;
  background: var(--signal);
  color: #06070a !important;
  border-radius: 0;
  font-family: 'Oswald', sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 600;
  padding: 10px 18px;
}

/* ================= HERO: pelna szerokosc, editorial lewy ================= */
body.theme-tactical .hero-stage { padding: 0 0 34px; }
body.theme-tactical .hero-banner-frame {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  /* [CHANGE 2026-09-18 HERO] Bylo `min-height: 420px` — przy krotkim obrazku
     (np. na malym ekranie) zostawialo czarny pas pod grafika. Teraz wysokosc
     ramki wynika z obrazka. */
  min-height: 0;
  background: #000;
}
body.theme-tactical .hero-banner-frame::before,
body.theme-tactical .hero-banner-frame::after { display: none; }
body.theme-tactical .hero-banner-image {
  /* [CHANGE 2026-09-18 HERO] INTELIGENTNE KADROWANIE.
     Bylo: stala wysokosc `clamp(360px, 46vw, 640px)` + `object-position: center 42%`.
     Przy 1920 px dawalo to pudelko 3.00:1 dla obrazka 1.98:1, wiec `object-fit:
     cover` obcinal 34% WYSOKOSCI — i to niesymetrycznie (42% w dol).

     Zmierzone obciecia PRZED (obrazek 1763x892 = 1.98:1):
       1920 px -> 34.1%   1600 px -> 20.9%   1440 px -> 12.2%   1366 px -> 9.1%

     Teraz proporcje pudelka pochodza z PRAWDZIWEGO pliku (`--hero-ar` ustawiane
     inline w widoku przez `imageMeta`), wiec obrazek miesci sie bez ciecia:
       1920 px -> 0.0%    1440 px -> 0.1%     1280 px -> 0.1%

     `max-height` = bezpiecznik na niskie ekrany: gdyby pudelko wyszlo wyzsze niz
     widok, wysokosc jest ograniczana, a `object-position: center` obcina wtedy
     SYMETRYCZNIE (tyle samo z gory i z dolu) — zamiast stronniczego 42%.
     Podmiana grafiki dziala bez zmian w kodzie: nowy plik = nowe `--hero-ar`. */
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--hero-ar, 1.9);
  max-height: min(78vh, 760px);
  object-fit: cover;
  object-position: center center;
}
body.theme-tactical .hero-banner-overlay {
  background: linear-gradient(90deg, rgba(6,7,10,.94) 0%, rgba(6,7,10,.55) 45%, rgba(6,7,10,.2) 100%);
  align-items: center;
  justify-content: flex-start;
  padding: clamp(24px, 4vw, 64px) clamp(20px, 5vw, 72px);
}
body.theme-tactical .hero-stage h1 {
  font-family: 'Oswald', sans-serif;
  font-size: clamp(2.5rem, 6.4vw, 5.1rem);
  font-weight: 700;
  line-height: 0.92;
  letter-spacing: -0.005em;
  margin: 14px 0 0;
  max-width: 15ch;
}
body.theme-tactical .hero-stage h1::after { display: none; }

/* ---- zakładki: sciete, monospace, aktywna = lita amber ---- */
body.theme-tactical .hero-tabs {
  display: flex;
  gap: 0;
  margin-top: 0;
  border: 0;
  border-radius: 0;
  background: none;
  border-top: 1px solid var(--hair);
  overflow: visible;
}
body.theme-tactical .hero-tabs a {
  flex: 0 0 auto;
  min-height: 56px;
  padding: 0 26px;
  border-right: 1px solid var(--hair);
  background: var(--ink-2);
  color: #dbe4f2;
  font-family: 'Rajdhani', sans-serif;
  font-size: 1.12rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
body.theme-tactical .hero-tabs a:first-child { border-left: 1px solid var(--hair); }
body.theme-tactical .hero-tabs a:hover { color: #fff; background: var(--panel); }
body.theme-tactical .hero-tabs a.active {
  background: var(--signal);
  color: #06070a;
  box-shadow: none;
}
body.theme-tactical .hero-tabs a.active::after { display: none; }

/* ================= HEADLINE: duzy + maly, hairline ================= */
body.theme-tactical .headline-grid {
  margin-top: 0;
  gap: 1px;
  grid-template-columns: 1fr 360px;
  background: var(--hair);
  border: 1px solid var(--hair);
}
body.theme-tactical .headline-main,
body.theme-tactical .headline-side {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: var(--ink);
  transition: none;
}
body.theme-tactical .headline-main:hover,
body.theme-tactical .headline-side:hover { transform: none; box-shadow: none; }
body.theme-tactical .headline-image,
body.theme-tactical .headline-side-image {
  filter: contrast(1.04) saturate(1.04);
  transition: transform .8s cubic-bezier(.2,0,.2,1);
}
body.theme-tactical .headline-main:hover .headline-image,
body.theme-tactical .headline-side:hover .headline-side-image {
  transform: scale(1.03);
}
body.theme-tactical .headline-caption,
body.theme-tactical .headline-side-caption {
  position: absolute;
  left: auto;
  right: 0;
  bottom: 0;
  max-width: 100%;
  background: rgba(6, 7, 10, 0.92);
  backdrop-filter: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  color: #fff;
  padding: 14px 20px 14px 16px;
  border-left: 3px solid var(--signal);
}
body.theme-tactical .headline-caption::before,
body.theme-tactical .headline-side-caption::before { display: none; }
body.theme-tactical .headline-caption {
  font-family: 'Oswald', sans-serif;
  font-size: clamp(1.05rem, 2.1vw, 1.5rem);
  font-weight: 600;
  letter-spacing: 0.01em;
}
/* ---- "NOW IN GAME" — dopracowany blok: etykieta z linia + mocny tytul ---- */
body.theme-tactical .headline-side-caption {
  left: 0;
  right: auto;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 9px;
  background: linear-gradient(0deg, rgba(6,7,10,.96) 0%, rgba(6,7,10,.72) 60%, rgba(6,7,10,0) 100%);
  backdrop-filter: none;
  border: 0;
  border-left: 0;
  padding: 40px 22px 20px;
}
body.theme-tactical .headline-side-caption small {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: var(--signal);
  color: #06070a;
  border: 0;
  border-radius: 0;
  padding: 6px 16px 6px 11px;
  font-family: 'Rajdhani', sans-serif;
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  clip-path: polygon(0 0, 100% 0, calc(100% - 11px) 100%, 0 100%);
  box-shadow: 0 6px 18px -8px rgba(255, 138, 43, 0.55);
}
body.theme-tactical .headline-side-caption small::before {
  content: "";
  width: 7px;
  height: 7px;
  background: #06070a;
  transform: rotate(45deg);
  flex: 0 0 auto;
}
body.theme-tactical .headline-side-caption strong {
  color: #fff;
  font-family: 'Rajdhani', sans-serif;
  font-size: 1.5rem;
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  max-width: 22ch;
}

/* ================= SEKCJE: numeracja + linia (edytorski) ================= */
body.theme-tactical .section { padding: 44px 0; }
body.theme-tactical .section-head {
  display: flex;
  align-items: baseline;
  gap: 16px;
  margin-bottom: 22px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--hair);
}
body.theme-tactical .section-head h2 {
  position: relative;
  font-size: 1.5rem;
  font-weight: 600;
}
body.theme-tactical .section-head a {
  margin-left: auto;
  color: var(--muted);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
body.theme-tactical .section-head a::after { content: " →"; color: var(--signal); }
body.theme-tactical .section-head a:hover { color: #fff; }

/* ================= KARTY: dossier, hairline, klamry HUD ================= */
body.theme-tactical .card,
body.theme-tactical .news-card {
  --pad: 15px;
  position: relative;
  background: var(--panel);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--hair);
  border-radius: 0;
  box-shadow: none;
  padding: var(--pad);
  transition: border-color .2s ease, background .2s ease;
}
/* narożne klamry (pojawiaja sie na hover) */
body.theme-tactical .card::before,
body.theme-tactical .card::after,
body.theme-tactical .news-card::before,
body.theme-tactical .news-card::after {
  content: "";
  position: absolute;
  width: 12px;
  height: 12px;
  opacity: 0;
  transition: opacity .2s ease;
  pointer-events: none;
}
body.theme-tactical .card::before,
body.theme-tactical .news-card::before {
  top: -1px; left: -1px;
  border-top: 2px solid var(--signal);
  border-left: 2px solid var(--signal);
}
body.theme-tactical .card::after,
body.theme-tactical .news-card::after {
  bottom: -1px; right: -1px;
  border-bottom: 2px solid var(--signal);
  border-right: 2px solid var(--signal);
}
/* support-card ma wlasne tlo bez ramki -> klamra lewy-gorny musi dotykac gory kafelka */
body.theme-tactical .support-card::before {
  top: 0;
  left: 0;
}
body.theme-tactical .card:hover,
body.theme-tactical .news-card:hover {
  transform: none;
  background: var(--panel-2);
  border-color: var(--hair-2);
  box-shadow: none;
}
body.theme-tactical .card:hover::before,
body.theme-tactical .card:hover::after,
body.theme-tactical .news-card:hover::before,
body.theme-tactical .news-card:hover::after { opacity: 1; }
body.theme-tactical .card-media {
  margin: calc(var(--pad) * -1) calc(var(--pad) * -1) var(--pad);
  border-radius: 0;
  background: #000;
  border-bottom: 1px solid var(--hair);
}
body.theme-tactical .card-media img {
  filter: contrast(1.03) saturate(1.05);
  transition: transform .9s cubic-bezier(.2,0,.2,1);
}
body.theme-tactical .news-card:hover .card-media img {
  transform: scale(1.04);
}
body.theme-tactical .card h3 {
  margin: 4px 0 8px;
  font-weight: 600;
  color: #fff;
}
body.theme-tactical .card p:not(.shop-wallet-value):not(.price) { color: var(--muted); }
body.theme-tactical .card a {
  color: var(--signal);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
}
body.theme-tactical .card a:hover { color: #fff; }
body.theme-tactical .news-type-badge {
  background: transparent;
  color: var(--signal);
  border: 1px solid rgba(255, 138, 43, 0.4);
  border-radius: 0;
  padding: 2px 7px;
}

/* ================= SIDEBAR: panele z glowem gornej krawedzi ================= */
body.theme-tactical .latest-sidebar-feature,
body.theme-tactical .latest-sidebar-panel {
  background: var(--panel);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--hair);
  border-radius: 0;
  box-shadow: none;
  overflow: hidden;
}
body.theme-tactical .latest-sidebar-feature { position: relative; }
body.theme-tactical .latest-sidebar-feature::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 40%, rgba(6,7,10,.9) 100%);
  pointer-events: none;
}
body.theme-tactical .latest-sidebar-feature img {
  filter: contrast(1.04) saturate(1.04);
}
body.theme-tactical .latest-sidebar-caption { z-index: 2; }
body.theme-tactical .latest-sidebar-caption strong {
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  letter-spacing: 0.02em;
}
body.theme-tactical .latest-sidebar-panel { padding: 18px; }
body.theme-tactical .latest-sidebar-panel h3 {
  margin-top: 0;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--hair);
  font-size: 0.95rem;
  letter-spacing: 0.08em;
}
body.theme-tactical .latest-sidebar-panel a { color: #e7eaf0; font-size: 1.03rem; }
body.theme-tactical .latest-sidebar-panel a:hover { color: var(--signal); }
body.theme-tactical .latest-sidebar-panel li + li { border-top: 1px dashed rgba(34,40,52,.9); }
/* bialy chip z site.css -> ciemny z sygnatura amber (inwersja "AI looku") */
body.theme-tactical .latest-sidebar-caption strong {
  background: rgba(6, 7, 10, 0.9);
  color: #fff;
  border-left: 3px solid var(--signal);
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 8px 12px;
  font-size: 1.02rem;
}
body.theme-tactical .latest-sidebar-caption .news-type-badge { color: var(--signal); }
/* --- "NOW IN GAME" — profesjonalny gaming badge: lita amber plytka,
     scety skos, ciemny tytul, diament-wskaznik (styl Riot/WF key-art) --- */
body.theme-tactical .latest-sidebar-caption .news-type-badge {
  width: fit-content;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: var(--signal);
  color: #06070a;
  border: 0;
  border-radius: 0;
  padding: 6px 16px 6px 11px;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 0.86rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  clip-path: polygon(0 0, 100% 0, calc(100% - 11px) 100%, 0 100%);
  box-shadow: 0 6px 18px -8px rgba(255, 138, 43, 0.55);
}
body.theme-tactical .latest-sidebar-caption .news-type-badge::before {
  content: "";
  width: 7px;
  height: 7px;
  background: #06070a;
  transform: rotate(45deg);
  flex: 0 0 auto;
}
body.theme-tactical .latest-sidebar-feature img {
  filter: contrast(1.04) saturate(1.04);
}
body.theme-tactical .discord-widget-panel { padding: 18px; }
body.theme-tactical .discord-widget-hint {
  color: var(--muted);
  font-family: 'Rajdhani', sans-serif;
  font-size: 1.03rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-align: center;
  margin-top: 14px;
}

/* ================= SUPPORT: zamiast emoji — panele z indeksem ================= */
body.theme-tactical .support-section {
  border-top: 1px solid var(--hair);
  background: linear-gradient(180deg, var(--ink-2), transparent);
  padding: 56px clamp(20px, 4vw, 56px) 64px;
}
body.theme-tactical .support-section h2 {
  font-size: 1.7rem;
  margin: 0 0 6px;
}
body.theme-tactical .support-section .section-head { margin-bottom: 6px; }
body.theme-tactical .support-subtitle { color: var(--muted); }
body.theme-tactical .support-cards { gap: 1px; background: var(--hair); border: 1px solid var(--hair); }
body.theme-tactical .support-card {
  position: relative;
  background: var(--panel);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 26px 22px;
  transition: background .2s ease;
}
body.theme-tactical .support-card:hover { transform: none; background: var(--panel-2); border-color: transparent; }
/* support-card dziedziczy czyste klamry narożne z .card (top-left dotyka gory).
   Wczesniej nadpisanie ::before licznikiem "01" przesuwalo rogal w dol — usuniete. */
/* emoji to "AI tell" — chowamy glif, w jego miejsce krotka etykieta mono */
body.theme-tactical .support-card-icon {
  width: auto;
  height: auto;
  font-size: 0;              /* ukrywa emoji */
  margin-bottom: 16px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  filter: none;
  opacity: 1;
}
body.theme-tactical .support-card-icon::before {
  content: "//";
  font-family: 'IBM Plex Mono', monospace;
  font-size: 1.1rem;
  letter-spacing: 0.1em;
  color: var(--signal);
}
body.theme-tactical .support-card h3 { font-size: 1.05rem; letter-spacing: 0.04em; }
body.theme-tactical .support-card p { color: var(--muted); }

/* ================= FOOTER: techniczny ================= */
body.theme-tactical .site-footer {
  margin-top: 0;
  padding: 26px 0 30px;
  border-top: 1px solid var(--hair);
  background: var(--ink-2);
}
body.theme-tactical .footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}
body.theme-tactical .footer-links { display: flex; gap: 22px; }
body.theme-tactical .footer-links a {
  color: var(--muted);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
body.theme-tactical .footer-links a:hover { color: var(--signal); }
/* tap-target >= ~32px dla linkow tekstowych (WCAG 2.5.8 / Apple HIG)
   — padding TYLKO w pionie, zero przelewa w poziomie */
body.theme-tactical .footer-links a,
body.theme-tactical .section-head a,
body.theme-tactical .info-strip-inner a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 9px 0;
  margin: -9px 0;
}
body.theme-tactical .footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 1.45rem;
  text-transform: uppercase;
  color: #fff;
}
body.theme-tactical .footer-brand img { height: 50px; width: 50px; filter: none; }

/* ================= FORMULARZE / TABELE ================= */
body.theme-tactical input[type="text"],
body.theme-tactical input[type="password"],
body.theme-tactical select,
body.theme-tactical .admin-field input {
  background: #000;
  border: 1px solid var(--hair-2);
  border-radius: 0;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.82rem;
}
body.theme-tactical input:focus,
body.theme-tactical select:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: inset 0 0 0 1px rgba(255,138,43,.25);
}
body.theme-tactical table th {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--ink-2);
}
body.theme-tactical table td { border-color: var(--hair); }
body.theme-tactical .table-wrap { border: 1px solid var(--hair); }
body.theme-tactical .price { color: var(--signal); font-family: 'IBM Plex Mono', monospace; }

/* ================= LEADERBOARD / TABY ================= */
body.theme-tactical .leaderboard-tabs,
body.theme-tactical .shop-tabs {
  gap: 0;
  border-bottom: 1px solid var(--hair);
}
body.theme-tactical .leaderboard-tab,
body.theme-tactical .shop-tab-button {
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--muted);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 10px 16px;
  position: relative;
}
body.theme-tactical .leaderboard-tab.active,
body.theme-tactical .shop-tab-button.is-active {
  background: none;
  color: #fff;
  box-shadow: none;
}
body.theme-tactical .leaderboard-tab.active::after,
body.theme-tactical .shop-tab-button.is-active::after {
  content: "";
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: -1px;
  height: 2px;
  background: var(--signal);
}

/* ================= BATTLE PASS: czarny, sygnalny amber ================= */
body.theme-tactical .bp-page { background: transparent; }
body.theme-tactical .bp-hero {
  border: 1px solid var(--hair);
  border-radius: 0;
  background:
    radial-gradient(90% 120% at 100% 0%, rgba(255,64,129,.12), transparent 60%),
    linear-gradient(180deg, #140a1a, #08060c);
  box-shadow: none;
}
/* [CHANGE 2026-09-15 BP-PURPLE] Hero: granat -> purpur (motyw operacji). Nakladka
   nad zdjeciem (atlas.jpg) byla ciemnogranatowa -> teraz ciemnopurpurowa. */
body.theme-tactical .bp-hero-inner::before {
  background:
    linear-gradient(110deg, rgba(18, 6, 24, 0.92) 12%, rgba(38, 12, 46, 0.5) 52%, rgba(20, 8, 26, 0.88) 100%),
    radial-gradient(120% 120% at 84% 8%, rgba(255, 64, 129, 0.22) 0%, rgba(255, 64, 129, 0.05) 34%, rgba(18, 6, 24, 0.78) 76%);
}
body.theme-tactical .bp-hero-media img {
  filter: saturate(1.05) contrast(1.06) sepia(0.28) hue-rotate(288deg);
}
body.theme-tactical .bp-hero-text {
  background: rgba(18, 8, 24, 0.5);
  border-color: rgba(255, 64, 129, 0.12);
}
body.theme-tactical .bp-hero-text h1 {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: clamp(2rem, 5vw, 3.6rem);
  line-height: .95;
}
body.theme-tactical .bp-subtitle,
body.theme-tactical .bp-hero-tagline { color: var(--muted); }
body.theme-tactical .bp-hero-ring,
body.theme-tactical .bp-hero-noise { display: none; }
body.theme-tactical .bp-hero-chip,
body.theme-tactical .bp-timer-value,
body.theme-tactical .bp-stat-value,
body.theme-tactical .bp-task-xp,
body.theme-tactical .bp-command-v {
  font-family: 'IBM Plex Mono', monospace;
  color: var(--signal);
}
body.theme-tactical .bp-stat-card,
body.theme-tactical .bp-task-card,
body.theme-tactical .bp-rewards-table-wrap,
body.theme-tactical .bp-currency-panel,
body.theme-tactical .bp-route-panel,
body.theme-tactical .bp-globe-section,
body.theme-tactical .bp-quest-panel {
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 0;
  box-shadow: none;
}
body.theme-tactical .bp-section-title {
  font-family: 'Oswald', sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 1.05rem;
  color: #fff;
  padding-left: 12px;
  border-left: 3px solid var(--signal);
}
body.theme-tactical .bp-level-bar {
  background: #000;
  border: 1px solid var(--hair-2);
  border-radius: 0;
}
body.theme-tactical .bp-level-bar-fill {
  background: linear-gradient(90deg, var(--signal-dim), var(--signal));
  border-radius: 0;
}
body.theme-tactical .bp-rail-dot {
  border-radius: 0;
  transform: rotate(45deg);
  background: #000;
  border: 1px solid var(--hair-2);
}
body.theme-tactical .bp-rail-lvl { font-family: 'IBM Plex Mono', monospace; }
body.theme-tactical .bp-rail-node.reached .bp-rail-dot {
  background: var(--signal);
  border-color: var(--signal);
  box-shadow: 0 0 0 3px rgba(255,138,43,.18);
}
body.theme-tactical .bp-rail-track { background: var(--hair); }
body.theme-tactical .bp-task-card { border-radius: 0; }
body.theme-tactical .bp-task-card:hover { border-color: var(--hair-2); transform: none; }
body.theme-tactical .bp-task-name {
  font-family: 'Oswald', sans-serif;
  font-weight: 500;
  letter-spacing: .02em;
  text-transform: none;
}
body.theme-tactical .bp-task-progress-bar { background: #000; border: 1px solid var(--hair); border-radius: 0; }
body.theme-tactical .bp-task-progress-fill { background: var(--signal); border-radius: 0; }
body.theme-tactical .btn-gold {
  background: var(--signal);
  color: #06070a;
  border: 1px solid var(--signal);
  border-radius: 0;
  font-family: 'Oswald', sans-serif;
  text-transform: uppercase;
  letter-spacing: .12em;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
}
body.theme-tactical .bp-badge-claimed,
body.theme-tactical .bp-badge-locked,
body.theme-tactical .bp-task-claimed-badge { border-radius: 0; font-family: 'IBM Plex Mono', monospace; letter-spacing: .1em; }
body.theme-tactical .bp-loader-aura,
body.theme-tactical .bp-loader-orbit { display: none; }
body.theme-tactical .bp-loader-shell { background: var(--panel); border: 1px solid var(--hair); border-radius: 0; }
body.theme-tactical .bp-loader-ring { border-color: rgba(255,138,43,.35) transparent; }

/* ================= BATTLE PASS: cyan/zielen -> amber (pelna spojnosc) =====
   site.css (region 5440+) definiuje tokeny --bp-cyan-bright/--bp-green-ok i
   gradienty #22d7ff->#58e7a0. Nadpisujemy JEZYK KOLOROW, nie layout. ===== */
body.theme-tactical .bp-page {
  /* REDEFINICJA TOKENOW = naprawia WSZYSTKIE var(--accent)/var(--accent-2) w BP
     jednym ciem (site.css uzyla ich ~10x: gradienty paskow, rail-dot, task-xp,
     section-title, badge). To "ze sztuka": nie nadpisujemy 40 selektorow, tylko
     zrodlo. Scope .bp-page = reszta portalu nietknieta. */
  --accent: var(--signal);
  --accent-2: #ffb066;
  --bp-cyan-bright: var(--signal);
  --bp-green-ok: #ffb066;
  --bp-line-soft: rgba(255, 138, 43, 0.16);
  --bp-line-strong: rgba(255, 138, 43, 0.3);
  --bp-glow-cyan: rgba(255, 138, 43, 0.24);
  background:
    radial-gradient(1000px 460px at 85% -6%, rgba(255, 180, 70, 0.16) 0%, rgba(255, 180, 70, 0) 70%),
    radial-gradient(900px 500px at 8% 6%, rgba(255, 138, 43, 0.08) 0%, rgba(255, 138, 43, 0) 72%),
    linear-gradient(148deg, #07080c 0%, #0a0c12 54%, #0d0b08 100%);
}
body.theme-tactical .bp-level-bar-fill {
  background: linear-gradient(90deg, var(--signal-dim) 0%, var(--signal) 60%, #ffc27a 100%);
  box-shadow: 0 0 16px rgba(255, 138, 43, 0.35);
}
body.theme-tactical .bp-task-progress-fill {
  background: linear-gradient(90deg, var(--signal-dim), var(--signal));
}
body.theme-tactical .bp-command-bar-fill {
  background: linear-gradient(90deg, var(--signal-dim), var(--signal));
  box-shadow: 0 0 12px rgba(255, 138, 43, 0.4);
}
body.theme-tactical .bp-rail-node.reached .bp-rail-dot {
  background: var(--signal);
  border-color: var(--signal);
  box-shadow: 0 0 8px rgba(255, 138, 43, 0.55);
}
body.theme-tactical .bp-rail-node.claimed .bp-rail-dot {
  background: var(--signal-dim);
  border-color: var(--signal-dim);
}
body.theme-tactical .bp-scanline-bar {
  background: rgba(6, 8, 12, 0.85);
  border-top-color: rgba(255, 138, 43, 0.22);
  border-bottom-color: rgba(255, 138, 43, 0.22);
}
body.theme-tactical .bp-scanline-chip { color: var(--signal); }
body.theme-tactical .bp-scanline-sep { background: rgba(255, 138, 43, 0.25); }
body.theme-tactical .bp-task-card.bp-task-state-active { border-left-color: var(--signal); }
body.theme-tactical .bp-task-card.bp-task-state-claimable { border-left-color: #ffb066; }
body.theme-tactical .bp-state-active {
  color: var(--signal);
  border-color: rgba(255, 138, 43, 0.35);
  background: rgba(255, 138, 43, 0.08);
}
body.theme-tactical .bp-state-claimable {
  color: #ffb066;
  border-color: rgba(255, 176, 102, 0.45);
  background: rgba(255, 176, 102, 0.1);
}
body.theme-tactical .bp-task-mode-chip {
  color: var(--signal);
  border-color: rgba(255, 138, 43, 0.35);
  background: rgba(255, 138, 43, 0.07);
}
body.theme-tactical .bp-task-card[data-task-code*="pve"] .bp-task-mode-chip {
  color: #ffb066;
  border-color: rgba(255, 176, 102, 0.35);
  background: rgba(255, 176, 102, 0.07);
}
body.theme-tactical .bp-cmd-hq-v.full { color: #ffb066; }
body.theme-tactical .bp-cmd-claimable .bp-command-label { color: #ffb066 !important; }
body.theme-tactical .bp-cmd-claimable .bp-command-value { color: #ffb066; }
body.theme-tactical .bp-row-reached td { background: rgba(255, 138, 43, 0.05); }
/* GLOBUS: zostawiony w NIEZMIENIONEJ formie (canvas, markery, poswieta) —
   usuniete wczesniejsze hue-rotate. Tylko border tooltipa UI -> amber. */
body.theme-tactical .bp-globe-tooltip { border-color: rgba(255, 138, 43, 0.45); }
body.theme-tactical .bp-command-strip { border-color: rgba(255, 138, 43, 0.2); }
body.theme-tactical .bp-task-inspector { border-color: rgba(255, 138, 43, 0.24); }
body.theme-tactical .bp-subtitle { color: #ffe3cc; }
body.theme-tactical .bp-hero-tagline,
body.theme-tactical .bp-level-next-reward { color: var(--muted); }
body.theme-tactical .bp-loader-ring { border-color: rgba(255, 138, 43, 0.35) transparent; }
/* pasek tickera (OPERATION STATUS / OBJECTIVES LOADED): nie na 100% ekranu,
   dokladnie jak pozostale sekcje BP (shell-wide = min(1800px, 98vw)) */
body.theme-tactical .bp-scanline-bar {
  width: min(1800px, 98vw);
  max-width: 100%;
  margin: 0 auto;
}
/* reszta cyanow BP -> amber (tokeny nie objete nadpisanie --bp-cyan-bright w
   specyficznych gradientach) */
body.theme-tactical .bp-hero-ring { border-color: rgba(255, 138, 43, 0.18); }
body.theme-tactical .bp-hero-ring-a { box-shadow: 0 0 90px rgba(255, 138, 43, 0.2), inset 0 0 46px rgba(255, 138, 43, 0.12); }
body.theme-tactical .bp-hero-chip { border-color: rgba(255, 138, 43, 0.3); }
body.theme-tactical .bp-rail-track::before { background: linear-gradient(90deg, rgba(255, 138, 43, 0.5), rgba(255, 194, 122, 0.44)); }
body.theme-tactical .bp-task-card { border-color: rgba(255, 138, 43, 0.14); }
body.theme-tactical .bp-task-card:hover { border-color: rgba(255, 138, 43, 0.4); }
body.theme-tactical .bp-loader {
  background:
    radial-gradient(620px 280px at 70% 22%, rgba(255, 175, 65, 0.16) 0%, rgba(255, 175, 65, 0) 70%),
    radial-gradient(560px 260px at 26% 72%, rgba(255, 138, 43, 0.14) 0%, rgba(255, 138, 43, 0) 72%),
    #06070a;
}
body.theme-tactical .bp-loader-grid {
  background-image:
    linear-gradient(rgba(255, 138, 43, 0.12) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 138, 43, 0.12) 1px, transparent 1px);
}
body.theme-tactical .bp-loader-aura-a { background: rgba(255, 138, 43, 0.26); }
body.theme-tactical .bp-loader-shell { border-color: rgba(255, 138, 43, 0.3); }
body.theme-tactical .bp-loader-media { border-color: rgba(255, 138, 43, 0.32); background: radial-gradient(circle at 30% 20%, rgba(255, 168, 90, 0.22), rgba(8, 10, 14, 0.94)); }
body.theme-tactical .bp-loader-ring { border-top-color: rgba(255, 138, 43, 0.8); border-bottom-color: rgba(255, 138, 43, 0.15); }
body.theme-tactical .bp-loader-progress { border-color: rgba(255, 138, 43, 0.3); }
body.theme-tactical .bp-loader-progress-fill { background: linear-gradient(90deg, rgba(255, 138, 43, 0.95), rgba(255, 200, 130, 0.95)); box-shadow: 0 0 18px rgba(255, 138, 43, 0.42); }
body.theme-tactical .bp-command-item { border-color: rgba(255, 138, 43, 0.18); }
body.theme-tactical .bp-command-bar { border-color: rgba(255, 138, 43, 0.22); }
body.theme-tactical .bp-quest-progress-bar { background: rgba(255, 138, 43, 0.18); border-color: rgba(255, 138, 43, 0.28); }
body.theme-tactical .bp-quest-state-badge.bp-quest-state-available { background: rgba(255, 138, 43, 0.22); border-color: rgba(255, 138, 43, 0.35); }
body.theme-tactical .bp-quest-state-badge.bp-quest-state-claimable { background: rgba(255, 176, 102, 0.24); border-color: rgba(255, 176, 102, 0.45); }
body.theme-tactical .bp-quest-reward-item { border-color: rgba(255, 138, 43, 0.16); }
body.theme-tactical .bp-quest-reward-item:hover { border-color: rgba(255, 138, 43, 0.4); }
body.theme-tactical .btn-gold {
  background: linear-gradient(90deg, var(--signal-dim), var(--signal));
  border-color: var(--signal);
}

/* ================= LOGOWANIE ADMINA / 2FA (inline style w EJS) =============
   Nadpisanie przez :has() jest kruche — zamiast tego body.auth-gate klasa
   dokladana w szablonie. Kolory amber, hairline, sciete naroze. */
body.auth-gate {
  /* tlo: artwork mfloading.png (1920x1080) + gesty scrim dla czytelnosci
     formularza (panel loginu jest nieprzezroczysty, ale kontrast tla musi
     byc niski wobec bursztynowych akcentow) */
  background:
    radial-gradient(90% 60% at 50% -10%, rgba(255, 138, 43, 0.06), transparent 60%),
    linear-gradient(180deg, rgba(6, 7, 10, 0.82) 0%, rgba(6, 7, 10, 0.9) 100%),
    url('/img/mfloading.png') center / cover no-repeat fixed #06070a;
}
body.auth-gate .login-container { background: transparent; }
/* admin-2fa-setup.ejs uzywa innej struktury (.wrap/.card) z inline-gradients */
body.auth-gate .wrap { background: transparent; }
body.auth-gate .card {
  background: #0d1016;
  border: 1px solid #222834;
  border-radius: 0;
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.9);
}
body.auth-gate .login-box,
body.auth-gate .login-card {
  background: var(--panel, #0d1016);
  border: 1px solid #222834;
  border-radius: 0;
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.9);
}
body.auth-gate .login-box h1,
body.auth-gate .login-card h1 {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #fff;
}
body.auth-gate .login-box h1::after,
body.auth-gate .login-card h1::after {
  content: "";
  display: block;
  width: 46px;
  height: 2px;
  margin: 12px auto 0;
  background: #ff8a2b;
}
body.auth-gate input {
  background: #000;
  border: 1px solid #2f3746;
  border-radius: 0;
  color: #e7eaf0;
  font-family: 'IBM Plex Mono', monospace;
}
body.auth-gate input:focus {
  outline: none;
  border-color: #ff8a2b;
  box-shadow: inset 0 0 0 1px rgba(255, 138, 43, 0.25);
}
body.auth-gate button[type="submit"],
body.auth-gate .btn-primary,
body.auth-gate button {
  background: #ff8a2b;
  color: #06070a;
  border: 0;
  border-radius: 0;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
  box-shadow: none;
}
body.auth-gate button:hover,
body.auth-gate button[type="submit"]:hover { box-shadow: 0 10px 26px -12px rgba(255, 138, 43, 0.6); }
body.auth-gate button.danger { background: #e0312f; color: #fff; }
body.auth-gate a { color: #ff8a2b; }
body.auth-gate .error-message {
  color: #ffb4b4;
  background: rgba(224, 49, 47, 0.1);
  border: 1px solid rgba(224, 49, 47, 0.35);
  border-radius: 0;
}

/* ================= 404 / STRONA BLEDU ================= */
body.error-page {
  background:
    radial-gradient(90% 60% at 50% -10%, rgba(255, 138, 43, 0.05), transparent 60%),
    #06070a;
}
body.error-page .grid-overlay {
  background-image:
    linear-gradient(rgba(255, 138, 43, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 138, 43, 0.035) 1px, transparent 1px);
}
body.error-page .card {
  background: #0d1016;
  border: 1px solid #222834;
  border-radius: 0;
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.9);
  clip-path: polygon(18px 0, 100% 0, 100% calc(100% - 18px), calc(100% - 18px) 100%, 0 100%, 0 18px);
}
body.error-page .logo { font-family: 'Rajdhani', sans-serif; font-weight: 700; letter-spacing: 0.08em; color: #fff; }
body.error-page .code {
  background: linear-gradient(135deg, #ffffff 0%, #ff8a2b 70%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  letter-spacing: 0.04em;
}
body.error-page .headline { font-family: 'Rajdhani', sans-serif; text-transform: uppercase; letter-spacing: 0.06em; }
body.error-page .divider { background: linear-gradient(90deg, transparent, #ff8a2b, transparent); }
body.error-page .sub .path { color: #ff8a2b; }
body.error-page .ring .track { stroke: rgba(255, 138, 43, 0.16); }
body.error-page .ring .bar { stroke: #ff8a2b; }
body.error-page .count-text .l2 a { color: #ff8a2b; }
body.error-page .btn {
  border: 1px solid #ff8a2b;
  color: #ff8a2b;
  background: transparent;
  border-radius: 0;
  font-family: 'Rajdhani', sans-serif;
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
}
body.error-page .btn:hover { background: #ff8a2b; color: #06070a; box-shadow: 0 10px 26px -12px rgba(255, 138, 43, 0.6); }

/* ================= SHOP / PROMO ================= */
body.theme-tactical .shop-package-card,
body.theme-tactical .shop-pack-card,
body.theme-tactical .shop-milestone-card,
body.theme-tactical .shop-checkout-panel,
body.theme-tactical .shop-wallet-card,
body.theme-tactical .promo-redeem-card,
body.theme-tactical .promo-history-card {
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 0;
  box-shadow: none;
}
body.theme-tactical .shop-package-card:hover,
body.theme-tactical .shop-pack-card:hover { transform: none; border-color: var(--hair-2); }
body.theme-tactical .shop-package-card.is-selected {
  border-color: var(--signal);
  box-shadow: inset 0 0 0 1px rgba(255,138,43,.35);
}
body.theme-tactical .shop-package-amount,
body.theme-tactical .shop-wallet-value {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  color: #fff;
}
body.theme-tactical .shop-tab-button { border-radius: 0; }
body.theme-tactical .promo-code-input {
  background: #000;
  border: 1px solid var(--hair-2);
  border-radius: 0;
  font-family: 'IBM Plex Mono', monospace;
  letter-spacing: .1em;
}
body.theme-tactical .promo-modal,
body.theme-tactical .guild-join-modal {
  background: var(--panel-2);
  border: 1px solid var(--hair-2);
  border-radius: 0;
}
body.theme-tactical .promo-modal-backdrop,
body.theme-tactical .guild-join-modal-backdrop { background: rgba(3, 4, 6, 0.9); }

/* ================= FOOTER WIDEO (footer.mp4) =================
   Tlo wideo pod trescia footera; scrim dla czytelnosci linkow;
   linki/brand zachowane. Wideo ladowane lazy (skrypt w partialu). */
body.theme-tactical .site-footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ink-2);
}
body.theme-tactical .site-footer .footer-inner { min-height: 150px; }
body.theme-tactical .footer-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
  opacity: 1;
  /* klip jest bardzo ciemny (srednia ~50/255) — rozjaznienie CSS, bez reencode */
  filter: brightness(2.35) saturate(1.2) contrast(1.05);
  pointer-events: none;
}
/* scrim + winieta:-warstwa miedzy wideo a tekstem (nie przyciemnia linkow) */
body.theme-tactical .site-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(115% 125% at 50% 50%, rgba(6, 7, 10, 0.35) 0%, rgba(6, 7, 10, 0.45) 38%, rgba(6, 7, 10, 0.78) 72%, rgba(6, 7, 10, 0.96) 100%),
    linear-gradient(180deg, var(--ink-2) 0%, rgba(6, 7, 10, 0.55) 38%, rgba(6, 7, 10, 0.72) 100%);
}
body.theme-tactical .site-footer .footer-inner { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) {
  body.theme-tactical .footer-video { display: none; }
}

/* ================= LAUNCHER SHELL (WebView) ================= */
body.theme-tactical.launcher-shell-body { background: var(--ink); }
/* paski serwisowe z inline-gradientem (niebieski/zloty) -> wlosowlosy tactical */
body.theme-tactical #launcherMaintenanceBarUpcoming {
  background: linear-gradient(90deg, rgba(52,101,164,.35), rgba(52,101,164,.15)) !important;
  border: 1px solid rgba(125,165,215,.4) !important;
  border-radius: 0 !important;
  color: #dbe7ff !important;
}
body.theme-tactical #launcherMaintenanceBar {
  background: linear-gradient(90deg, rgba(255,138,43,.22), rgba(255,176,102,.1)) !important;
  border: 1px solid rgba(255,138,43,.5) !important;
  border-radius: 0 !important;
  color: #ffd9b0 !important;
}
body.theme-tactical .launcher-info-strip {
  background: var(--ink-2);
  border: 1px solid var(--hair);
}
body.theme-tactical .launcher-info-strip-text { color: var(--signal); }
/* UX feedu launchera: flex z grow — 1 karta wypelnia szerokosc, wiele sie
   zawija w rzedy po ~280px (grid auto-fit nie zwijal pustego toru przy 1 newsie) */
body.theme-tactical.launcher-shell-body .launcher-shell-layout .latest-news-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}
body.theme-tactical.launcher-shell-body .launcher-shell-layout .latest-news-grid > .shell-card {
  flex: 1 1 300px;
  min-width: 0;
}
/* hero karty: site.css ma max-height:260px + aspect 21/9 — przy pelnej karcie
   limit wysokosci kurczy szerokosc do ~607px. Zdejmujemy limit: obraz pelnia karte.
   [CHANGE 2026-09-18 IMG-ADAPT] Bylo `height: clamp(170px, 24vw, 300px)` (stala
   wysokosc). Przy karcie ~340px obraz 1.98:1 tracil ~29% wysokosci i to
   niesymetrycznie. Teraz proporcja pudelka pochodzi z PRAWDZIWEGO pliku
   (`--img-ar` ustawiony na `.card-media` w `launcher-shell.ejs`). */
body.theme-tactical.launcher-shell-body .shell-card .card-media {
  max-height: none;
  height: auto;
  aspect-ratio: var(--img-ar, 1.9);
}
/* ── [CHANGE 2026-09-18] LAUNCHER SHELL: skrót tresci + przycisk ──
   Ten sam problem co na portalu: `slice(0, 180) + '...'` ucinal w POLOWIE SLOWA,
   a `\n` z tresci bylo zwijane przez HTML (entery nierespektowane).
   Teraz `excerpt()` tnie na granicy slowa i zachowuje akapity, limit 180 -> 300. */
body.theme-tactical.launcher-shell-body .shell-card .shell-excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
  line-clamp: 6;
  overflow: hidden;
  /* ENTERY z tresci sa respektowane (JS przepuszcza `\n`) */
  white-space: pre-line;
  overflow-wrap: anywhere;
  color: var(--muted);
  margin: 0 0 12px;
}
body.theme-tactical.launcher-shell-body .shell-card a.shell-readmore {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  margin-top: auto;
  padding: 8px 14px;
  border: 1px solid var(--signal);
  background: transparent;
  color: var(--signal);
  font-family: 'Oswald', sans-serif;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  line-height: 1;
  text-transform: uppercase;
  text-decoration: none;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
  transition: background .16s ease, color .16s ease;
}
body.theme-tactical.launcher-shell-body .shell-card a.shell-readmore::after {
  content: "\2192";
  font-size: 1.05em;
  line-height: 1;
  transition: transform .16s ease;
}
body.theme-tactical.launcher-shell-body .shell-card a.shell-readmore:hover {
  background: var(--signal);
  color: #06070a;
}
body.theme-tactical.launcher-shell-body .shell-card a.shell-readmore:hover::after { transform: translateX(3px); }
body.theme-tactical.launcher-shell-body .launcher-shell-empty {
  border: 1px dashed var(--hair-2);
  color: var(--muted);
  text-align: center;
  padding: 34px;
}
body.theme-tactical .launcher-account-card,
body.theme-tactical .launcher-content-panel,
body.theme-tactical .launcher-status-box,
body.theme-tactical .shell-card {
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 0;
}
body.theme-tactical .launcher-shell-tabs a {
  border-radius: 0;
  background: none;
  border: 1px solid var(--hair);
  color: var(--muted);
  font-family: 'IBM Plex Mono', monospace;
  font-size: .68rem;
  letter-spacing: .14em;
  text-transform: uppercase;
}
body.theme-tactical .launcher-shell-tabs a.active {
  background: var(--signal);
  border-color: var(--signal);
  color: #06070a;
}
body.theme-tactical .launcher-launch-btn {
  background: var(--signal);
  color: #06070a;
  border-radius: 0;
  font-family: 'Oswald', sans-serif;
  text-transform: uppercase;
  letter-spacing: .14em;
}

/* ================= DROBNE ================= */
body.theme-tactical ::selection { background: rgba(255, 138, 43, 0.28); color: #fff; }
body.theme-tactical .auth-feedback { border-radius: 0; }
body.theme-tactical .cookie-consent-panel {
  background: var(--panel-2);
  border: 1px solid var(--hair-2);
  border-radius: 0;
}
body.theme-tactical .legal-page { border: 1px solid var(--hair); background: var(--panel); border-radius: 0; }
body.theme-tactical .article-card,
body.theme-tactical .article-hero,
body.theme-tactical .page-block { border-radius: 0; }
/* ---- STICKY FOOTER: strona krotsza niz ekran -> footer i tak na dole ---- */
body.theme-tactical {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
body.theme-tactical main { flex: 1 0 auto; }

/* [CHANGE 2026-09-15] gorny pasek na cala szerokosc (jak shell-wide):
   wczesniej .shell ograniczal topbar do 1380px/93vw — po dodaniu Download
   akcje rozbijaly sie na watkich-marginach. Start blizej lewej, koniec
   blizej prawej krawedzi okna. */
body.theme-tactical .topbar-inner {
  width: min(1800px, 98vw);
  max-width: 100%;
  padding-left: 8px;
  padding-right: 8px;
}
body.theme-tactical .topbar .main-nav { gap: 6px; }
body.theme-tactical .topbar .actions { gap: 10px; }

/* ================= SHOP: portfel / zakładki / chipy nahród ================= */
body.theme-tactical .shop-wallet-value {
  color: #ffffff;
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 2rem;
  letter-spacing: 0.02em;
  text-shadow: none;
}
body.theme-tactical .shop-wallet-label { color: #eef2f8; }
/* kwoty portfela: pelna biel, wyrazisty Oswald, wiekszy */
body.theme-tactical .shop-wallet-value {
  color: #ffffff;
  text-shadow: none;
  font-size: 2.15rem;
  font-weight: 700;
}
/* ilosc kredytow w pakiecie (formule 900+100=1000) wieksza */
body.theme-tactical .shop-package-formula { font-size: 1.5rem; }
body.theme-tactical .shop-package-formula-segment { color: #ffffff; font-weight: 800; }
/* cena EUR wyrazniejsza i wieksza */
body.theme-tactical .price {
  color: #ffffff;
  font-family: 'Oswald', sans-serif;
  font-size: 1.18rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
body.theme-tactical .shop-pack-offer-price {
  color: #eef2f8;
  font-size: 1.02rem;
  font-weight: 700;
}
/* karta portfela: wyrazistsza, ale subtelna — grafit + amber mgla */
body.theme-tactical .shop-wallet-card {
  background:
    linear-gradient(180deg, rgba(255, 138, 43, 0.07), rgba(255, 138, 43, 0) 55%),
    var(--panel-2);
  border: 1px solid var(--hair-2);
}
/* zakładki Credits/Packs: bez niebieskiego gradientu — czysty tactical */
body.theme-tactical .shop-tabs {
  display: inline-flex;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 0;
}
body.theme-tactical .shop-tab-button {
  font-family: 'Oswald', sans-serif;
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  background: transparent;
  border: 0;
  padding: 10px 26px;
  position: relative;
}
body.theme-tactical .shop-tab-button:hover { color: #fff; }
body.theme-tactical .shop-tab-button.is-active {
  background: var(--signal);
  color: #06070a;
}
body.theme-tactical .shop-tab-button.is-active::after { display: none; }
/* chipy nahród (900 kredytow / 2000 koron) i milestone — wyrazistsze:
   grafit + amber linia + delikatna mgla, sciete naroze */
body.theme-tactical .shop-pack-reward-item,
body.theme-tactical .shop-milestone-reward-item {
  background:
    linear-gradient(180deg, rgba(255, 138, 43, 0.06), rgba(255, 138, 43, 0) 60%),
    var(--panel-2);
  border: 1px solid rgba(255, 138, 43, 0.35);
  border-radius: 0;
  color: #f2f5fa;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  padding: 9px 13px;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
body.theme-tactical .shop-pack-reward-item span,
body.theme-tactical .shop-milestone-reward-item span { color: var(--signal); font-weight: 600; }
body.theme-tactical .shop-package-corner-badge {
  background: var(--signal);
  color: #06070a;
  border-radius: 0;
  font-family: 'IBM Plex Mono', monospace;
  letter-spacing: 0.1em;
}

/* ================= LEADERBOARD: taby jak tytul, panel nahród ================= */
/* zero phantom scrolla w kontenerze tabeli na desktopie (1px overflow:auto) */
@media (min-width: 981px) {
  body.theme-tactical .leaderboard-layout .table-wrap { overflow: visible; }
}
body.theme-tactical .leaderboard-tab {
  font-family: 'Oswald', sans-serif;
  font-size: 0.92rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: #cfd7e4;
  background: var(--ink-2);
  border: 1px solid var(--hair);
  padding: 9px 16px;
}
body.theme-tactical .leaderboard-tab:hover { color: #fff; border-color: var(--hair-2); }
body.theme-tactical .leaderboard-tab.active {
  color: #fff;
  background: var(--panel-2);
  border-color: var(--hair-2);
}
body.theme-tactical .leaderboard-tab.active::after { background: var(--signal); }
/* panel "Daily rewards preview / Next daily payout" — tactical, czytelny */
body.theme-tactical .leaderboard-rewards-card {
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 0;
}
body.theme-tactical .leaderboard-rewards-card h3 {
  font-size: 1.15rem;
  letter-spacing: 0.06em;
  color: #fff;
}
body.theme-tactical .leaderboard-timer {
  background: #000;
  border: 1px solid var(--hair-2);
  border-left: 3px solid var(--signal);
  border-radius: 0;
  padding: 12px 14px;
}
body.theme-tactical .leaderboard-timer p[style] {
  color: #ffffff !important;
}
body.theme-tactical .leaderboard-timer p[style]:last-child,
body.theme-tactical .leaderboard-timer [data-next-payout-text] {
  font-family: 'Oswald', sans-serif !important;
  font-size: 1.3rem !important;
  font-weight: 500 !important;
  letter-spacing: 0.06em;
  color: #ffffff !important;
}
body.theme-tactical .leaderboard-rewards-card h4 {
  color: var(--signal);
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  border-bottom: 1px solid var(--hair);
  padding-bottom: 6px;
}
body.theme-tactical .leaderboard-reward-list li {
  background: var(--ink-2);
  border: 1px solid var(--hair);
  border-radius: 0;
}
body.theme-tactical .leaderboard-reward-list li strong {
  font-family: 'Oswald', sans-serif;
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: #fff;
}
body.theme-tactical .leaderboard-reward-list li span {
  color: #eef2f8;
  font-size: 0.95rem;
}

/* ================= ARTYKUL NEWSA: szerszy (1080px) + marginesy wewnatrz ================= */
body.theme-tactical .article-card {
  max-width: 1080px;
  margin: 0 auto;
  background: var(--panel);
  border: 1px solid var(--hair);
  padding: clamp(22px, 4vw, 52px);
}
body.theme-tactical .article-card h1 {
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  margin: 6px 0 18px;
}
body.theme-tactical .article-body {
  font-size: 1.06rem;
  line-height: 1.8;
  color: #cfd7e4;
  max-width: 74ch;
}
body.theme-tactical .article-hero { border: 1px solid var(--hair); position: relative; }
/* maskowanie ucietych koncowek zdjecia: gradientowy blur na ostatnich ~70px */
body.theme-tactical .article-hero::after {
  content: "";
  position: absolute;
  left: -1px;
  right: -1px;
  bottom: -1px;
  height: 70px;
  pointer-events: none;
  backdrop-filter: blur(9px) saturate(1.02);
  -webkit-backdrop-filter: blur(9px) saturate(1.02);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.55) 55%, #000 100%);
  mask-image: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.55) 55%, #000 100%);
  border-radius: 0 0 0 0;
}
/* blur BOCZNY: lewa/prawa krawedz zdjecia rozmywa sie w tlo karty */
body.theme-tactical .article-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  -webkit-mask-image: linear-gradient(90deg, #000 0%, transparent 6%, transparent 94%, #000 100%);
  mask-image: linear-gradient(90deg, #000 0%, transparent 6%, transparent 94%, #000 100%);
}

/* ================= BREADCRUMBS (news detail) ================= */
.breadcrumbs ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 18px;
  padding: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.breadcrumbs li + li::before { content: "›"; margin-right: 6px; color: var(--signal-dim); }
.breadcrumbs a { color: var(--muted); text-decoration: none; }
.breadcrumbs a:hover { color: var(--signal); }
.breadcrumbs li[aria-current="page"] { color: #cfd7e4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch; }

/* ================= PASEK TLA HOME (background.jpg) =================
   Dekoracyjny pas miedzy sekcja news a "Need help?" (wstrzykiwany w
   home.ejs jako .home-bg-band). Tlo fixed = obraz "przeswituje" przez
   okno jak panorama; maski gradientowe wygaszaja gore i dol. */
.home-bg-band {
  height: clamp(220px, 34vw, 460px);
  max-width: 1800px;
  margin: 0 auto;
  background:
    /* 1) winieta — miekkie sciemnienie krawedzi i narozyznikow (okno w scene) */
    radial-gradient(115% 95% at 50% 45%, rgba(4, 5, 8, 0) 0%, rgba(4, 5, 8, 0) 38%, rgba(4, 5, 8, 0.42) 74%, rgba(4, 5, 8, 0.82) 100%),
    /* 2) pionowy scrim — gestszy u gory/dolu, przezroczysty w srodku */
    linear-gradient(180deg, rgba(6, 7, 10, 0.78) 0%, rgba(6, 7, 10, 0.4) 26%, rgba(6, 7, 10, 0.4) 64%, rgba(6, 7, 10, 0.82) 100%),
    /* [CHANGE 2026-09-15 perf] background.png (PNG 883 KB) -> background.jpg
       (JPEG q78 328 KB, ten sam artwork). Paletowy PNG bez alfy = marny
       rozmiar; JPEG nie powoduje bandingu (winieta/scrim maskuja ew. artefakty). */
    url('/img/background.jpg') center / cover no-repeat fixed;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 86%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 86%, transparent 100%);
}
@media (max-width: 980px) {
  .home-bg-band {
    height: clamp(180px, 44vw, 300px);
    background-attachment: scroll; /* iOS/Android: fixed przy cover bywa bug */
    background-position: center 38%;
  }
}

/* ================= LATEST NEWS: rowne karty + pager =================
   site.css: .latest-layout{align-items:stretch} rozciagalo lewa siatke do
   wysokosci sidebaru (Discord iframe 400px) -> karta miala ~668px pustki pod
   trescia. Start zamiast stretch + line-clamp wyimka = rowne, zwarte karty. */
body.theme-tactical .latest-layout { align-items: start; }
body.theme-tactical .latest-news-grid { align-items: stretch; }
body.theme-tactical .news-card {
  display: flex;
  flex-direction: column;
}
body.theme-tactical .news-card h3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  min-height: 2.4em;
}
body.theme-tactical .news-card .news-excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  /* [CHANGE 2026-09-18 EXCERPT] Bylo `line-clamp: 3` => widac bylo ~3 linie
     (klient prosil o wiecej tresci). Teraz 6 linii + limit znakow w JS
     podniesiony 200 -> 320. */
  -webkit-line-clamp: 6;
  line-clamp: 6;
  overflow: hidden;
  /* ⚠️ KLUCZOWE: `pre-line` sprawia, ze ENTERY z tresci sa respektowane
     (HTML domyslnie zwija biale znaki, wiec akapity sie zlewaly).
     `excerpt()` w JS przepuszcza `\n`, to wlasnie tutaj je widac. */
  white-space: pre-line;
  /* dlugie URL-e / kody nie rozsadzaja karty */
  overflow-wrap: anywhere;
  margin: 0 0 12px;
}
/* ── [CHANGE 2026-09-18] PRZYCISK "READ MORE" ──
   Bylo: goły link w IBM Plex Mono .7rem. Teraz element akcentowany, spojny
   z `.btn` (sciete narozniki, Oswald, amber), z wejsciem strzalki i wypelnieniem
   na hover — zeby gracz realnie chcial kliknac. */
body.theme-tactical .card a.news-readmore {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  align-self: flex-start;
  margin-top: auto;
  padding: 9px 16px;
  border: 1px solid var(--signal);
  background: transparent;
  color: var(--signal);
  font-family: 'Oswald', sans-serif;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  line-height: 1;
  text-transform: uppercase;
  text-decoration: none;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  transition: background .16s ease, color .16s ease;
}
body.theme-tactical .card a.news-readmore::after {
  content: "\2192";
  font-size: 1.05em;
  line-height: 1;
  transition: transform .16s ease;
}
body.theme-tactical .card a.news-readmore:hover {
  background: var(--signal);
  color: #06070a;
}
body.theme-tactical .card a.news-readmore:hover::after { transform: translateX(3px); }
body.theme-tactical .card a.news-readmore:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
}
body.theme-tactical .news-card > a:last-child { margin-top: auto; }

/* pager 1 2 3 (pojawia sie tylko gdy >1 strony) */
.news-pager {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: 26px;
}
.news-pager-page {
  min-width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--hair);
  text-decoration: none;
  transition: color .16s ease, border-color .16s ease, background .16s ease;
}
.news-pager-page:hover { color: #fff; border-color: var(--hair-2); background: var(--panel-2); }
.news-pager-page.is-current {
  color: #06070a;
  background: var(--signal);
  border-color: var(--signal);
  font-weight: 600;
}

/* ================= DOWNLOAD CTA (header, po prawej od jezyka) =============
   Blekitny gradient — swiadomie inny kolor niz amber akcent motywu: ma byc
   JEDYNY przyciskiem "akcji pobrania" i kierowac wzrok gracza. Dlugi,
   elegancki, z mikroruchem ikony na hover. */
body.theme-tactical .download-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-width: 148px;
  justify-content: center;
  height: 40px;
  padding: 0 22px;
  border: 1px solid rgba(96, 173, 255, 0.55);
  border-radius: 10px;
  background: linear-gradient(135deg, #1e6fd9 0%, #2f8ef0 45%, #55b7ff 100%);
  color: #fff;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 0.98rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 6px 18px -8px rgba(47, 142, 240, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.28);
  transition: transform .16s ease, box-shadow .2s ease, filter .2s ease;
}
body.theme-tactical .download-btn:hover {
  transform: translateY(-1px);
  filter: brightness(1.07);
  box-shadow: 0 10px 26px -8px rgba(47, 142, 240, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.32);
}
body.theme-tactical .download-btn:active { transform: translateY(0); filter: brightness(0.96); }
body.theme-tactical .download-btn:focus-visible { outline: 2px solid #7cc0ff; outline-offset: 2px; }
body.theme-tactical .download-btn-icon { transition: transform .25s ease; }
body.theme-tactical .download-btn:hover .download-btn-icon { transform: translateY(2px); }
/* toast "coming soon" (pokazywany przez site.js) */
.download-toast {
  position: fixed;
  top: 78px;
  right: 24px;
  z-index: 1200;
  background: var(--panel-2, #12161e);
  color: #eef2f8;
  border: 1px solid rgba(96, 173, 255, 0.45);
  border-left: 3px solid #2f8ef0;
  padding: 12px 18px;
  font-family: 'Rajdhani', sans-serif;
  font-size: 0.95rem;
  letter-spacing: 0.03em;
  box-shadow: 0 18px 40px -16px rgba(0, 0, 0, 0.8);
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity .25s ease, transform .25s ease;
  pointer-events: none;
}
.download-toast.is-visible { opacity: 1; transform: translateY(0); }

/* ================= RESPONSYWNOSC =================
   Fix phantom horizontal scroll: site.css liczy .shell/.shell-wide w vw,
   a vw ignoruje pasek przewijania -> przy watkim ekranie kontener jest ~7px
   szerszy niz dostepne miejsce = przewijanie w prawo. max-width:100%
   ogranicza do szerokosci rodzica (clientWidth, bez paska).
   site.css lamie layout przy 980px (topbar-inner -> 1 kolumna, gridy -> 1fr).
   Nadpisania B (flex w topbar, flex w hero-tabs, 1px gap gridy) musza byc
   tu przywrocone do zachowan mobilnych, inaczej strona sie przelewa. */
body.theme-tactical .shell,
body.theme-tactical .shell-wide { max-width: 100%; }

@media (max-width: 1280px) {
  /* topbar: 6 elementow nawigacji + actions nie mieszcza sie w 980-1280px
     (grid 300px/1fr/auto) — flex+wrap zapobiega przelewaniu (tez bug oryginalu) */
  body.theme-tactical .topbar-inner {
    display: flex;
    flex-wrap: wrap;
    row-gap: 4px;
    min-height: 0;
    padding: 8px 0;
  }
  body.theme-tactical .main-nav { flex: 1 1 auto; flex-wrap: wrap; }
  /* [CHANGE 2026-09-20 BP-R30-NAV-FONT] To jest DOKLADNIE ten przedzial
     szerokosci, w ktorym zglaszano lamanie „Battle Pass" na dwie linie.
     `0.92rem` (bylo 1rem) + `nowrap` z reguly bazowej = etykieta miesci sie
     w slocie i nie da sie jej zlamac. */
  body.theme-tactical .main-nav a { height: 52px; padding: 0 12px; font-size: 0.92rem; }
  body.theme-tactical .actions { flex: 0 0 auto; }
}

@media (max-width: 980px) {
  /* leaderboard tabs: flex bez zawijania przelewal dokument (bug tez w oryginale)
     -> zawijanie w rzedy; BP panele o stala szerokosci grid -> 1 kolumna */
  body.theme-tactical .leaderboard-tabs {
    flex-wrap: wrap;
    gap: 0;
  }
  body.theme-tactical .leaderboard-tab { padding: 10px 14px; }
  body.theme-tactical .bp-currency-panel,
  body.theme-tactical .bp-globe-section {
    grid-template-columns: 1fr !important;
    min-width: 0;
  }
  body.theme-tactical .bp-currency-k,
  body.theme-tactical .bp-currency-main,
  body.theme-tactical .bp-currency-note { width: 100% !important; max-width: 100%; }
  body.theme-tactical .bp-globe-stage { max-width: 100%; min-width: 0; }
  body.theme-tactical .bp-globe-stage canvas { max-width: 100%; height: auto; }
  body.theme-tactical .bp-globe-command { max-width: 100%; }
  body.theme-tactical .bp-stats-row { flex-wrap: wrap; }
  body.theme-tactical .topbar-inner {
    display: block;
    min-height: 0;
    padding: 10px 0;
  }
  body.theme-tactical .brand { font-size: 1.15rem; }
  body.theme-tactical .brand img { width: 40px; height: 40px; }
  body.theme-tactical .main-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    margin: 8px 0;
  }
  body.theme-tactical .main-nav a {
    height: auto;
    padding: 8px 12px;
    font-size: 1rem;
  }
  body.theme-tactical .main-nav a::after { display: none; }
  /* [CHANGE 2026-09-17 NAV-MOBILE-TOGGLE] User pkt 2: „Chciałbym w wersji
     mobilnej ukryc pasek home/news/ pod jednym przyciskiem bo ciagle za duzo
     miejsca zajmuje".
     ⚠️ Ta reguła MUSI byc TUTAJ, a nie w `site.css`: motyw Tactical laduje sie
     PO `site.css` i ustawia `body.theme-tactical .main-nav { display: flex }`
     (specyficznosc 0,2,0 > 0,1,0 z `site.css`), wiec schowanie nawigacji
     w `site.css` bylo BEZ EFEKTU — pasek dalej zajmowal ~430 px wysokosci.
     Pokejscie do tego miejsca zajelo chwile: computed style pokazywal `flex`
     mimo poprawnej reguly `display: none` w arkuszu bazowym. */
  body.theme-tactical .main-nav {
    display: none;
    margin: 0;
  }
  body.theme-tactical .topbar-inner.is-nav-open .main-nav {
    display: flex;
    margin: 8px 0;
    animation: bpNavPanelIn 0.18s ease-out 1 both;
  }
  /* Przycisk menu trzyma sie prawej krawedzi rzedu akcji (na wypadek zawezenia). */
  body.theme-tactical .nav-toggle { margin-left: auto; }
  /* [CHANGE 2026-09-17 NAV-MOBILE-ROW] Uklad mobilnego paska: JEDEN rzad
     `[logo] [akcje + hamburger]`, a panel nawigacji (po otwarciu) jako drugi
     pelnowymiarowy rzad. `order` jest konieczne, bo w DOM nawigacja stoi
     PRZED `.actions` — bez tego rozwiniety panel wepchnalby rzad akcji (razem
     z przyciskiem zamykania) na sam dol paska.

     ⚠️ Tu NIE MA `width: 100%` na `.actions` (bylo w poprzedniej wersji i to
     ono spychalo akcje do wlasnego wiersza, przez co pasek mial 3 linie zamiast
     dwoch). `min-width: 0` pozwala rzędowi sie zwezic, a `flex-wrap: nowrap`
     trzyma wszystkie akcje w JEDNEJ linii. */
  body.theme-tactical .topbar-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    min-height: 0;
    padding: 8px 0;
  }
  body.theme-tactical .brand { order: 1; flex: 0 1 auto; }
  body.theme-tactical .actions {
    position: relative;
    order: 2;
    flex: 1 1 auto;
    min-width: 0;
    width: auto;
    flex-wrap: nowrap;
    justify-content: flex-end;
  }
  body.theme-tactical .main-nav { order: 3; flex: 1 1 100%; }
  /* [CHANGE 2026-09-17 NAV-MOBILE-COMPACT] Rzad akcji MUSI zmiescic sie w jednej
     linii — inaczej przycisk „Download" lamie sie do drugiego rzedu i zjada
     ~60 px wysokosci na sam siebie (dokladnie to widac na 390 px). Zmniejszamy
     odstepy, sprowadzamy Download do kwadratowej ikony i tniemy nazwe gracza.

     ⚠️ DWA POMYLONE SZCZEGOLY, KTORE TRZEBA ZNAC:
      1) `.actions` ma `gap` ustawiony w `body.theme-tactical .topbar .actions`
         (specyficznosc 0,3,1) — samo `body.theme-tactical .actions` (0,2,1)
         NIE wystarczy, regula bazowa wygrywa.
      2) `.download-btn` ma `min-width: 148px` — bez wyzerowania tego pola
         `width: 38px` nic nie daje i przycisk dalej lamie rzad do dwoch linii. */
  body.theme-tactical .topbar .actions { gap: 6px; }
  body.theme-tactical .download-btn {
    width: 38px;
    height: 34px;
    min-width: 0;
    padding: 0;
    flex: 0 0 38px;
    justify-content: center;
  }
  body.theme-tactical .download-btn span { display: none; }
  body.theme-tactical .launcher-user-badge { max-width: 112px; flex: 0 1 auto; }
  body.theme-tactical .launcher-user-name { max-width: 62px; }
  body.theme-tactical .auth-inline-form .btn { padding: 5px 9px; font-size: 0.72rem; }
  /* hero-tabs: B zrobilo z nich flex — na mobile wracaja do 1 kolumny */
  body.theme-tactical .hero-tabs {
    display: grid;
    grid-template-columns: 1fr;
  }
  body.theme-tactical .hero-tabs a {
    border-right: 0;
    border-bottom: 1px solid var(--hair);
  }
  body.theme-tactical .hero-tabs a:first-child { border-left: 0; }
  /* headline-grid / support-cards: B uzyla gap:1px jako "linii" — na mobile
     1 kolumna, wiec linia ma byc pozioma (border) zamiast pionowej przerwy */
  body.theme-tactical .headline-grid {
    grid-template-columns: 1fr;
    gap: 1px;
  }
  body.theme-tactical .support-cards {
    grid-template-columns: 1fr;
    gap: 1px;
  }
  /* [CHANGE 2026-09-18 HERO] Bylo `height: clamp(280px, 62vw, 420px)` — stala
     wysokosc wymuszala ciecie. Teraz na waskich ekranach zmieniamy PROPORCJE
     pudelka (`--hero-ar`), a nie wysokosc: 1.6 zamiast 1.98 daje wyzszy banner.
     Przy obrazku 1.98:1 pudelko 1.6:1 obcina BOKI (~19%), a NIE gore/dol —
     czyli dokladnie to, o co prosil user. */
  body.theme-tactical .hero-banner-image { --hero-ar: 1.6; }
  body.theme-tactical .hero-stage h1 { font-size: clamp(2rem, 9vw, 3rem); }
  body.theme-tactical .headline-side-caption strong { font-size: 1.25rem; }
  body.theme-tactical .footer-inner {
    flex-direction: column;
    gap: 16px;
    text-align: center;
  }
  body.theme-tactical .footer-links { flex-wrap: wrap; justify-content: center; gap: 14px; }
  body.theme-tactical .footer-brand { font-size: 1.2rem; }
  body.theme-tactical .footer-brand img { width: 40px; height: 40px; }
}

@media (max-width: 560px) {
  body.theme-tactical .section { padding: 28px 0; }
  body.theme-tactical .hero-banner-overlay { padding: 20px; }
  body.theme-tactical .headline-caption,
  body.theme-tactical .headline-side-caption { padding: 26px 16px 16px; }
  body.theme-tactical .latest-sidebar-caption strong { font-size: 1rem; }
  body.theme-tactical .btn,
  body.theme-tactical .btn-red { padding: 10px 16px; font-size: 0.78rem; }
}

/* ============================================================================
   BATTLE PASS — FINALIZACJA PALETY "TACTICAL BLACK" (autorytatywny blok)
   Naprawa systemowa: (1) tokeny --accent/--accent-2 przedefiniowane w .bp-page
   wyzej — to zasilaja WSZYSTKIE var(--accent) w BP; (2) ponizsze nadpisania
   łapią LITERAŁY rgba/hex cyan/zielen/roz, ktorych tokeny nie obejmuja (~90
   miejsc przescanowanych z site.css linie 2456-5760).
   WYJATEK ZGODNIE Z ZYCZENIEM: globus (#bp-globe-canvas, .bp-globe-marker,
   .bp-globe-ring/layer/shine) — NIEZMIENIONY.
   Paleta: amber #ff8a2b = akcent, #ffb066 = wariant jasny (claimable/done),
   hairline #222834/#2f3746 = ramki neutralne, tekst = cieple biele.
   ========================================================================== */

/* --- hero: teksty i chipy --- */
body.theme-tactical .bp-subtitle { color: #f0e8dc; }
body.theme-tactical .bp-hero-tagline { color: #b9ae9c; }
body.theme-tactical .bp-level-next-reward { color: #c9bfae; }
body.theme-tactical .bp-hero-op-id { color: rgba(255, 138, 43, 0.55); }
body.theme-tactical .bp-hero-chip { border: 1px solid rgba(255, 138, 43, 0.28); }
body.theme-tactical .bp-chip-live {
  color: #ffb066;
  border-color: rgba(255, 176, 102, 0.35);
  background: rgba(255, 176, 102, 0.08);
}
body.theme-tactical .bp-chip-dot { background: #ffb066; }

/* --- paski postepu / rail --- */
body.theme-tactical .bp-level-bar { border-color: rgba(255, 138, 43, 0.22); }
body.theme-tactical .bp-rail-dot { border-color: #2f3746; }
body.theme-tactical .bp-rail-node.reached .bp-rail-dot { box-shadow: 0 0 12px rgba(255, 138, 43, 0.5); }
body.theme-tactical .bp-rail-node.claimed .bp-rail-dot { background: var(--signal-dim); border-color: var(--signal-dim); }
body.theme-tactical .bp-rewards-table th { border-bottom: 1px solid #222834; }
body.theme-tactical .bp-badge-claimed { color: #ffb066; }
body.theme-tactical .bp-badge-locked { color: #6b7280; }
body.theme-tactical .bp-badge-claimable { color: #ffb066; background: rgba(255, 176, 102, 0.1); border-color: rgba(255, 176, 102, 0.3); }
body.theme-tactical .bp-task-desc { color: #a8a294; }
body.theme-tactical .bp-task-claimed-badge { color: #ffb066; }

/* --- loader --- */
body.theme-tactical .bp-loader-core {
  background: radial-gradient(circle at 34% 34%, #ffd9a8 0%, #ff9c3c 45%, #4a2508 100%);
  box-shadow: 0 0 36px rgba(255, 138, 43, 0.45);
}

/* --- quest panel / lock / ikony --- */
body.theme-tactical .bp-quest-lock-icon-open {
  color: rgba(255, 176, 102, 0.9);
  filter: drop-shadow(0 0 6px rgba(255, 176, 102, 0.3));
}
body.theme-tactical .bp-quest-state-badge.bp-quest-state-available {
  background: rgba(148, 152, 165, 0.16);
  border-color: rgba(148, 152, 165, 0.3);
  color: #c3c8d4;
}
body.theme-tactical .bp-quest-state-badge.bp-quest-state-claimed {
  background: rgba(255, 138, 43, 0.08);
  border-color: rgba(255, 138, 43, 0.22);
  color: #c9a06e;
}
body.theme-tactical .bp-quest-progress-bar { border-color: #2f3746; }
body.theme-tactical .bp-quest-progress-fill { background: var(--signal); }
body.theme-tactical .bp-quest-reward-icon { background: rgba(255, 138, 43, 0.12); }
body.theme-tactical .bp-quest-reward-item:hover .bp-quest-reward-icon { background: rgba(255, 138, 43, 0.2); }
body.theme-tactical .bp-quest-reward-icon.xp {
  background: linear-gradient(135deg, rgba(255, 138, 43, 0.28), rgba(255, 194, 122, 0.16));
}
body.theme-tactical .bp-quest-reward-item:hover .bp-quest-reward-icon.xp {
  background: linear-gradient(135deg, rgba(255, 138, 43, 0.44), rgba(255, 194, 122, 0.26));
}
body.theme-tactical .bp-quest-reward-icon.item {
  background: linear-gradient(135deg, rgba(200, 160, 255, 0.14), rgba(220, 190, 255, 0.08));
}
body.theme-tactical .bp-quest-meta { border-top: 1px solid #222834; }

/* --- kontynenty / currency / access / ops --- */
body.theme-tactical .bp-continent-panel { border-color: #222834; }
body.theme-tactical .bp-currency-fallback { border-color: #222834; }
body.theme-tactical .bp-continent-card {
  border: 1px solid #2b2334;
  border-left: 3px solid #3a2b45;
  background: linear-gradient(150deg, rgba(30, 14, 38, 0.78), rgba(12, 9, 16, 0.85));
}
body.theme-tactical .bp-continent-card:hover { border-color: rgba(255, 77, 146, 0.5); }
body.theme-tactical .bp-continent-card.is-active {
  border-color: rgba(255, 77, 146, 0.62);
  box-shadow: 0 0 0 1px rgba(255, 64, 129, 0.25), 0 10px 26px rgba(120, 20, 80, 0.22);
}
body.theme-tactical .bp-continent-card.is-unlocked { border-left-color: rgba(255, 126, 178, 0.6); }
body.theme-tactical .bp-continent-card:focus-visible { outline-color: rgba(255, 77, 146, 0.9); }
body.theme-tactical .bp-continent-dossier-tag.is-active {
  color: #ff9ecb;
  background: rgba(255, 77, 146, 0.1);
  border-color: rgba(255, 77, 146, 0.32);
}
body.theme-tactical .bp-infinite-card { border-color: #222834; }
body.theme-tactical .bp-infinite-col { border-color: #222834; }
body.theme-tactical .bp-access-chip { border-color: #2f3746; }
body.theme-tactical .bp-access-chip.is-ready { border-color: rgba(255, 176, 102, 0.4); color: #ffb066; }
body.theme-tactical .bp-ops-tile { border-color: #222834; }
body.theme-tactical .bp-ops-card[data-unlocked="true"] { border-left-color: rgba(255, 176, 102, 0.45); }
body.theme-tactical .bp-ops-value { color: #f5f1ea; }

/* --- task state badges (dot/claim glow) --- */
body.theme-tactical .bp-task-state-badge { border-color: #2f3746; }
body.theme-tactical .bp-task-card[data-ui-state="active"] .bp-task-state-badge { border-color: rgba(255, 138, 43, 0.45); }
body.theme-tactical .bp-task-card[data-ui-state="claimable"] .bp-task-state-badge { border-color: rgba(255, 176, 102, 0.4); }
body.theme-tactical .bp-task-card.is-globe-focus {
  border-color: rgba(255, 138, 43, 0.55);
  box-shadow: 0 0 0 1px rgba(255, 138, 43, 0.35), 0 14px 24px rgba(0, 0, 0, 0.28);
}
@keyframes tbGlobePing {
  from { box-shadow: 0 0 0 0 rgba(255, 138, 43, 0.7); }
  to   { box-shadow: 0 0 0 14px rgba(255, 138, 43, 0); }
}
body.theme-tactical .bp-task-card.is-globe-ping { animation: tbGlobePing 1s ease-out; }
body.theme-tactical .bp-dot.is-active { background: var(--signal); }
body.theme-tactical .bp-dot.is-done { background: #ffb066; }
body.theme-tactical .bp-command-strip { border-color: rgba(255, 138, 43, 0.2); }
body.theme-tactical .bp-cmd-claimable {
  background: rgba(255, 176, 102, 0.07) !important;
  border-color: rgba(255, 176, 102, 0.28) !important;
}
body.theme-tactical .bp-task-card.bp-task-state-claimable .bp-claim-task { box-shadow: 0 0 12px rgba(255, 176, 102, 0.35); }

/* --- route panel (mapa konturowa misji): [CHANGE 2026-09-15 BP-PURPLE] amber ->
   purpura/róż palety mapy (fill #2a1230, borders #ff4081, coastline #ff4d92). --- */
body.theme-tactical .bp-route-panel {
  border-color: rgba(255, 77, 146, 0.32);
  background: linear-gradient(160deg, rgba(26, 12, 32, 0.94), rgba(12, 10, 18, 0.92));
  box-shadow: inset 0 0 40px rgba(255, 64, 129, 0.06), 0 12px 28px rgba(0, 0, 0, 0.28);
}
body.theme-tactical .bp-route-kicker { color: #ff4d92; }
body.theme-tactical .bp-route-title { color: #ffe3ee; }
body.theme-tactical .bp-route-meta { color: #a894a4; }
body.theme-tactical .bp-route-visual-wrap {
  border-color: rgba(255, 77, 146, 0.25);
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 64, 129, 0.08), transparent 55%),
    rgba(10, 8, 14, 0.92);
}
body.theme-tactical .bp-route-contour {
  fill: rgba(255, 138, 43, 0.1);
  stroke: rgba(255, 160, 80, 0.85);
  filter: drop-shadow(0 0 5px rgba(255, 138, 43, 0.3));
}
/* ---- [CHANGE 2026-09-15 BP-MAP-COLORS] Trasa w kolorach mapy (rozowo-magenta) ----
   Wczesniej linki/wordy byly amber (255,138,43) -> na purpurowej mapie wygladaly
   jak " brazowe sciezki". Teraz trzymamy sie palety SVG: #ff4d92 (coastline),
   #ff4081 (borders), #2a1230 (fill). */
body.theme-tactical .bp-route-link { stroke: rgba(255, 77, 146, 0.6); }
body.theme-tactical .bp-route-link.is-complete { stroke: rgba(255, 126, 178, 0.95); }
/* [CHANGE 2026-09-16 BP-NODE-REDESIGN] User (5. raz): po kliknieciu punktu byla
   GRUBA BIALA obwodka + bialy srodek i nie dalo sie odczytac numeru. Nowy jezyk:
   obwodka = magENTA (nigdy biel), srodek = gleboki purpurowy, numer = jasny roz.
   Zaznaczenie czytane po JASNYM wypelnieniu + rozowej poswiacie, nie po bieli. */
body.theme-tactical .bp-route-node-core { stroke: rgba(255, 77, 146, 0.8); stroke-width: 0.42; }
body.theme-tactical .bp-route-node-inner { fill: #1c0c1f; }
body.theme-tactical .bp-route-node-label { fill: #ffd9e8; }
/* [CHANGE 2026-09-16 BP-HALO-SOFT] To byta PRAWDZWA "duza nieprzyjemna obramowka":
   .is-current mialo drop-shadow 9px LICZONE W JEDNOSTKACH viewBox (100x72 -> ~1245px),
   wiec 9 j. = ~112px realnego halo wokot aktywnego punktu. Zredukowane do 1.1 j.
   (~14px) + niska alfa. Aktywny krok poznawany po kolorze wypelnienia, nie po swiecie.
   (is-selected byl juz cienki w zeszlej turze — to NIE on byt winowajca.) */
body.theme-tactical .bp-route-node.is-current .bp-route-node-core {
  stroke: #ff8ec0;
  filter: drop-shadow(0 0 1.1px rgba(255, 77, 146, 0.4));
}
body.theme-tactical .bp-route-node.is-current .bp-route-node-inner { fill: #ff8ec0; }
/* [CHANGE 2026-09-16 BP-NODE-NOWHITE] Etykieta kroku "current" byla czysta biel —
   to samo w sobie nie bylo winowajca (chodzilo o bialy STROKE), ale dla spójności
   jezyka kolorow schodzimy do jasnego rozu. */
body.theme-tactical .bp-route-node.is-current .bp-route-node-label { fill: #ffd9e8; }
/* [CHANGE 2026-09-16 BP-NODE-STATES] User: poswiata zaleznie od stanu taska:
   AKTYWNY = zlota, ZAKONCZONY = zielona, NIEDOSTEPNY = subtelna czerwona.
   (Selekcja po kliknieciu zostaje magENTA — ma wyzsza specyficznosc, patrz nizej.) */
body.theme-tactical .bp-route-node.is-active .bp-route-node-core {
  stroke: #ffc247;
  stroke-width: 0.5;
  filter: drop-shadow(0 0 1.3px rgba(255, 194, 71, 0.75));
}
body.theme-tactical .bp-route-node.is-active .bp-route-node-inner { fill: #6b4a0e; }
body.theme-tactical .bp-route-node.is-active .bp-route-node-label { fill: #ffe6a8; }
body.theme-tactical .bp-route-node.is-done .bp-route-node-core {
  stroke: #5ef5a8;
  stroke-width: 0.5;
  filter: drop-shadow(0 0 1.3px rgba(94, 245, 168, 0.7));
}
body.theme-tactical .bp-route-node.is-done .bp-route-node-inner { fill: #14442c; }
body.theme-tactical .bp-route-node.is-done .bp-route-node-label { fill: #c6ffe4; }
body.theme-tactical .bp-route-node.is-locked .bp-route-node-core {
  stroke: rgba(255, 96, 96, 0.55);
  filter: drop-shadow(0 0 0.9px rgba(255, 80, 80, 0.35));
}
body.theme-tactical .bp-route-node.is-locked .bp-route-node-inner { fill: rgba(70, 20, 26, 0.85); }
body.theme-tactical .bp-route-node.is-locked .bp-route-node-label { fill: #ffb9b9; }
/* [CHANGE 2026-09-24 BP-R74-NODE-GLOW] UKOŃCZONE = ZIELONE, ZŁOTA TYLKO AKTYWNE.
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE: „Misje (node) które zostały ukończone świecą się na żółto a nie
   na zielono - na żółto ma się świecić (poświatą) tylko aktywny quest".

   PRZYCZYNA: `getRouteStateFromTask()` (public/js/battlepass.js) zwraca
   `claimable` dla zadania UKOŃCZONEGO z NIEODEBRANĄ nagrodą (`completed=1`,
   `reward_claimed=0`) — a to jest stan DOMYŚLNY tuż po ukończeniu. Ten blok
   nadawał `claimable` DOKŁADNIE taki sam złoty stroke i złotą poświatę jak
   `active`, więc ukończone zadanie wyglądało jak w trakcie robienia.

   ⚠️ DLACZEGO ZMIANA W CSS, A NIE W `getRouteStateFromTask`:
   stan `claimable` NIE jest tylko kolorem — niosą go liczniki (CLAIMABLE),
   etykieta „READY TO CLAIM", `data-ui-state`, bramki przycisków i legenda.
   Zmiana stanu na `done` skasowałaby informację „jest co odebrać" z całego UI.
   Tu zmieniamy WYŁĄCZNIE wygląd poświaty, zachowując semantykę stanu.

   ⚠️ `is-claimable` dostaje teraz DOKŁADNIE te same wartości co `is-done`
   (stroke #5ef5a8, poświata zielona, inner #14442c, label #c6ffe4), żeby
   ukończone zadanie było wizualnie JEDNOZNACZNE z odebranym. Złoty (#ffc247)
   zostaje zarezerwowany dla `is-active` — czyli jedynego stanu „w trakcie".
   ⚠️ `is-claimable` MUSI stać PO `is-done` w tym pliku: specyficzność obu
   selektorów jest identyczna (0,3,0), więc wygrywa późniejszy — a kolejność
   `is-done` -> `is-claimable` jest naturalna i czytelna.
   ⚠️ Bazowy `site.css` ma `is-claimable` już zielony; ten blok tylko
   sprowadza motyw tactical do tej samej palety (wcześniej się rozjeżdżały). */
body.theme-tactical .bp-route-node.is-claimable .bp-route-node-core {
  stroke: #5ef5a8;
  stroke-width: 0.5;
  filter: drop-shadow(0 0 1.3px rgba(94, 245, 168, 0.7));
}
body.theme-tactical .bp-route-node.is-claimable .bp-route-node-inner { fill: #14442c; }
body.theme-tactical .bp-route-node.is-claimable .bp-route-node-label { fill: #c6ffe4; }
body.theme-tactical .bp-route-node.is-open .bp-route-node-inner { fill: rgba(255, 150, 188, 0.9); }
body.theme-tactical .bp-route-node-brief {
  border-color: rgba(255, 77, 146, 0.5);
  background: rgba(10, 7, 13, 0.97);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 64, 129, 0.12);
}
body.theme-tactical .bp-route-node-brief-step { color: #ff9ecb; }
/* [CHANGE 2026-09-16 BP-LOCK-COLORS] Ten sam jezyk stanow co klocka w inspektorze
   zadania (pkt 1 usera): ZLOTA = dostepne/aktywne, ZIELONA = ukonczone,
   SZARA = jeszcze nieodblokowane. `data-state` ustawia updateRouteTaskPreview,
   `data-lock` = open|locked. */
body.theme-tactical .bp-route-node-brief-state { border-color: #3a2b45; color: #8a92a3; }
body.theme-tactical .bp-route-node-brief-state[data-lock="open"] {
  border-color: rgba(255, 194, 71, 0.55);
  color: #ffc247;
  box-shadow: 0 0 18px rgba(255, 194, 71, 0.22);
}
body.theme-tactical .bp-route-task-lock[data-lock="open"] {
  border-color: rgba(255, 194, 71, 0.5);
  color: #ffc247;
  box-shadow: 0 0 18px rgba(255, 194, 71, 0.2);
}
body.theme-tactical .bp-route-task-preview[data-state="done"] .bp-route-task-lock {
  border-color: rgba(94, 245, 168, 0.5);
  color: #5ef5a8;
  box-shadow: 0 0 18px rgba(94, 245, 168, 0.2);
}
body.theme-tactical .bp-route-task-preview[data-state="done"] .bp-route-task-state { color: #5ef5a8; }
body.theme-tactical .bp-route-task-preview[data-state="active"] .bp-route-task-lock {
  border-color: rgba(255, 194, 71, 0.5);
  color: #ffc247;
  box-shadow: 0 0 18px rgba(255, 194, 71, 0.2);
}
body.theme-tactical .bp-route-task-preview[data-state="locked"] .bp-route-task-lock {
  border-color: #3a2b45;
  color: #8a92a3;
  box-shadow: none;
}
body.theme-tactical .bp-route-task-preview[data-state="locked"] .bp-route-task-state { color: #8a92a3; }
body.theme-tactical .bp-route-node-brief-progress-bar { background: #000; border: 1px solid #3a2b45; }
body.theme-tactical .bp-route-node-brief-progress-fill { background: linear-gradient(90deg, #ff4081, #ff8ec0); }
body.theme-tactical .bp-route-task-tile { border-color: #2b2334; background: rgba(16, 11, 20, 0.92); }

/* [CHANGE 2026-09-15 BP-BRIEF-FLIP] Brief nad/pod pinezka: JS liczy wprost gorwy
   rog elementu (top = py-14-H albo py+16), wiec kotwica transformu musi byc STAŁA
   (tylno translateX srodkuje poziomo). bazowy site.css ma translate(-50%,-115%)
   w kilku regułach (is-visible) — nadpisujemy jedna linia z !important. */
body.theme-tactical .bp-route-node-brief {
  transform: translateX(-50%) !important;
}
/* Po odznaczeniu wezla: chowamy fikcyjne kafelki Item/Token (placeholderi). */
body.theme-tactical .bp-route-task-preview.is-empty .bp-route-task-tile.is-item,
body.theme-tactical .bp-route-task-preview.is-empty .bp-route-task-tile.is-skull,
body.theme-tactical .bp-route-task-preview.is-empty .bp-route-node-brief-reward-item,
body.theme-tactical .bp-route-task-preview.is-empty .bp-route-node-brief-reward-token {
  display: none;
}
body.theme-tactical .bp-route-task-preview.is-empty .bp-route-task-rewards-title {
  display: none;
}

/* [CHANGE 2026-09-15 BP-PURPLE] Klik knotka: gruba biala obwodka (0.85 w viewBox
   ~1000px = ~8px) + mocny glow wygladaly jak "powiekszenie". Zostaje subtelny,
   rozowy sygnal zaznaczenia.
   [CHANGE 2026-09-15 BP-ZOOM-SOFT] User: efekt WCIĄZ jest za duzy. Przyczyna:
   `drop-shadow` w SVG liczy blur w JEDNOSTKACH viewBoxa (100x72 skalowany do
   ~1000px), wiec "4px" = ~40px realnych — halo wieksze od samego punktu.
   Zmniejszone: blur 4 -> 1.2 (realnie ~12px), krycie 0.45 -> 0.35, obwodka
   0.5 -> 0.42. Zaznaczenie pozostaje czytelne (jasny rozowy pit + lekka poswiata). */
/* [CHANGE 2026-09-15 BP-ZOOM-SOFT2] User: biala obwodka CIAGLE za duza. Zeszlismy
   z 0.42 -> 0.16 jednostek (realnie ~2px) i usunelismy glow (blur 1.2 -> 0.35).
   Zaznaczenie czytane po kolorze wypelnienia (inner), nie po obwodce. */
body.theme-tactical .bp-route-node:focus-visible .bp-route-node-core,
body.theme-tactical .bp-route-node:hover .bp-route-node-core {
  stroke: #ff8ec0;
  stroke-width: 0.46;
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-NODE-PURPLE] PRAWDZIWY powod "grubej bialej obwodki":
   regula `:hover` (powyzej) miala IDENTYCZNA specyficznosc jak `.is-selected`
   i stala PO NIEJ, wiec po kliknieciu (mysz zostaje nad punktem) wygrywala
   hover -> stroke #ffd9e8 (niemal biel) + inner #ffd9e8 = bialy srodek i
   nieczytelny numer. Dodatkowo base site.css mial #ffffff + white glow.
   FIX: selekcja ma WYZSZA specyficznosc (dodatkowy `.bp-route-panel`), jest
   zdefiniowana NA KONCU, a kolory sa jednoznacznie purpurowe:
     obwodka = #ff4d92 (magenta mapy), srodek = #4a1245 (gleboki furgonetka),
     numer = #ffe3f0 (jasny roz na ciemnym tle = czytelny).
   ========================================================================== */
body.theme-tactical .bp-route-panel .bp-route-node.is-selected .bp-route-node-core {
  stroke: #ff4d92;
  stroke-width: 0.62;
  filter: drop-shadow(0 0 1.4px rgba(255, 64, 129, 0.7));
}
body.theme-tactical .bp-route-panel .bp-route-node.is-selected .bp-route-node-inner { fill: #4a1245; }
body.theme-tactical .bp-route-panel .bp-route-node.is-selected .bp-route-node-label { fill: #ffe3f0; }
body.theme-tactical .bp-route-node { outline: none; }

/* --- neutralne chipy "available/claimed" (byly chłodno-niebieskawe) --- */
body.theme-tactical .bp-task-track-chip { color: rgba(168, 162, 148, 0.85); border-color: rgba(168, 162, 148, 0.18); }
body.theme-tactical .bp-state-available { color: rgba(168, 162, 148, 0.75); border-color: rgba(168, 162, 148, 0.22); }
body.theme-tactical .bp-state-claimed { color: rgba(168, 162, 148, 0.5); border-color: rgba(168, 162, 148, 0.12); }
body.theme-tactical .bp-task-card.bp-task-state-claimed { border-left-color: rgba(168, 162, 148, 0.22); }
body.theme-tactical .bp-task-card.bp-task-state-available { border-left-color: rgba(168, 162, 148, 0.22); }
body.theme-tactical .bp-section-badge { background: rgba(168, 162, 148, 0.08); border-color: rgba(168, 162, 148, 0.18); color: #a8a294; }

/* --- teksty "bliskobiale chlodne" -> cieple biele --- */
body.theme-tactical .bp-globe-command-v,
body.theme-tactical .bp-globe-command-note { color: #efe9df; }
body.theme-tactical .bp-globe-command-note { color: #a8a294; }
body.theme-tactical .bp-globe-command { border-color: #222834; background: rgba(10, 11, 14, 0.8); }

/* --- ocieplenie resztkowych CHŁODNYCH etykiet BP (slate/cyan -> amber/ciepły szary) --- */
body.theme-tactical .bp-kicker { color: var(--signal); }
body.theme-tactical .bp-timer-label,
body.theme-tactical .bp-stat-label,
body.theme-tactical .bp-command-k,
body.theme-tactical .bp-currency-k,
body.theme-tactical .bp-currency-note,
body.theme-tactical .bp-access-k,
body.theme-tactical .bp-access-note,
body.theme-tactical .bp-ops-label,
body.theme-tactical .bp-ops-note,
body.theme-tactical .bp-continent-status,
body.theme-tactical .bp-tasks-col-title { color: #a8a49b; }
body.theme-tactical .bp-currency-code { color: #c9a06e; }
/* [CHANGE 2026-09-15 BP-PURPLE] status odblokowanego kontynentu — róż palety mapy */
body.theme-tactical .bp-continent-card.is-unlocked .bp-continent-status { color: #ff9ecb; }
body.theme-tactical .bp-tasks-col-head .bp-tasks-col-title { color: #e7e2d8; }
body.theme-tactical .bp-access-chip { border-color: #2f3746; color: #a8a49b; }
body.theme-tactical .bp-access-chip.is-ready { color: #ffb066; border-color: rgba(255, 176, 102, 0.4); }
body.theme-tactical .bp-access-chip.is-off { color: #6f6a61; border-color: #262a33; }
body.theme-tactical .bp-route-node-brief-state { color: #a8a49b; }
body.theme-tactical .bp-globe-command-region { color: #c9a06e; }
body.theme-tactical .bp-loader-text { color: #d8d2c6; }
body.theme-tactical .bp-loader-subtext { color: #a8a49b; }

/* --- ostatnie resztki chlodnych etykiet (skan zywy v=f) --- */
body.theme-tactical .bp-tasks-reset { color: #8f8a80; }
body.theme-tactical .bp-tasks-reset:hover { color: var(--signal); }
body.theme-tactical .bp-quest-progress-text { color: #a8a49b; }
body.theme-tactical .bp-quest-meta-label { color: #8f8a80; }
body.theme-tactical .bp-quest-meta-value { color: #d8d2c6; }
body.theme-tactical .bp-continent-progress { color: #a8a49b; }
body.theme-tactical .bp-continent-progress strong { color: #e7e2d8; }
body.theme-tactical .bp-ops-tile small { color: #a8a49b; }
body.theme-tactical .bp-infinite-k { color: #8f8a80; }
body.theme-tactical .bp-infinite-v { color: #efe9df; }
body.theme-tactical .bp-infinite-card.is-active .bp-infinite-v { color: var(--signal); }

/* ============================================================================
   [CHANGE 2026-09-15 BP-UI] BATTLE PASS — nowy układ interface'u:
   (1) zakładki sekcji Continents | Tasks w miejscu paska "scanline",
   (2) HUD — scalony pas statusu (dawne command-strip + access-rail + ops-economy),
   (3) tor nagród DWUPASMOWY (FREE u góry, PREMIUM pod spodem), 100 poziomów,
   (4) większa ikona waluty eventu.
   Zasady jak reszta motywu: czerń warstwowa, hairline 1px, JEDEN amber #ff8a2b,
   Oswald (nagłówki/wartości) + IBM Plex Mono (etykiety/dane), zero glow.
   ========================================================================== */

/* ---- ZAKŁADKI (jak pasek zakładek u góry portalu) ---- */
body.theme-tactical .bp-tabs {
  display: flex;
  align-items: stretch;
  margin: 18px auto 0;
  border-bottom: 1px solid var(--hair);
}
body.theme-tactical .bp-tab {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-family: 'Rajdhani', sans-serif;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 12px 22px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: color .15s ease, border-color .15s ease;
}
body.theme-tactical .bp-tab:hover { color: var(--text); }
body.theme-tactical .bp-tab.is-active { color: var(--signal); border-bottom-color: var(--signal); }
body.theme-tactical .bp-tab-badge {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  line-height: 1;
  padding: 3px 6px;
  color: var(--ink);
  background: var(--signal);
}
body.theme-tactical .bp-tab-panel { padding-top: 4px; }
body.theme-tactical .bp-tab-panel[hidden] { display: none !important; }

/* ---- HUD (scalony pas: core | season | tasks) ---- */
body.theme-tactical .bp-hud {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1.5fr) minmax(0, 1fr);
  gap: 1px;
  margin-top: 16px;
  background: var(--hair);
  border: 1px solid var(--hair);
}
body.theme-tactical .bp-hud-group {
  background: var(--panel);
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 22px;
}
body.theme-tactical .bp-hud-core,
body.theme-tactical .bp-hud-tasks { justify-content: space-between; }
body.theme-tactical .bp-hud-season { flex-direction: column; align-items: stretch; gap: 8px; }
body.theme-tactical .bp-hud-stat { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
body.theme-tactical .bp-hud-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-hud-v {
  font-family: 'Oswald', sans-serif;
  font-size: 1.12rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text);
}
body.theme-tactical .bp-hud-tier.is-full { color: var(--signal); }
body.theme-tactical .bp-hud-tier.is-trial { color: #a8a49b; }
body.theme-tactical .bp-hud-claimable { color: #ffb066; }
body.theme-tactical .bp-hud-season-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
body.theme-tactical .bp-hud-season-pct { font-family: 'Oswald', sans-serif; font-size: 1.02rem; color: var(--signal); }
body.theme-tactical .bp-hud .bp-command-bar { margin: 0; }

/* ---- TOR NAGRÓD (2 pasma × 100 poziomów, przewijany poziomo) ---- */
body.theme-tactical .bp-track { padding: 0 0 26px; }
body.theme-tactical .bp-track-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin: 26px 0 12px;
}
body.theme-tactical .bp-track-head-left { display: flex; flex-direction: column; gap: 8px; }
body.theme-tactical .bp-track-head .bp-section-title { margin: 0; }
body.theme-tactical .bp-track-legend { display: flex; gap: 14px; }
body.theme-tactical .bp-track-legend-item {
  position: relative;
  padding-left: 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-track-legend-item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 6px;
  height: 6px;
  margin-top: -3px;
  background: var(--hair-2);
}
body.theme-tactical .bp-track-legend-item.is-free::before { background: #cfd6e2; }
body.theme-tactical .bp-track-legend-item.is-premium::before { background: var(--signal); }

body.theme-tactical .bp-track-xp { min-width: 260px; text-align: right; }
body.theme-tactical .bp-track-xp-row { display: flex; align-items: baseline; justify-content: flex-end; gap: 8px; }
body.theme-tactical .bp-track-xp-k { font-family: 'IBM Plex Mono', monospace; font-size: 0.62rem; letter-spacing: 0.14em; color: var(--muted); }
body.theme-tactical .bp-track-xp-v { font-family: 'Oswald', sans-serif; font-size: 1.24rem; font-weight: 600; color: var(--text); }
body.theme-tactical .bp-track-xp-bar { height: 3px; margin: 6px 0 5px; background: var(--hair); overflow: hidden; }
body.theme-tactical .bp-track-xp-fill { display: block; height: 100%; background: var(--signal); }
/* ═══ [CHANGE 2026-09-25 BP-R87-NEXT-REWARD-FONT] WIĘKSZA CZCIONKA „Next reward" ═══
   ZGLOSZENIE (R87, pkt 2): „Reward Track: Chce zebys swiekszył czcionkę dla:
   Next reward: Free (nagroda)... Premium (nagroda)".
   BYŁO `0.62rem` — ta sama wielkość co drobne podpisy pomocnicze (`.bp-track-xp-k`),
   a ten wiersz niesie KONKRETNĄ informację o najbliższej nagrodzie, więc ginął
   w hierarchii. JEST `0.86rem` (+39%), z `line-height` ustawionym jawnie, bo
   dłuższe nazwy („Atlas Domination Skin Box") łamią się teraz na dwie linie.
   ⚠️ NIE zwiększam `min-width` kontenera `.bp-track-xp` (260 px): przy szerszym
   tekście wiersz ma się ZAWIJAĆ, a nie rozpychać nagłówek toru. */
body.theme-tactical .bp-track-xp-next {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.86rem;
  line-height: 1.45;
  letter-spacing: 0.02em;
  color: #b9b3a8;
}
/* `strong` to etykiety FREE / PREMIUM — wyróżnione jaśniejszym odcieniem, żeby
   po powiększeniu całości nadal było widać, gdzie kończy się jedna linia nagród. */
body.theme-tactical .bp-track-xp-next strong { color: #ece7de; font-weight: 700; letter-spacing: 0.04em; }

body.theme-tactical .bp-track-scroll {
  border: 1px solid var(--hair);
  background: var(--ink-2);
  overflow-x: auto;
  overflow-y: hidden;
  padding: 12px 10px;
  scrollbar-width: thin;
  scrollbar-color: var(--hair-2) transparent;
}
body.theme-tactical .bp-track-grid { display: flex; gap: 2px; min-width: max-content; }

/* kolumna jednego poziomu — nadpisuje .bp-rail-node z site.css (56px/flex-center) */
body.theme-tactical .bp-rail-node.bp-track-col {
  width: 104px;
  flex: 0 0 104px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  cursor: default;
}
body.theme-tactical .bp-track-lvl {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--muted);
  text-align: center;
  padding: 2px 0 6px;
  /* [CHANGE 2026-09-25 BP-R87-CONTINUITY-LINE] 1 px -> 2 px.
     ZGLOSZENIE (R87, pkt 3): „dodatkowo ta linia mogłaby być o 1 pixel grubsza".
     Grubość MUSI być ustawiona w regule BAZOWEJ, a nie tylko w regule `.reached`:
     inaczej odcinki osiągniętych i nieosiągniętych poziomów miałyby RÓŻNĄ
     wysokość i numery poziomów przestałyby stać w jednej linii (dokładnie ta
     klasa błędu, którą naprawiał BP-R29d-TRACK-UNIFORM przy kafelkach).
     ⚠️ `border-bottom` (shorthand) ustawia też `style` i `color` — dlatego
     reguły niżej zmieniają wyłącznie `border-bottom-color`, a nie cały shorthand. */
  border-bottom: 2px solid var(--hair);
}
/* ═══ [CHANGE 2026-09-25 BP-R87-CONTINUITY-LINE] KOLOR PASA CIĄGŁOŚCI WEDŁUG POSIADANEGO BP ═══
   ZGLOSZENIE (R87, pkt 3): „Reward Track chce zeby kolor pomarańczowy nad
   kafelkiem (chodzi mi o efekt ciągłosci jak mamy wbite 20 poziomow to ten
   pomaranczowy kolorek jest do kafelka 20 (nad nim)) To jesli mamy battlepassa
   free to zeby on był badziej taki białawy (ale nie czysto biały) a jak mamy
   premium to żeby był on pomarańczowy".

   CO TO JEST „POMARAŃCZOWY NAD KAFELKIEM": `border-bottom` numeru poziomu leży
   DOKŁADNIE między numerem a kafelkiem i dla osiągniętych poziomów ma kolor
   `--signal-dim`. Ponieważ każda kolumna rysuje własny odcinek, wizualnie powstaje
   JEDEN pas biegnący od poziomu 1 do aktualnego — to opisywana „ciągłość".

   ⚠️ KOLOR ZALEŻY OD `wallet.premium`, NIE OD `prof.premium`. Uprawnienie premium
   żyje w `op_wallets` i to jego sprawdza serwer (`claimLevelReward` zwraca 403 bez
   premium) — widok używa już `trackHasPremium` przy liczeniu CLAIM ALL. Dwie różne
   flagi premium na jednej stronie to ten sam rozjazd, który zakazuje zasada
   „jedno źródło prawdy".
   ⚠️ Wersja darmowa: `#d3d9e4` — biały złamany w stronę chłodnej szarości.
     Czysta biel (`#ffffff`) w tym motywie jest zarezerwowana dla tekstu nagłówków,
     więc użyta tu przebijałaby hierarchię i pas wyglądałby jak przypadkowy artefakt.
   ⚠️ Specyficzność reguł tieru to (0,6,1) wobec (0,4,1) reguły `.reached` wyżej,
     więc wygrywają niezależnie od kolejności w pliku. */
body.theme-tactical .bp-track.is-free .bp-rail-node.bp-track-col.reached .bp-track-lvl {
  color: #d3d9e4;
  border-bottom-color: #d3d9e4;
}
body.theme-tactical .bp-track.is-premium .bp-rail-node.bp-track-col.reached .bp-track-lvl {
  color: var(--signal);
  border-bottom-color: var(--signal);
}

body.theme-tactical .bp-track-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 6px;
  padding: 9px 9px 8px;
  border: 1px solid var(--hair);
  background: var(--panel);
}
/* ⚠️ WYSOKOSC kafelka ustawia DOPIERO blok „BP-REWARDS" nizej (linia ~2740).
   Nie ustawiaj tu `min-height`: oba bloki maja ten sam selektor, wiec pozniejszy
   wygrywa, a rozjazd dwoch wartosci bylby dokladnie ta klasa bledu, ktora
   powodowala nierowne kafelki (`min-height` + `height` to DWIE wlasciwosci). */
/* Klodka zablokowanego poziomu — prawy gorny rog kafelka (obu pasow). */
body.theme-tactical .bp-track-corner-lock {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #b9a99c;
  background: rgba(6, 8, 12, 0.72);
  border: 1px solid var(--hair);
  z-index: 2;
}
body.theme-tactical .bp-track-corner-lock svg { width: 13px; height: 13px; }
/* [CHANGE 2026-09-15 BP-TRACK-FRAME] Usuniete pionowe/poprzeczne pomaranczowe kreski
   miedzy komorkami premium. Caly pas premium obramowany po osi X (gora + dol linia). */
body.theme-tactical .bp-track-cell.is-free { border-left: 1px solid var(--hair); }
body.theme-tactical .bp-track-cell.is-premium {
  border-left: 1px solid var(--hair);
  border-top: 2px solid var(--signal-dim);
  border-bottom: 2px solid var(--signal-dim);
}
body.theme-tactical .bp-track-cell.is-locked { opacity: 0.42; }
body.theme-tactical .bp-track-cell.is-claimed { background: #0b0e13; }
body.theme-tactical .bp-track-cell.is-noaccess { opacity: 0.5; }
body.theme-tactical .bp-track-reward {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  line-height: 1.35;
  letter-spacing: 0.02em;
  color: #cfd6e2;
  word-break: break-word;
}
body.theme-tactical .bp-track-cell.is-claimed .bp-track-reward { color: var(--muted); text-decoration: line-through; }
body.theme-tactical .bp-track-cell .bp-claim-level {
  width: 100%;
  padding: 4px 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
body.theme-tactical .bp-track-claimed,
body.theme-tactical .bp-track-lock {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: center;
  color: var(--muted);
}
body.theme-tactical .bp-track-claimed { color: var(--signal); }
body.theme-tactical .bp-track-lock { display: inline-flex; align-items: center; justify-content: center; min-height: 18px; }

/* ---- Event currency: większa ikona (46 -> 72 px) ---- */
body.theme-tactical .bp-currency-icon { width: 72px; height: 72px; }
body.theme-tactical .bp-currency-fallback svg { width: 40px; height: 40px; }

/* ---- [CHANGE 2026-09-15 BP-HERO] Hero: większe chipy + okienko w naszym stylu ---- */
body.theme-tactical .bp-hero-meta { gap: 10px; margin-top: 15px; }
body.theme-tactical .bp-hero-chip {
  min-height: 30px;
  padding: 7px 15px;
  font-size: 0.82rem !important;
  letter-spacing: 0.15em;
  border-radius: 0;
  color: #ece6dc;
  background: rgba(9, 11, 15, 0.72);
  border-color: rgba(255, 138, 43, 0.42) !important;
}
body.theme-tactical .bp-chip-season {
  color: #ffb066;
  background: rgba(255, 138, 43, 0.1);
  border-color: rgba(255, 176, 102, 0.5) !important;
}
/* Okienko hero (premium CTA + countdown + OP //): czerń warstwowa, hairline, zero glow/radius */
body.theme-tactical .bp-hero-timer {
  border-radius: 0;
  border: 1px solid var(--hair-2);
  background: var(--panel);
  box-shadow: none;
  backdrop-filter: none;
  padding: 16px 18px;
  gap: 8px;
  min-width: 264px;
}
body.theme-tactical .bp-premium-cta { box-shadow: none; }
/* [CHANGE 2026-09-15 BP-HERO2] Cena z przekresleniem: "for ~~20 EUR~~ 10 EUR". */
body.theme-tactical .bp-premium-price {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #c9a06e;
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
}
body.theme-tactical .bp-price-old {
  /* [CHANGE 2026-09-16] Przekreslone 20 EUR bylo za blade i za malo widoczne.
     Teraz: wyrazny szary, tylko odrobine mniejszy niz 10 EUR. */
  color: #b7b0a6;
  text-decoration: line-through;
  text-decoration-color: rgba(255, 77, 146, 0.75);
  text-decoration-thickness: 2px;
  font-weight: 600;
  font-size: 0.92rem;
}
body.theme-tactical .bp-price-new {
  color: #ffd9e8;
  font-weight: 700;
  font-size: 1.06rem;
  letter-spacing: 0.04em;
}
body.theme-tactical .bp-timer-label { color: #8f8a80; }
body.theme-tactical .bp-timer-value { color: #ffb066; }

/* ---- [CHANGE 2026-09-15 BP-REWARDS] Tor: większa siatka + ikony nagród ---- */
body.theme-tactical .bp-rail-node.bp-track-col {
  /* [CHANGE 2026-09-20 BP-R29d-TRACK-UNIFORM] 158 -> 182 px. User: „Powieksz
     jeszcze troche wszystkie kafelki - zadbaj zeby wielkosc kazdego byla
     jednakowa". Szersza kolumna = dluzsze nazwy nagrod mieszcza sie w mniejszej
     liczbie linii, wiec kafelki sa bardziej wyrownane takze wizualnie. */
  width: 182px;
  flex: 0 0 182px;
}
body.theme-tactical .bp-track-lvl { font-size: 0.9rem; padding: 3px 0 8px; }

/* ---- [CHANGE 2026-09-15 BP-TRACK-XP] Pasek XP nad kolumna aktualnego poziomu ----
   Placeholder o stalej wysokosci jest w kazdej kolumnie (patrz battlepass.ejs),
   ale widoczny tylko z klasa .is-active -> numery poziomow sie nie rozjezdzaja.
   Szerokosc = szerokosc kafelka (100% kolumny = 134px). */
body.theme-tactical .bp-track-xpinline {
  height: 16px;
  margin: 0 0 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  visibility: hidden;
}
body.theme-tactical .bp-track-xpinline.is-active { visibility: visible; }
body.theme-tactical .bp-track-xpinline-bar {
  position: relative;
  display: block;
  width: 100%;
  height: 14px;
  border: 1px solid var(--hair-2);
  border-radius: 3px;
  background: #0a0d12;
  overflow: hidden;
}
body.theme-tactical .bp-track-xpinline-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  background: linear-gradient(90deg, rgba(255, 138, 43, 0.88), rgba(255, 176, 102, 0.96));
  box-shadow: 0 0 8px rgba(255, 138, 43, 0.45);
}
body.theme-tactical .bp-track-xpinline-txt {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.5rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #ffe8d2;
  text-shadow: 0 0 3px rgba(0, 0, 0, 0.92);
  white-space: nowrap;
}
/* ═══ [CHANGE 2026-09-20 BP-R29d-TRACK-UNIFORM] STALA WYSOKOSC KAFELKA ═══
   ZGLOSZENIE: „zadbaj zeby wielkosc kazdego byla jednakowa: teraz te ktore mam
   zaliczone i moze claim nagrode - sa wieksze - dodatkowo z jakiegos powodu
   lvl 65 tez jest nieco wiekszy".

   PRZYCZYNA: bylo `min-height: 150px`, czyli WYSOKOSC MINIMALNA (nie stala).
   Kafelek rosl powyzej niej, gdy dochodzil mu content:
     • poziom DO ODEBRANIA ma PRZYCISK „CLAIM" (~34 px), poziom odebrany ma napis
       „CLAIMED" (~14 px), a poziom ZABLOKOWANY nie ma tam NICZEGO — trzy rozne
       wysokosci stopki daja trzy rozne wysokosci kafelka;
     • dluzsza nazwa nagrody (np. „Atlas Domination Skin Box" na lvl 65) zawijala
       sie do dodatkowej linii i rozciagala kafelek — stad „lvl 65 jest wiekszy".

   JEST: `height` (STALA) + `min-height: 0`, wiec wysokosc NIE zalezy od contentu
   i wszystkie kafelki sa identyczne. 196 px = 150 (dawne minimum) + miejsce na
   stopke i wieksza ikone, wiec nic nie jest obcinane.
   ⚠️ `min-height: 0` MUSI byc tutaj: `height` i `min-height` to DWIE ROZNE
   wlasciwosci, a wygrywa wieksza — bez wyzerowania `min-height` z bloku
   podstawowego kafelek moglby urosnac do 150 px i nadpisac `height`.
   ⚠️ To JEDYNE miejsce z wysokoscia kafelka. Nie dodawaj `min-height` wyzej. */
body.theme-tactical .bp-track-cell {
  height: 196px;
  min-height: 0;
  padding: 11px 11px 10px;
  gap: 8px;
}
body.theme-tactical .bp-track-reward {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  /* ⚠️ `min-height: 0` + `overflow: hidden` w parze ze STALA wysokoscia kafelka:
     dluga nazwa nie rozpycha juz siatki, tylko przycina sie w swoim obszarze.
     Bez tego dluzsza etykieta znowu zmienialaby wysokosc kafelka. */
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
body.theme-tactical .bp-reward-ico {
  /* slot ikony: ciemna grafika (event currency = hełm) potrzebuje tła i hairline,
     inaczej ginie na czerni kafelka.
     [CHANGE 2026-09-18 BP-R27E-5-REWARD-ICON] 54px -> 72px (grafika ~62px).
     User: „W reward track - chcialbym zeby wszystkie obrazki przedmiotów były
     troche większe".
     ⚠️ Zmiana TYLKO tutaj działa na cały tor, bo oba pasma (darmowe i premium)
     renderują ikonę tą samą klasą `.bp-reward-ico` — nie ma drugiego miejsca
     z rozmiarem, które trzeba by zgubić. Wysokość kafelka jest STAŁA
     (`height: 196px`), więc większa ikona nie rozepchnie siatki poziomów.
     [CHANGE 2026-09-20 BP-R29d-TRACK-UNIFORM] 72 -> 84 px + stała wysokość
     slotu: ikony o różnych proporcjach (kwadratowe granaty vs szerokie rendery
     broni) mają teraz IDENTYCZNY slot, więc etykiety pod nimi startują w tej
     samej linii na każdym kafelku. */
  width: 84px;
  height: 84px;
  padding: 5px;
  object-fit: contain;
  background: var(--panel-2);
  border: 1px solid var(--hair);
  border-radius: 0;
  flex-shrink: 0;
}
body.theme-tactical .bp-track-reward-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.25;
  color: #f4f4f2;
  /* Maksymalnie 3 linie nazwy + wielokropek. Bez limitu dluga nazwa sciskalaby
     podpis z iloscia/czasem do zera przy stalej wysokosci kafelka. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* [CHANGE 2026-09-20 BP-R29d-TRACK-QTY] PODPIS ILOSCI / CZASU.
   Zgloszenie: „free i premium na poziomie 2 ma Atlas Smoke Grenade - ale nigdzie
   nie ma pokazanych ilosci [...] VIP Booster, XP Booster, Dollar booster - brak
   czasu [...] Bron atlasowa - brak czasu / opisu ze jest na czas lub na perma".
   Gracz widzial NAZWE nagrody i nie wiedzial, ILE jej dostaje ani NA JAK DLUGO —
   czyli nie mogl ocenic wartosci poziomu. Podpis jest celowo mniejszy i w kolorze
   przygaszonym: to informacja DRUGORZEDNA wobec nazwy, ale musi byc widoczna bez
   najechania myszka (w wersji mobilnej tooltipa nie ma wcale). */
body.theme-tactical .bp-track-reward-sub {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.2;
  color: #ffb066;
  white-space: nowrap;
}
body.theme-tactical .bp-track-cell .bp-claim-level {
  padding: 9px 8px;
  font-size: 0.76rem;
  letter-spacing: 0.12em;
}
body.theme-tactical .bp-track-claimed,
body.theme-tactical .bp-track-lock { font-size: 0.66rem; }
/* [CHANGE 2026-09-20 BP-R29d-TRACK-UNIFORM] STOPKA KAFELKA O STALEJ WYSOKOSCI.
   Trzy stany stopki mialy dotad TRZY rozne wysokosci: przycisk „CLAIM" (~34 px),
   napis „CLAIMED" (~14 px) i brak tresci na poziomie zablokowanym (0 px). Stad
   wzielo sie zgloszenie „te ktore mam zaliczone i moze claim nagrode - sa
   wieksze". Wyrownanie stopki sprawia, ze obszar nazwy nagrody ma w kazdym
   kafelku DOKLADNIE tyle samo miejsca, wiec etykiety startuja w tej samej linii. */
body.theme-tactical .bp-track-cell .bp-claim-level,
body.theme-tactical .bp-track-claimed {
  min-height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ---- responsywność HUD + toru ---- */
@media (max-width: 1080px) {
  body.theme-tactical .bp-hud { grid-template-columns: 1fr 1fr; }
  body.theme-tactical .bp-hud-tasks { grid-column: 1 / -1; }
}
@media (max-width: 700px) {
  body.theme-tactical .bp-hud { grid-template-columns: 1fr; }
  body.theme-tactical .bp-tabs { overflow-x: auto; }
  body.theme-tactical .bp-tab { padding: 10px 14px; letter-spacing: 0.1em; }
  body.theme-tactical .bp-track-xp { text-align: left; min-width: 0; }
  body.theme-tactical .bp-track-xp-row { justify-content: flex-start; }
  body.theme-tactical .bp-rail-node.bp-track-col { width: 132px; flex-basis: 132px; }
  body.theme-tactical .bp-hero-timer { min-width: 0; width: 100%; align-items: stretch; }
}

/* ============================================================================
   [CHANGE 2026-09-15 BP-CHESTS/BP-ACH] Zakładki CHESTS i ACHIEVEMENTS.
   ========================================================================== */
body.theme-tactical .bp-tab-badge-muted { background: var(--hair-2); color: var(--muted); }

/* ---- CHESTS: karty skrzyń + lista zawartości (szansa %) ---- */
body.theme-tactical .bp-chest-region { margin: 26px 0 4px; }
body.theme-tactical .bp-chest-region:first-of-type { margin-top: 10px; }
body.theme-tactical .bp-chest-region-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--hair);
}
body.theme-tactical .bp-chest-region-line {
  width: 3px;
  height: 18px;
  background: var(--signal-dim);
}
body.theme-tactical .bp-chest-region.is-unlocked .bp-chest-region-line { background: #ff4d92; }
body.theme-tactical .bp-chest-region-title {
  margin: 0;
  font-family: 'Oswald', sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}
body.theme-tactical .bp-chest-region-state {
  margin-left: auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.12em;
  color: var(--muted);
}
body.theme-tactical .bp-chest-region.is-unlocked .bp-chest-region-state { color: #ff9ecb; }
body.theme-tactical .bp-chest-region.is-locked { opacity: 0.62; }
body.theme-tactical .bp-chest-title-row { display: flex; align-items: center; gap: 10px; }
body.theme-tactical .bp-chest-art {
  /* [CHANGE 2026-09-16 BP-ICO-BIG] Ikony zawartosci byly za male (20px) —
     zwiekszone wyraznie dla zgodnego UX/UI. */
  width: 64px;
  height: 64px;
  object-fit: contain;
  flex-shrink: 0;
  background: var(--ink-2);
  border: 1px solid var(--hair);
  border-radius: 6px;
  padding: 4px;
}
body.theme-tactical .bp-chest-entry-name { display: inline-flex; align-items: center; gap: 10px; }
body.theme-tactical .bp-chest-entry-ico {
  width: 44px;
  height: 44px;
  object-fit: contain;
  flex-shrink: 0;
  background: var(--ink-2);
  border: 1px solid var(--hair);
  border-radius: 5px;
  padding: 3px;
}
body.theme-tactical .bp-chest-card.is-unavailable { opacity: 0.72; }
body.theme-tactical .bp-chest-locked-note {
  margin-top: auto;
  display: block;
  text-align: center;
  padding: 9px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  color: var(--muted);
  border: 1px dashed var(--hair-2);
}
/* [CHANGE 2026-09-16 BP-EU-GATE] Klocka premium (Golden Box bez BP premium). */
body.theme-tactical .bp-chest-locked-note.is-premium-gate {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #ff9ecb;
  border-color: rgba(255, 64, 129, 0.5);
  background: rgba(255, 64, 129, 0.08);
}
body.theme-tactical .bp-chest-locked-note.is-premium-gate svg { color: #ff9ecb; flex-shrink: 0; }
/* [CHANGE 2026-09-16 BP-CHEST-TIERS] Lista progow (korony/WF$/boostery) w skrzyni. */
body.theme-tactical .bp-chest-entry-tiers {
  list-style: none;
  margin: 8px 0 0;
  padding: 8px 0 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  border-top: 1px solid var(--hair);
}
body.theme-tactical .bp-chest-tier {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: center;
  gap: 9px;
}
body.theme-tactical .bp-chest-tier-ico { width: 26px; height: 26px; object-fit: contain; opacity: 0.9; }
body.theme-tactical .bp-chest-tier-amt {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.8rem;
  font-weight: 600;
  color: #f4f4f2;
}
body.theme-tactical .bp-chest-tier-pct {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: #ff9ecb;
}
/* [CHANGE 2026-09-20 BP-R30-CHEST-GRID] 4 SKRZYNIE = 100% SZEROKOSCI OSI X.
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R30, pkt 1): „Na kazdym kontynencie moga znajdowac sie maksymalnie
   4 skrzynki. [...] Zmien layout tak, aby 4 skrzynki zajmowaly 100% dostepnej
   szerokosci osi X. Jezel obecny kontener jest za waski, nalezy go odpowiednio
   rozszerzyc."

   BYLO: `repeat(auto-fill, minmax(320px, 1fr))`. Ta regula dobiera liczbe kolumn
   sama — przy szerokim kontenerze (do 1800px) wchodzilo 5 kolumn, przy waskim
   3, a czwarta skrzynia ladowala w nowym wierszu albo zostawiala pusta luke.
   Siatka NIE byla wiec zwiazana z liczba skrzyn (max 4) i nie wypelniala osi X.

   JEST: DOKLADNIE 4 kolumny rownej szerokosci (`repeat(4, minmax(0, 1fr))`).
   4 skrzynie zajmuja wtedy cala szerokosc kontenera co do piksela, bo
   `minmax(0, 1fr)` pozwala kolumnom zmiescic sie bez wymuszania 320px minimum
   (to wlasnie to minimum wypychalo kafle i tworzylo poziomy zapas/luke).
   `min-width: 0` na karcie jest OBOWIAZKOWE: bez niego dlugie nazwy przedmiotow
   (np. „Mega VIP Booster") rozciagnelyby kolumne siatki powyzej jej udzialu
   i znow rozjechalyby uklad.

   ⚠️ PONIZEJ 1180px siatka schodzi do 2 kolumn — 4 kolumny na 4 skrzynie
   wymagalyby ~1180px, zeby karta pozostala czytelna (nazwa + cena + zawartosc).
   To NIE jest odejscie od zgloszenia: zgloszenie dotyczy ukladu desktopowego
   („100% dostepnej szerokosci osi X"), a na mobile 4 kolumny bylyby nieczytelne.
   Reguly dla mniejszych szerokosci sa nizej, w odpowiednich @media. */
body.theme-tactical .bp-chests-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}
body.theme-tactical .bp-chests-grid > * { min-width: 0; }
/* ⚠️ KONTENER MUSI BYC SZEROKI — inaczej 4 kolumny sa ciasne, a nie „100% osi X".
   Sekcja Chests jest renderowana w `.shell-wide` (min(1800px, 98vw)), ale
   `site.css` liczy `.shell-wide` w `vw`, co przy widocznym pionowym pasku
   przewijania dawalo lekki poziomy zapas. `max-width: 100%` (jak w istniejacej
   regule dla `.topbar-inner`) zostawia kontener w granicach okna. */
body.theme-tactical .bp-chests { width: 100%; max-width: 100%; }
/* [CHANGE 2026-09-21 BP-R30-CHEST-MARGIN] LEKKI MARGINES PO BOKACH PANELU SKRZYŃ.
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R30, pkt 1): „W Skrzynkach zostaw lekki margines po lewej i prawej
   stronie".
   ⚠️ To NIE jest cofniecie poprzedniej poprawki („4 skrzynki = 100% szerokosci").
   Tamta dotyczyla SIATKI (ile miejsca zajmuja kafle w swoim kontenerze), a ta
   dotyczy KONTENERA (odstep kontenera od krawedzi okna). Obie dzialaja naraz:
   panel jest zwezony o `--bp-chest-gutter` z kazdej strony, a 4 kolumny nadal
   wypelniaja go w calosci.
   ⚠️ `padding` na `.bp-chests` zamiast `max-width` w `vw`: `.shell-wide` liczy
   szerokosc w `vw`, wiec zwezanie w `vw` rozjechaloby sie z paskiem przewijania.
   `padding` + `box-sizing: border-box` dziala niezaleznie od jednostki rodzica. */
body.theme-tactical .bp-chests {
  /* [CHANGE 2026-09-21 BP-R33-CHEST-MARGIN-REAL] MARGINES, KTORY NAPRAWDE DZIALA.
     ─────────────────────────────────────────────────────────────────────────
     ZGLOSZENIE (R33, pkt 2): „Ciągle skrzynie są za mocno rozciągnięte na
     stronie - margines jest za słaby".

     PRZYCZYNA — NIE WARTOSC, A DWIE REGULY. W tym pliku były DWA razy
     zdefiniowane `.bp-chests { --bp-chest-gutter: ... }`:
       • R32 (wyzej w pliku): `clamp(24px, 4vw, 72px)`
       • R31 (NIZEJ w pliku): `clamp(20px, 2.6vw, 56px)`
     Selektor i specyficznosc sa IDENTYCZNE, wiec wygrywala regula POZNIEJSZA —
     czyli mniejsza. Kazda „poprawka marginesu” z R31/R32 byla wiec kasowana
     przez starsza regule, ktora stala nizej. Dlatego zgloszenie wracalo.
     ⚠️ To NIE jest kwestia gustu — to ten sam blad, co dwa razy w tym samym
     pliku: „dwie reguly o tym samym selektorze to jedna regula ukryta”.
     Stara regula R31 zostala USUNIETA (patrz nizej), a wartosc jest teraz
     w JEDNYM miejscu i wyraznie wieksza.
     ⚠️ Gorna granica 140px chroni ultra-szerokie ekrany przed rozjechaniem
     siatki 4 kolumn; dolna 40px dziala na telefonie. */
  --bp-chest-gutter: clamp(40px, 6vw, 140px);
  padding-left: var(--bp-chest-gutter);
  padding-right: var(--bp-chest-gutter);
  box-sizing: border-box;
}
/* [CHANGE 2026-09-21 BP-R33-CHEST-MARGIN-REAL] USUNIETA DRUGA REGULA `.bp-chests`.
   ────────────────────────────────────────────────────────────────────────────
   Stala TUTAJ (dodana w R31) i — mimo ze byla starsza — wygrywala, bo w CSS przy
   jednakowym selektorze rozstrzyga kolejnosc w pliku, a nie data zmiany.
   Efekt: kazdy pozniejszy „wiekszy margines" (R32, R33) byl po cichu kasowany.
   Wartosc `--bp-chest-gutter` jest teraz zdefiniowana DOKLADNIE RAZ, w bloku
   BP-R33-CHEST-MARGIN-REAL powyzej. NIE PRZYWRACAJ tu drugiej definicji. */

/* Ten sam odstep dla sekcji ze skrzyniami z zadan („FROM TASKS"), zeby obie
   czesci panelu mialy rowna linie — inaczej naglowki by sie nie zgadzaly. */
body.theme-tactical .bp-chest-region { margin-left: 0; margin-right: 0; }
@media (max-width: 1180px) {
  body.theme-tactical .bp-chests-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  body.theme-tactical .bp-chests-grid { grid-template-columns: minmax(0, 1fr); }
}
body.theme-tactical .bp-chest-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  background: var(--panel);
  border: 1px solid var(--hair);
  border-top: 2px solid var(--signal-dim);
}
body.theme-tactical .bp-chest-head { display: flex; flex-direction: column; gap: 3px; }
body.theme-tactical .bp-chest-k,
body.theme-tactical .bp-chest-contents-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-chest-title {
  font-family: 'Oswald', sans-serif;
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--text);
}
body.theme-tactical .bp-chest-cost {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: var(--ink-2);
  border: 1px solid var(--hair);
}
body.theme-tactical .bp-chest-cost-ico { width: 28px; height: 28px; object-fit: contain; flex-shrink: 0; }
body.theme-tactical .bp-chest-cost-v { font-family: 'Oswald', sans-serif; font-size: 1.14rem; color: #ffb066; }
body.theme-tactical .bp-chest-cost-code { font-family: 'IBM Plex Mono', monospace; font-size: 0.62rem; letter-spacing: 0.1em; color: var(--muted); }
body.theme-tactical .bp-chest-contents { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
body.theme-tactical .bp-chest-entry-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
/* [CHANGE 2026-09-21 BP-R31-ENTRY-ONE-COLOR] JEDEN KOLOR CZCIONKI W WIERSZU.
   ─────────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R31, pkt 3): „skrzynia atlas basic box - atlas gear (temporary
   ma dwa kolory czcionek - chce ten bielszy".

   ZBADANE: nazwa (`<span class="bp-chest-entry-label">`) dziedziczy kolor
   z `.bp-chest-entry-name` (`#cfd6e2` — chlodna szarosc), a szansa obok ma
   `#a8a49b` (cieply szary). Dwa rozne odcienie w jednej linii wygladaja jak
   blad drukarski — user nazywa to „dwoma kolorami czcionek".
   ⚠️ Tylko karta ATLAS Gear to pokazywala, bo jako jedyna nie ma zadnej klasy
   rzadkosci (`is-rare`/`is-ultra`/`is-gold`), ktora by kolor nadpisala — reszta
   wierszy dostawala jednolity kolor z reguly rzedkosci.
   JEST: nazwa uzywa tego samego `#cfd6e2` co typowo (jaśniejszy z dwóch), a
   szansa przejmuje ten odcien — caly wiersz jest jednolity. */
body.theme-tactical .bp-chest-entry-label {
  color: #cfd6e2;
  /* ═══ [FIX 2026-09-25 BP-R86-CHEST-LABEL-WRAP] PEŁNA NAZWA, NIE WIELOKROPEK ═══
     ─────────────────────────────────────────────────────────────────────────
     ZNALEZIONE PRZEZ AUDYT R86: reguła zawijania dla tej klasy istniała TYLKO
     w bloku `@media (max-width: 620px)` (linia ~7505). Na DESKTOPIE nazwa była
     zwykłym `inline` bez `min-width: 0`, więc ulegała domyślnemu zachowaniu flex
     — długa nazwa („Atlas Advanced Weapons Box") była ściskana/ucinana zamiast
     się zawinąć dokładnie tam, gdzie jest najwięcej miejsca.
     ⚠️ To ta sama intencja co R27e-6 („pełne nazwy nagród, bez wielokropka"),
     ale zaaplikowana połowicznie: ktoś dodał poprawkę dla wąskich ekranów
     i uznał temat za zamknięty. `zawija=false min-width-0=false` w smoke.
     ⚠️ `min-width: 0` jest WARUNKIEM, nie kosmetyką — bez niego dziecko flex
     nie może być węższe niż jego treść (ta sama pułapka co przy
     `.bp-task-item-name` i `.bp-route-node-item-meta strong`). */
  min-width: 0;
  overflow-wrap: anywhere;
}
body.theme-tactical .bp-chest-entry-chance { font-family: 'IBM Plex Mono', monospace; font-size: 0.68rem; color: #cfd6e2; }
body.theme-tactical .bp-chest-entry-bar { display: block; height: 3px; margin-top: 4px; background: var(--hair); }
body.theme-tactical .bp-chest-entry-fill { display: block; height: 100%; background: var(--signal); }
/* [CHANGE 2026-09-21 BP-R36c-OPEN-BTN-BIGGER] WIĘKSZA CZCIONKA + IKONA WALUTY.
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE: „na buttonach OPEN FOR X SKULLS - zwiększyć czcionkę - a zamiast
   slowa Skulls dac ikonke czaszek".
   ⚠️ DLACZEGO JAWNY `font-size`, A NIE POLEGANIE NA `.btn`:
   `btn-sm` (z `site.css`, ładowanego PRZED tym plikiem) ustawia 0.75rem, a
   `body.theme-tactical .btn` w tym pliku — 0.8rem. Efektywny rozmiar zależał
   więc od KOLEJNOŚCI arkuszy i specyficzności selektorów, czyli był kruchy:
   przeniesienie reguły albo dołożenie arkusza zmieniłoby wygląd bez związku
   z tą zmianą. Tutaj rozmiar jest podany wprost.
   ⚠️ `letter-spacing` zmniejszony z 0.14em (odziedziczone po `.btn`): przy
   większej czcionce i dłuższej treści („OPEN FOR 1 200 000" + ikona) szerokie
   rozstrzelenie rozpychało przycisk i łamało go na dwie linie.
   ⚠️ `gap` + `align-items: center` ustawiają liczbę i ikonę w jednej linii,
   wyśrodkowanej w pionie — bez tego ikona (element zastępowany) siada na
   linii bazowej i wygląda jak opuszczona względem liczby.
   ⚠️⚠️ JEDNA REGUŁA, NIE DWIE. Wcześniej `margin-top: auto` było w osobnej
   regule `body.theme-tactical .bp-open-chest { … }` wyżej — czyli w arkuszu
   istniały DWIE reguły na ten sam selektor. To nie tylko bałagan: bramka P10
   szukała bloku po `indexOf(selektor)` i trafiała w PIERWSZĄ (tę z samym
   marginesem), więc nie widziała ani `font-size`, ani `gap` i zgłaszała brak
   zmiany, która była w pliku. Scalone do jednego bloku. */
body.theme-tactical .bp-open-chest {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-size: 1rem;
  letter-spacing: 0.08em;
  white-space: nowrap;
}
body.theme-tactical .bp-open-chest .bp-open-cost-v { font-size: 1.06rem; font-weight: 700; }
body.theme-tactical .bp-open-chest .bp-open-cost-ico {
  /* ⚠️ [CHANGE 2026-09-21 BP-R37-OPEN-ICON-BIGGER] WIĘKSZA IKONA WALUTY.
     ZGLOSZENIE (pkt 3): „Chce zeby czaszki przy open for X czaszek były większe
     (battlepass-chests)".
     BYŁO 20x20 px — przy czcionce 16 px ikona była WYŻSZA od cyfr, ale wizualnie
     drobna (a to ona niesie informacje „czym płacę"), a przy ciemnej grafice
     moździerza na złotym tle ginęła.
     JEST 26x26 px — czyli ~1.6 rozmiaru czcionki. Więcej niż 30 px zaczynało
     rozpychać przycisk („OPEN FOR 1 200 000" + ikona) i łamać go na dwie linie.
     ⚠️ Atrybut `width`/`height` w HTML też jest podniesiony na 26 — gdyby CSS
     się nie wczytał (albo gracz miał stary arkusz z cache), ikona i tak będzie
     właściwego rozmiaru. Dwa źródła tej samej liczby są tu CELOWE: awaryjne.
     ⚠️ `vertical-align: middle` NIE jest potrzebne — kontener jest `inline-flex`
     z `align-items: center` (patrz reguła wyżej), a dodanie go tutaj mogłoby
     przesunąć ikonę względem tekstu w zależności od przeglądarki. */
  width: 26px;
  height: 26px;
  object-fit: contain;
  flex-shrink: 0;
}
/* ⚠️ [CHANGE 2026-09-21 BP-R37-OPEN-ALWAYS-PRICE] „NOT ENOUGH" JAKO TEKST DLA
   CZYTNIKA EKRANU, A NIE DRUGA LINIA PRZYCISKU.
   Cena i waluta muszą być widoczne ZAWSZE (patrz komentarz w widoku), ale
   przycisk nie ma miejsca na drugą linię, a `title` nie jest czytany przez
   czytniki ekranu jako treść. Dlatego komunikat jedzie w tym span-ie, wizualnie
   ukrytym — dostępny, nie zajmuje miejsca. */
body.theme-tactical .bp-open-chest .bp-open-short {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* Zablokowany przycisk pozostaje czytelny — bez tego `opacity: 0.45` z reguły
   niżej gasi też cenę, a to wtedy JEDYNA informacja potrzebna graczowi. */
body.theme-tactical .bp-open-chest.is-short .bp-open-cost-label { opacity: 0.75; }
body.theme-tactical .bp-open-chest[disabled] { opacity: 0.45; cursor: not-allowed; }
/* ⚠️ Zablokowany przycisk NIE MOŻE mieć `opacity` tak niskiej, żeby cena stała
   się nieczytelna — dlatego podnosimy ją dla wariantu `is-short` (nie stać gracza).
   Kolejność ma znaczenie: ta reguła MUSI być PO regule `[disabled]`. */
body.theme-tactical .bp-open-chest.is-short[disabled] { opacity: 0.62; }
/* [CHANGE 2026-09-18 BP-R27E-5-REWARD-BOX] KARTA SKRZYNI Z ZADANIA.
   Wizualnie odroznia sie od skrzyn kupowanych i to jest informacja, nie ozdoba:
     * zloty pasek u gory = NAGRODA (nie towar w sklepie),
     * brak bloku ceny — nie da sie jej kupic za Skulle, wiec pokazywanie
       „0 SKULLS" byloby nieprawda,
     * przycisk dosuniety do dolu (`margin-top: auto`) DOKLADNIE tak samo jak
       `bp-open-chest` — w siatce `bp-chests-grid` karty maja rozna wysokosc
       (liczba pozycji), a przyciski na roznych poziomach wygladaja jak blad
       ukladu strony. */
body.theme-tactical .bp-chest-card-reward { border-top-color: #d8b266; }
body.theme-tactical .bp-chest-card-reward .bp-chest-k { color: #d8b266; }
body.theme-tactical .bp-open-reward-box { margin-top: auto; }
body.theme-tactical .bp-open-reward-box[disabled] { opacity: 0.45; cursor: not-allowed; }
body.theme-tactical .bp-open-reward-box .bp-chest-contents-count { margin-left: 4px; }
body.theme-tactical .bp-chest-card-reward .bp-chest-contents-all .bp-chest-contents-count { margin-left: 0; }

/* [CHANGE 2026-09-21 BP-R32-GUARANTEE-BAR-VIEW] PASEK LICZNIKA GWARANCJI W SKRZYNIACH.
   Ten sam jezyk wizualny co pasek w sekcji „Guaranteed Reward" modalu
   (`.bp-guarantee-progress`) — gracz ma rozpoznac, ze to TEN SAM licznik,
   a nie dwie rozne mechaniki. Bursztyn = gwarancja zlota/najrzadsza. */
/* [CHANGE 2026-09-21 BP-R34-CHEST-GOLD-BAR] WIERSZ GWARANCJI ZLOTA W KARCIE SKRZYNI.
   ─────────────────────────────────────────────────────────────────────────
   ZASTEPUJE pasek sekcyjny `#bp-chest-guarantee-bar` (R32/R33), ktory stal NAD
   siatka skrzyń. Zgloszenie R34 pkt 1 wskazalo miejsce wprost: POD licznikiem
   `Guaranteed rare in X` — a ten licznik jest w KAZDEJ karcie skrzyni.
   ⚠️ SIATKA 1:1 JAK `.bp-chest-pity` (auto | pasek | wartosc) — gracz ma
   rozpoznac „to ta sama mechanika, tylko zlota", wiec uklad przejmujemy w calosci.
   ⚠️ Bursztyn celowo: na tej stronie zloto = gwarancja zlota (ten sam odcień co
   `.bp-chest-pity.is-ready`, `.bp-reel-tag.is-gold`). */
body.theme-tactical .bp-chest-gold {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  margin: 4px 0 2px;
  padding: 5px 8px;
  background: rgba(26, 18, 4, 0.6);
  border: 1px solid rgba(255, 194, 71, 0.22);
}
body.theme-tactical .bp-chest-gold-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.52rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #ffc247;
  white-space: nowrap;
}
body.theme-tactical .bp-chest-gold-bar {
  display: block;
  height: 5px;
  background: #0a0710;
  border: 1px solid var(--hair);
  overflow: hidden;
}
body.theme-tactical .bp-chest-gold-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #ff8a2b, #ffc247);
  transition: width 0.4s ease;
}
body.theme-tactical .bp-chest-gold-v {
  font-family: 'Oswald', sans-serif;
  font-size: 0.82rem;
  color: #ffd97a;
  min-width: 18px;
  text-align: right;
  white-space: nowrap;
}
/* Nastepne otwarcie wypłaca zloto — wiersz zaploniony (ten sam sygnal co
   `.bp-chest-pity.is-ready`). Warunek: zostaje DOKLADNIE jedno otwarcie. */
body.theme-tactical .bp-chest-gold.is-ready {
  border-color: #ffc247;
  background: rgba(46, 30, 4, 0.66);
  box-shadow: 0 0 12px rgba(255, 194, 71, 0.25);
}
body.theme-tactical .bp-chest-gold.is-ready .bp-chest-gold-fill { background: linear-gradient(90deg, #f0a01f, #ffd97a); }
body.theme-tactical .bp-chest-gold.is-ready .bp-chest-gold-v { color: #ffc247; }

/* [CHANGE 2026-09-21 BP-R32-CLAIM-ALL-TRACK] PRZYCISK „CLAIM ALL" W TORZE NAGRÓD.
   Uzywa bazowej klasy `.bp-claim-all` (ta sama obsluga JS i styl co w TASKS), a tu
   tylko dokladamy uklad w naglowku toru + podzial na linie FREE/PREMIUM. */
body.theme-tactical .bp-track-claim {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
}
body.theme-tactical .bp-track-claim-split {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

/* ---- ACHIEVEMENTS: 1 wiersz = 1 achievement (ikona z gry) ---- */
/* ---- ACHIEVEMENTS: karty postępu ---- */
body.theme-tactical .bp-achievements-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}
body.theme-tactical .bp-ach-card {
  padding: 14px;
  background: var(--panel);
  border: 1px solid var(--hair);
  border-left: 2px solid var(--hair-2);
}
body.theme-tactical .bp-ach-card.is-unlocked { border-left-color: var(--signal); }
body.theme-tactical .bp-ach-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
body.theme-tactical .bp-ach-title { font-family: 'Oswald', sans-serif; font-size: 1.04rem; font-weight: 600; color: var(--text); }
body.theme-tactical .bp-ach-state {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  color: var(--muted);
  white-space: nowrap;
}
body.theme-tactical .bp-ach-card.is-unlocked .bp-ach-state { color: var(--signal); }
body.theme-tactical .bp-ach-desc {
  margin: 6px 0 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.64rem;
  line-height: 1.5;
  color: #9c968b;
}
body.theme-tactical .bp-ach-bar { height: 3px; background: var(--hair); }
body.theme-tactical .bp-ach-fill { display: block; height: 100%; background: var(--signal); }
body.theme-tactical .bp-ach-card.is-unlocked .bp-ach-fill { background: #ffb066; }
body.theme-tactical .bp-ach-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-top: 7px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  color: var(--muted);
}
body.theme-tactical .bp-ach-meta strong { font-family: 'Oswald', sans-serif; font-size: 0.96rem; color: var(--text); }

/* ---- Tooltip toru: wieksze okienko + ikona nagrody ----
   [CHANGE 2026-09-15 BP-TOOLTIP-LANE] Tooltip pokazuje jedna nagrobe (ta, na
   ktorej komurke najechano) i jest wiekszy niz bazowy (320px) — user chcial
   czytelniejszy podglad. */
body.theme-tactical .bp-reward-tooltip {
  max-width: 420px;
  min-width: 220px;
  padding: 16px 18px;
  border-radius: 0;
  border: 1px solid var(--hair-2);
  background: rgba(10, 7, 13, 0.97);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.6);
}
body.theme-tactical .bp-reward-tooltip-title {
  font-size: 0.96rem;
  color: #ffd9e8;
  margin-bottom: 12px;
}
body.theme-tactical .bp-reward-tooltip-line { font-size: 0.92rem; }
body.theme-tactical .bp-reward-tooltip-line strong { color: #f4f4f2; font-weight: 700; }
body.theme-tactical .bp-reward-tooltip-line .bp-reward-tooltip-ico {
  width: 30px;
  height: 30px;
  object-fit: contain;
  margin-right: 9px;
  vertical-align: middle;
}

/* ============================================================================
   [CHANGE 2026-09-15 BP-MAP] Ujednolicenie kolorystyki mapy kontynentów.
   PRZYCZYNA: kontur (piaskowy) renderował się TYLKO dla africa/south_america/
   north_america/australia — europe i asia miały `display:none` (base site.css,
   linie 4396/4400), przez co te cztery kontynenty były "piaskowe", a Europa czysta.
   FIX: ukrywamy kontur dla WSZYSTKICH kontynentów (spójnie z Europą) — mapa
   renderuje się jednolicie (ciemne tło + amber trasa) na każdym kontynencie.
   Aby przywrócić obrys, wystarczy zmienić `display` ponżej na `inline`.
   ========================================================================== */
body.theme-tactical .bp-route-contour { display: none; }

/* ============================================================================
   [CHANGE 2026-09-16 BP-TASKS-PALETTE] Tasks: usuniecie resztek granatowego/
   niebieskiego i czerwonego. Tlo kafli nagrod bylo `rgba(10,25,40,.6)` (granat),
   ikony `rgba(80,140,200,.2)` + `#88c5ff` (niebieski), naglowek REWARDS byl
   CZERWONY (#ff6464). Wszystko przepiete na purpurowa palete (#ff4d92/#ff8ec0).
   ========================================================================== */
body.theme-tactical .bp-quest-reward-item {
  border-color: rgba(255, 77, 146, 0.18);
  background: rgba(28, 12, 31, 0.72);
}
body.theme-tactical .bp-quest-reward-item:hover {
  border-color: rgba(255, 77, 146, 0.42);
  background: rgba(40, 16, 45, 0.85);
}
body.theme-tactical .bp-quest-reward-item.event-currency {
  border-color: rgba(255, 126, 178, 0.35);
  background: rgba(255, 64, 129, 0.1);
}
body.theme-tactical .bp-quest-reward-item.event-currency:hover {
  border-color: rgba(255, 126, 178, 0.55);
  background: rgba(255, 64, 129, 0.16);
}
body.theme-tactical .bp-quest-rewards-title {
  color: #ff8ec0;
  border-bottom-color: rgba(255, 77, 146, 0.28);
}
body.theme-tactical .bp-quest-reward-icon {
  background: rgba(255, 77, 146, 0.14);
  color: #ffc2dc;
}
body.theme-tactical .bp-quest-reward-item:hover .bp-quest-reward-icon { background: rgba(255, 77, 146, 0.24); }
body.theme-tactical .bp-quest-reward-icon.xp { background: linear-gradient(135deg, rgba(255, 77, 146, 0.3), rgba(255, 142, 192, 0.18)); color: #ffd9e8; }
body.theme-tactical .bp-quest-reward-item:hover .bp-quest-reward-icon.xp { background: linear-gradient(135deg, rgba(255, 77, 146, 0.42), rgba(255, 142, 192, 0.26)); }
body.theme-tactical .bp-quest-reward-icon.coin { background: rgba(255, 126, 178, 0.18); color: #ffc2dc; }
body.theme-tactical .bp-quest-reward-icon.item { background: linear-gradient(135deg, rgba(160, 96, 255, 0.16), rgba(255, 77, 146, 0.16)); color: #dcc2ff; }
body.theme-tactical .bp-quest-reward-icon.skull { background: rgba(255, 77, 146, 0.18); color: #ffb3d1; }
body.theme-tactical .bp-quest-reward-value { color: #f0e6ef; }
/* [CHANGE 2026-09-18 BP-R27-NO-COLLAPSE-TRIANGLE] Reguły `::after` (trójkąt
   „▾" + jego kolor w hover) USUNIĘTE razem z przyciskiem zwijania — patrz
   komentarz `.bp-tasks-col-head` w `site.css`. */
body.theme-tactical .bp-tasks-col-head:hover .bp-tasks-reset { color: #ff9ecb; }
/* [CHANGE 2026-09-16 BP-LOCK-COLORS] Klockа w inspektorze taskow: zlota gdy task
   jest dostepny/aktywny, zielona gdy ukonczony, szara gdy jeszcze zablokowany.
   Klase is-available/is-done/is-locked ustawia updateInspectorFromCard(). */
body.theme-tactical .bp-quest-lock-icon.is-available {
  color: #ffc247;
  filter: drop-shadow(0 0 7px rgba(255, 194, 71, 0.38));
}
body.theme-tactical .bp-quest-lock-icon.is-done {
  color: #5ef5a8;
  filter: drop-shadow(0 0 7px rgba(94, 245, 168, 0.38));
}
body.theme-tactical .bp-quest-lock-icon.is-locked {
  color: #8a92a3;
  filter: none;
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-TASKS-COL-BG] User: "pod taskami jest jeszcze jedno tlo
   ktore jest granatowe" — to `.bp-tasks-column` z site.css:3318
   (`linear-gradient(180deg, rgba(5,18,32,.7), rgba(4,14,26,.5))`).
   ========================================================================== */
body.theme-tactical .bp-tasks-column {
  background: linear-gradient(180deg, rgba(34, 16, 42, 0.78), rgba(14, 9, 18, 0.62));
  border-color: rgba(255, 77, 146, 0.2);
}

/* [CHANGE 2026-09-16 BP-RESET-FONT] User: "Powiększ czcionkę i zmień czcionkę na
   taką jak mamy na głównym portalu MORFACE OPERATIONS CENTER" = Oswald uppercase. */
body.theme-tactical .bp-tasks-reset {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.98rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: #e7e2d8;
}
body.theme-tactical .bp-tasks-col-toggle:hover .bp-tasks-reset { color: #ff9ecb; }

/* ============================================================================
   [CHANGE 2026-09-16 BP-TASKS-PURPLE-BG] User: "Jak klikne tasks to ciagle tlo
   mam granatowe a nie purpurowe". Zrodlo: motyw uzywa `--panel: #0d1016` i
   `--panel-2: #12161e` (niebieskawe) oraz `--hair: #222834`. Panel inspektora i
   karty zadan braly je wprost. Przepiete na purpurowa palete mapy
   (#140c1a panel, #1d1024 hover, obwodki rgba(255,77,146)).
   ========================================================================== */
body.theme-tactical .bp-quest-panel {
  background: #140c1a;
  border-color: rgba(255, 77, 146, 0.22);
}
body.theme-tactical .bp-quest-header { border-bottom-color: rgba(255, 77, 146, 0.16); }
body.theme-tactical .bp-quest-progress-bar {
  background: #0b0710;
  border-color: rgba(255, 77, 146, 0.24);
}
body.theme-tactical .bp-quest-meta { border-top-color: rgba(255, 77, 146, 0.16); }
body.theme-tactical .bp-task-card {
  background: #140c1a;
  border-color: rgba(255, 77, 146, 0.16);
  border-left-color: rgba(200, 110, 160, 0.4);
}
body.theme-tactical .bp-task-card:hover {
  background: #1d1024;
  border-color: rgba(255, 77, 146, 0.42);
}
body.theme-tactical .bp-tasks-list { background: transparent; }
body.theme-tactical .bp-tasks-col-head .bp-tasks-col-title { border-bottom-color: rgba(255, 77, 146, 0.2); }

/* ============================================================================
   [CHANGE 2026-09-16 BP-BOOSTER-FILTER] User: ikona "Atlas Boosters Box" ma miec
   subtelny PURPUROWY filtr (odroznia ja od pozostalych skrzyn).
   ========================================================================== */
body.theme-tactical .bp-chest-card[data-chest-code="random_box_aw_booster_01"] .bp-chest-art {
  filter: hue-rotate(232deg) saturate(1.25) brightness(0.96);
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-CHEST-MODAL] Skrzynie: w "CONTENTS" tylko itemy
   PROMOWANE, a pelna lista w duzym modalu na srodku ekranu. Wzorzec UX (CS:GO /
   Dota / Fortnite): przyciemniony backdrop z blurem, panel ze sticky headerem,
   siatka kafli 1:1 z ikona, nazwa i szansa; Esc/zamykanie w headerze; scroll
   tylko w tresci (header i footer zostaja widoczne).
   ========================================================================== */
body.theme-tactical .bp-chest-entry-ico {
  width: 100%;
  max-width: 68px;
  height: 34px;
  object-fit: contain;
}
body.theme-tactical .bp-chest-contents-all {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding: 5px 11px;
  border: 1px solid rgba(255, 77, 146, 0.28);
  background: rgba(255, 77, 146, 0.07);
  color: #ffc2dc;
  border-radius: 6px;
  font: 600 0.6rem/1 'IBM Plex Mono', monospace;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
body.theme-tactical .bp-chest-contents-all:hover {
  background: rgba(255, 77, 146, 0.16);
  border-color: rgba(255, 77, 146, 0.5);
}
body.theme-tactical .bp-chest-contents-count { color: #a8929f; }
body.theme-tactical .bp-chest-entry.is-promoted { border-color: rgba(255, 77, 146, 0.34); background: rgba(255, 64, 129, 0.07); }
body.theme-tactical .bp-chest-entry-tag {
  margin-left: 6px;
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid rgba(255, 126, 178, 0.4);
  color: #ffc2dc;
  font: 700 0.5rem/1.6 'IBM Plex Mono', monospace;
  letter-spacing: 0.08em;
}

.bp-chest-modal { position: fixed; inset: 0; z-index: 1200; display: none; }
.bp-chest-modal.is-open { display: block; }
.bp-chest-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(3, 2, 5, 0.84);
  backdrop-filter: blur(4px);
}
.bp-chest-modal-panel {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  /* [CHANGE 2026-09-17 BP-R25-MODAL-WIDE] User pkt 6: „Zrób to okienko podglądu
     szersze". 1120 px uciskalo wiersze wariantów (nazwa + chipy + suma) do tego
     stopnia, że tekst zaczynał się łamać. 1320 px daje wierszowi oddech także
     na laptopie 1366×768 (gdzie `100vw - 32px` nadal pilnuje marginesów). */
  width: min(1320px, calc(100vw - 28px));
  max-height: min(90vh, 980px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid rgba(255, 77, 146, 0.3);
  border-radius: 14px;
  background: linear-gradient(165deg, #160c1d 0%, #0c0812 58%, #100a16 100%);
  box-shadow: 0 34px 90px rgba(0, 0, 0, 0.66);
}
.bp-chest-modal-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 20px;
  border-bottom: 1px solid rgba(255, 77, 146, 0.18);
  background: rgba(10, 6, 14, 0.72);
}
.bp-chest-modal-art { width: 52px; height: 52px; object-fit: contain; flex: 0 0 auto; }
.bp-chest-modal-titles { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.bp-chest-modal-kicker {
  font: 600 0.58rem/1 'IBM Plex Mono', monospace;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #b06a8c;
}
.bp-chest-modal-title {
  font-family: 'Oswald', sans-serif;
  font-size: 1.18rem;
  letter-spacing: 0.02em;
  color: #fff4e6;
}
.bp-chest-modal-meta { margin-left: auto; text-align: right; }
.bp-chest-modal-meta-v { display: block; font-family: 'Oswald', sans-serif; font-size: 1rem; color: #ffb066; }
.bp-chest-modal-meta-k { font: 600 0.55rem/1.6 'IBM Plex Mono', monospace; letter-spacing: 0.14em; color: #8f8a80; }
.bp-chest-modal-close {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 77, 146, 0.3);
  border-radius: 8px;
  background: rgba(255, 77, 146, 0.08);
  color: #ffc2dc;
  font-size: 1rem;
  cursor: pointer;
}
.bp-chest-modal-close:hover { background: rgba(255, 77, 146, 0.2); }
.bp-chest-modal-body { overflow-y: auto; padding: 16px 20px 20px; }
.bp-chest-modal-group { margin-bottom: 18px; }
.bp-chest-modal-group:last-child { margin-bottom: 0; }
.bp-chest-modal-group-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.bp-chest-modal-group-title {
  font: 700 0.6rem/1 'IBM Plex Mono', monospace;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #ff8ec0;
}
.bp-chest-modal-group-line { flex: 1; height: 1px; background: linear-gradient(90deg, rgba(255, 77, 146, 0.3), transparent); }
.bp-chest-modal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 10px;
}
.bp-chest-loot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 12px 9px 10px;
  border: 1px solid rgba(255, 77, 146, 0.15);
  border-radius: 10px;
  background: rgba(22, 11, 27, 0.72);
  text-align: center;
}
.bp-chest-loot.is-jackpot { border-color: rgba(255, 126, 178, 0.42); background: rgba(255, 64, 129, 0.08); }
/* ═══════════════════════════════════════════════════════════════════════════
   [CHANGE 2026-09-18 BP-R27-GOLD-TIERS] TRZY POZIOMY PODŚWIETLENIA NAGRODY.
   ─────────────────────────────────────────────────────────────────────────
   User pkt 12 tury 14: „W Atlas Golden Box - broń czasowa nie powinna byc
   podswietlana, a broń złota permanentna powinna być jeszcze lepiej
   podświetlona - standardowe podświetlenie zachowamy na permanentą zwykłą
   broń".

   POZIOM 1 — `.is-jackpot` (standard, bez zmian): zwykła permanentna broń
   i skiny. Różowy akcent, którego używa cała sekcja „Main Rewards".

   POZIOM 2 — `.is-gold` (najmocniejszy): złota permanentna broń. Pełna złota
   ramka + zewnętrzna poświata + subtelny gradient. Wcześniej złota i zwykła
   permanentna wyglądały identycznie, więc gracz nie widział różnicy między
   nagrodą o szansie 0,1% a tą o szansie 4%.
   ⚠️ Kolejność ma znaczenie: `.is-gold` MUSI być PO `.is-jackpot`, bo oba mają
   tę samą specyficzność (klasa + klasa na tym samym elemencie) i wygrywa
   reguła późniejsza.

   POZIOM 0 — `.is-temp` (brak podświetlenia): wersja czasowa (1h). Nie jest
   nagrodą „główną", tylko próbką broni — podświetlanie jej tak samo jak
   permanentnej zawyżało jej wartość w oczach gracza. */
.bp-chest-loot.is-gold {
  border-color: rgba(255, 194, 71, 0.75);
  background: linear-gradient(160deg, rgba(64, 44, 6, 0.55), rgba(24, 16, 4, 0.5));
  box-shadow: 0 0 0 1px rgba(255, 194, 71, 0.22), 0 0 18px rgba(255, 194, 71, 0.24);
}
.bp-chest-loot.is-gold .bp-chest-loot-name { color: #ffe3b0; }
.bp-chest-loot.is-gold .bp-chest-loot-chance { color: #ffc247; }
.bp-chest-loot.is-temp {
  border-color: var(--hair);
  background: rgba(10, 12, 18, 0.5);
  box-shadow: none;
}
.bp-chest-loot.is-temp .bp-chest-loot-name { color: var(--muted, #8a92a3); }
/* Wiersz skróconej zawartości na KARCIE skrzyni dziedziczy ten sam podział. */
body.theme-tactical .bp-chest-entry.is-gold {
  border-color: rgba(255, 194, 71, 0.6);
  background: linear-gradient(90deg, rgba(64, 44, 6, 0.5), rgba(255, 194, 71, 0.06));
}
body.theme-tactical .bp-chest-entry.is-temp {
  border-color: rgba(34, 40, 52, 0.6);
  background: transparent;
}
/* ═══════════════════════════════════════════════════════════════════════════
   [CHANGE 2026-09-20 BP-R30-GOLD-ONLY] ZLOTE PODSWIETLENIE TYLKO DLA ZLOTYCH.
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R30, pkt 6): „Atlas Basic Box: Skull Coin jest w Main Rewards
   wyswietlany na niebiesko, na rolce rowniez pojawia sie w niebieskim kolorze,
   ale dodatkowo jest podswietlany na zloto. Zlote podswietlenie ma byc
   zarezerwowane wylacznie dla zlotych przedmiotow."

   PRZYCZYNA (ZNALEZIONA, NIE ZGADNIETA): nagroda glowna w „Main Rewards" dostaje
   DWIe klasy naraz — `.is-jackpot` (bo to nagroda glowna) ORAZ `.is-ultra`
   (bo ma `payload.rarity = "ultra"`). A reguła `.is-jackpot` stoi w arkuszu
   WYŻEJ niż `.is-gold` i ustawia DOKŁADNIE te same właściwości (`border-color`,
   `background`). Przy równej specyficzności (dwie klasy na tym samym elemencie)
   wygrywa reguła PÓŹNIEJSZA — czyli `.is-gold`, mimo że element złoty nie jest.
   Efekt: złota (bursztynowa) ramka i poświata na NIEBIESKIM Skull Coinie.
   ⚠️ Kolejność w arkuszu była wiec „poprawna" wg komentarza obok `.is-gold`
   („MUSI być PO .is-jackpot"), ale wniosek z niej był błędny: to nie znaczy,
   że `.is-gold` ma wygrywać ZAWSZE — ma wygrywać tylko, gdy element JEST złoty.

   POPRAWKA: klasy rzadkości (`.is-rare` / `.is-ultra`) są OSTATECZNE — stoją
   PO `.is-gold` i czyszczą złote tło/ramkę/poświatę. Nie zmieniamy koloru
   tekstu nazwy ani szansy (te niosą rzadkość), czyścimy wyłącznie „złoto".
   ⚠️ Element może być złoty ALBO rzadkościowy — `rewardTier()` i `rarity`
   wykluczają się w danych (złote wpisy nie mają `payload.rarity`), więc ta
   reguła jest zabezpieczeniem na wypadek danych, które kiedyś będą miały oba. */
.bp-chest-loot.is-rare,
.bp-chest-loot.is-ultra {
  /* `background` i `border-color` przywracane przez reguły rzadkości NIŻEJ
     (stoją po `.is-gold`), a tu gasimy złotą poświatę, której one nie ustawiają. */
  box-shadow: 0 0 0 1px rgba(77, 159, 255, 0.24), 0 0 20px rgba(77, 159, 255, 0.2);
}
.bp-chest-loot.is-rare { box-shadow: 0 0 0 1px rgba(61, 220, 132, 0.2), 0 0 16px rgba(61, 220, 132, 0.18); }
/* Ten sam problem na KARCIE skrzyni — wiersz nagrody głównej też łączył
   `is-promoted` ze złotem. */
body.theme-tactical .bp-chest-entry.is-rare,
body.theme-tactical .bp-chest-entry.is-ultra { box-shadow: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   [CHANGE 2026-09-20 BP-R30-GUARANTEE-SECTION] SEKCJA GWARANCJI.
   ─────────────────────────────────────────────────────────────────────────
   Sekcja „Guaranteed Reward" w modalu „Show all contents" (pkt 5). Wizualnie
   ODRÓŻNIONA od pozostałych, bo mówi o innej obietnicy: nie „co może wypaść",
   tylko „co WYPADNIE na 100. otwarcie". Ten sam złoty akcent co przy nagrodach
   złotych, ale na poziomie SEKCJI — kafle w środku zachowują swoje kolory
   rzadkości. */
.bp-chest-modal-group.is-guarantee {
  padding: 12px 12px 14px;
  border: 1px solid rgba(255, 194, 71, 0.42);
  border-radius: 10px;
  background: linear-gradient(180deg, rgba(64, 44, 6, 0.28), rgba(20, 14, 6, 0.18));
}
.bp-chest-modal-group.is-guarantee .bp-chest-modal-group-title { color: #ffc247; }
.bp-chest-modal-group.is-guarantee .bp-chest-modal-group-line {
  background: linear-gradient(90deg, rgba(255, 194, 71, 0.42), transparent);
}
.bp-chest-modal-group.is-guarantee .bp-chest-modal-group-chance { color: #ffd98a; }
/* [CHANGE 2026-09-21 BP-R30-GUARANTEE-COUNTER] LICZNIK POSTEPU „12 / 100".
   Pasek + liczba obok, ten sam jezyk wizualny co pasek litosci na karcie skrzyni
   (`.bp-chest-pity`) — gracz rozpoznaje „to samo, co widzialem na karcie".
   ⚠️ Kolor licznika jest BURSZTYNOWY, bo to licznik gwarancji ZLOTEJ/najrzadszej,
   a nie licznik rzadkosci (te sa zielone/niebieskie). */
.bp-guarantee-progress {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 10px;
  padding: 7px 10px;
  border: 1px solid rgba(255, 194, 71, 0.24);
  background: rgba(46, 30, 4, 0.34);
}
.bp-guarantee-progress-bar {
  flex: 1 1 auto;
  height: 4px;
  background: rgba(255, 194, 71, 0.16);
  overflow: hidden;
}
.bp-guarantee-progress-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #ff8a2b, #ffc247);
  box-shadow: 0 0 10px rgba(255, 194, 71, 0.45);
}
.bp-guarantee-progress-v {
  flex: 0 0 auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: #ffc247;
  white-space: nowrap;
}
/* Gdy gwarancja jest gotowa do wypłaty (licznik na 0 po pelnym cyklu), licznik
   dostaje ten sam sygnal co pasek litosci gotowy do strzalu. */
.bp-guarantee-progress.is-ready { border-color: #ffc247; background: rgba(46, 30, 4, 0.6); }
/* ⚠️ Złote tło sekcji NIE MOŻE „przebijać" przez kafle rzadkości — kafle mają
   własne tło (`.is-rare` / `.is-ultra`), więc są nieprzezroczyste i to jest
   poprawne. Ta reguła jest tylko dla kafli BEZ klasy rzadkości. */
.bp-chest-modal-group.is-guarantee .bp-chest-loot { background: rgba(16, 11, 20, 0.82); }

/* ═══════════════════════════════════════════════════════════════════════════
   [CHANGE 2026-09-20 BP-R29-RARITY] KLASY RZADKOSCI — RARE (zielony) / ULTRA (niebieski).
   ─────────────────────────────────────────────────────────────────────────
   Wlasciciel: „Zrobimy podzial na rare - kolor zielony i ultra rare - kolor niebieski".
   Znacznik przychodzi z `op_lootbox_entries.payload.rarity` (bez migracji).

   KOLEJNOSC MA ZNACZENIE: te reguly stoja PO `.is-gold` / `.is-temp` / `.is-promoted`,
   wiec przy nakladaniu sie klas rzadkosc jest widoczna. `.is-temp` (bron czasowa)
   jest wyjatkiem — tam rzadkosc NIE zmienia koloru, bo wlasciciel wprost chcial,
   zeby bron czasowa nie byla podswietlana (patrz `BP-R27-GOLD-TIERS`).
   ═══════════════════════════════════════════════════════════════════════════ */
/* RARE — zielony */
.bp-chest-loot.is-rare {
  border-color: rgba(61, 220, 132, 0.6);
  background: linear-gradient(160deg, rgba(8, 46, 27, 0.55), rgba(6, 20, 14, 0.5));
  box-shadow: 0 0 0 1px rgba(61, 220, 132, 0.2), 0 0 16px rgba(61, 220, 132, 0.18);
}
.bp-chest-loot.is-rare .bp-chest-loot-name { color: #b6f5d2; }
.bp-chest-loot.is-rare .bp-chest-loot-chance { color: #3ddc84; }
/* ULTRA RARE — niebieski */
.bp-chest-loot.is-ultra {
  border-color: rgba(77, 159, 255, 0.68);
  background: linear-gradient(160deg, rgba(10, 32, 66, 0.6), rgba(6, 14, 30, 0.52));
  box-shadow: 0 0 0 1px rgba(77, 159, 255, 0.24), 0 0 20px rgba(77, 159, 255, 0.24);
}
.bp-chest-loot.is-ultra .bp-chest-loot-name { color: #cfe3ff; }
.bp-chest-loot.is-ultra .bp-chest-loot-chance { color: #4d9fff; }
/* Bron CZASOWA nie jest podswietlana — rzadkosc jej nie koloruje. */
.bp-chest-loot.is-temp.is-rare,
.bp-chest-loot.is-temp.is-ultra {
  border-color: var(--hair);
  background: rgba(10, 12, 18, 0.5);
  box-shadow: none;
}
.bp-chest-loot.is-temp.is-rare .bp-chest-loot-name,
.bp-chest-loot.is-temp.is-ultra .bp-chest-loot-name { color: var(--muted, #8a92a3); }
/* Ten sam podzial dla skroconej zawartosci na KARCIE skrzyni. */
body.theme-tactical .bp-chest-entry.is-rare {
  border-color: rgba(61, 220, 132, 0.5);
  background: linear-gradient(90deg, rgba(8, 46, 27, 0.5), rgba(61, 220, 132, 0.06));
}
body.theme-tactical .bp-chest-entry.is-ultra {
  border-color: rgba(77, 159, 255, 0.55);
  background: linear-gradient(90deg, rgba(10, 32, 66, 0.55), rgba(77, 159, 255, 0.08));
}
body.theme-tactical .bp-chest-entry.is-temp.is-rare,
body.theme-tactical .bp-chest-entry.is-temp.is-ultra {
  border-color: rgba(34, 40, 52, 0.6);
  background: transparent;
}
/* [CHANGE 2026-09-16 BP-LOOT-ICO] Ikony broni z gry maja proporcje 500x200 / 700x200,
   wiec w kwadratowym slocie 58px byly malutkie (58x23). Slot rozciagniety na szerokosc
   kafelka — render broni jest teraz duzy i czytelny, a kwadratowe ikony (granaty,
   helmy) nadal wysrodkowane dzieki object-fit: contain. */
.bp-chest-loot-ico { width: 100%; height: 58px; object-fit: contain; }
.bp-chest-loot-name {
  font-family: 'Rajdhani', sans-serif;
  font-size: 0.76rem;
  font-weight: 600;
  line-height: 1.2;
  color: #ece4f0;
}
.bp-chest-loot-amt { font: 600 0.6rem/1 'IBM Plex Mono', monospace; color: #b8aebd; }
.bp-chest-loot-chance {
  margin-top: auto;
  width: 100%;
  padding-top: 7px;
  border-top: 1px solid rgba(255, 77, 146, 0.14);
  font: 700 0.68rem/1 'IBM Plex Mono', monospace;
  color: #ff9ecb;
}
/* [CHANGE 2026-09-16 BP-TIER-EQ] User: kafelki ilosci i szansy maja byc JEDNAKOWEJ
   wielkosci. `grid ... 1fr` rozciagal pojedyncze kafelki do pelnej szerokosci karty
   (72px vs 149px). Teraz flex + SZTYWNA szerokosc 72px — kazdy kafelek identyczny,
   niezaleznie od tego ile ich jest w karcie. */
.bp-chest-loot-tiers { display: flex; flex-wrap: wrap; gap: 4px; width: 100%; margin-top: 6px; }
.bp-chest-loot-tier {
  flex: 0 0 72px;
  width: 72px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding: 3px 4px;
  border: 1px solid rgba(255, 77, 146, 0.22);
  border-radius: 5px;
  background: rgba(255, 77, 146, 0.05);
  font: 600 0.56rem/1.25 'IBM Plex Mono', monospace;
  color: #e2cede;
}
.bp-chest-loot-tier .bp-chest-tier-amt { color: #f0e3ee; }
.bp-chest-loot-tier .bp-chest-tier-pct { color: #ff9ecb; font-weight: 700; }
.bp-chest-modal-note {
  padding: 12px 20px;
  border-top: 1px solid rgba(255, 77, 146, 0.16);
  background: rgba(10, 6, 14, 0.7);
  font: 600 0.58rem/1.5 'IBM Plex Mono', monospace;
  letter-spacing: 0.06em;
  color: #8f8a80;
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-ACH-WIDE] Achievements: 1 wiersz = 1 achievement, pelna
   szerokosc (user: "wiersz byl szerszy"). Siatka 2-kolumnowa wypada.
   ========================================================================== */
body.theme-tactical .bp-achievements-list { display: flex; flex-direction: column; gap: 8px; }


/* ============================================================================
   [CHANGE 2026-09-16 BP-PREMIUM-TASKS] User pkt 6: dodatkowe zadanie daily i
   weekly odblokowywane wylacznie z Battle Pass Premium. Karta jest widoczna
   zawsze (zacheta), ale zablokowana wizualnie dopoki `prof.premium` = false.
   ========================================================================== */
body.theme-tactical .bp-task-card.is-premium-gated {
  position: relative;
  border-color: rgba(255, 194, 71, 0.28);
  background:
    linear-gradient(180deg, rgba(48, 32, 12, 0.55), rgba(20, 12, 26, 0.7)),
    radial-gradient(120% 90% at 100% 0%, rgba(255, 194, 71, 0.1), transparent 60%);
}
body.theme-tactical .bp-task-card.is-premium-gated::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 194, 71, 0.12);
}
body.theme-tactical .bp-task-card.is-premium-gated.is-locked {
  filter: saturate(0.45) brightness(0.86);
}
body.theme-tactical .bp-task-card.is-premium-gated.is-locked .bp-task-progress-fill {
  background: linear-gradient(90deg, rgba(255, 194, 71, 0.4), rgba(255, 194, 71, 0.15));
}
body.theme-tactical .bp-state-premium {
  background: linear-gradient(135deg, #ffd479, #ff9c1f);
  color: #22160a;
  border-color: rgba(255, 225, 170, 0.7);
}
body.theme-tactical .bp-task-premium-active {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7ff0b8;
}
body.theme-tactical .bp-task-premium-note {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.64rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #c8a25a;
  margin-right: auto;
}
body.theme-tactical .bp-task-card.is-premium-gated .bp-task-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
body.theme-tactical .bp-task-card.is-premium-gated .bp-premium-cta {
  padding: 4px 12px;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ============================================================================
   [CHANGE 2026-09-24 BP-R77-CLAIMED-GREYED] ODEBRANE DZISIEJSZE = WYSZARZONE.
   ────────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (doprecyzowanie): „jak juz ODEBRAŁEM dzisiejsze taski - dzisiejsze
   tylko i wylacznie to maja byc wyszarzone".

   BYLO: karta odebrana dostawala tylko `opacity: 0.6` (z `site.css`) i zielona
   odznake CLAIMED — na tle innych kart nadal wygladala „zywa", wiec nie bylo
   widac na pierwszy rzut oka, ktore zadania sa juz zamkniete.
   JEST: pelne wyszarzenie (grayscale + nizsza opacity) + przekreslona nazwa,
   czyli ten sam jezyk wizualny, ktory strona uzywa dla rzeczy zakonczonych.

   ⚠️ ZASIEG = TYLKO `#bp-daily-list` (karty DZIENNE). User powiedzial wprost
   „dzisiejsze tylko i wylacznie": tygodniowe i kontynentalne karty maja wlasne
   sekcje (`#bp-weekly-list`) i zostaja bez zmian. Bez tego id regula objelaby
   WSZYSTKIE `.bp-task-card.claimed` na stronie, czyli takze weekly.
   ⚠️ `site.css` ma `.bp-task-card.claimed { opacity: 0.6 }` (0,2,0).
   Ten selektor jest (1,3,0) — id `#bp-daily-list` + `body.theme-tactical` +
   `.bp-task-card.claimed` — wiec WYGRYWA. Motyw tactical laduje sie PO `site.css`.
   Bez `body.theme-tactical` regula bylaby ignorowana (ta sama klasa bledu co
   przy #14 w pamieci repo).
   ⚠️ NIE ruszamy odznaki CLAIMED ani jej koloru — komunikat „odebrane" musi
   zostac czytelny, wyszarzenie dotyczy KARTY, nie informacji.
   ⚠️⚠️ [2026-09-25 R86] TEN BLOK ZOSTAŁ ZASTĄPIONY PRZEZ `BP-R86-COMPLETED-VISIBLE`
   NIŻEJ. Cztery reguły z `#bp-daily-list` w selektorze zostały USUNIĘTE, bo:
     1. `#bp-daily-list` to kontener kart DZIENNYCH — odebrane TYGODNIOWE
        (kontener `#bp-weekly-list`) nie łapały się ani raz (zmierzone: 1 z 14
        odebranych zadań było wyszarzone),
     2. po rozszerzeniu zakresu stały się DUPLIKATAMI nowych reguł bez id.
        Dwie reguły na ten sam efekt to dokładnie ten wzorzec, który w tym
        projekcie już raz ukrył poprawną zmianę (patrz „DWA RAZY TA SAMA REGUŁA
        CSS = jedna ukryta" w pamięci repo).
   ========================================================================== */
/* ══ [CHANGE 2026-09-25 BP-R86-COMPLETED-VISIBLE] ══════════════════════════
   AKTUALNE reguły wyszarzenia i odznaki „TASK COMPLETED" — patrz komentarz
   przy `.bp-task-completed-badge` niżej. ══ */

/* ============================================================================
   [CHANGE 2026-09-25 BP-R86-COMPLETED-VISIBLE] UKOŃCZONE ZADANIA = WIDOCZNE,
   WYSZARZONE, Z INFORMACJĄ „TASK COMPLETED".
   ────────────────────────────────────────────────────────────────────────────
   ZGŁOSZENIE WŁAŚCICIELA (R86 pkt 2): „Prosiłem żeby wykonane taski ciągle były
   widoczne tylko wyszarzone z informacją ze task completed a ciagle tego nie ma".
   ⚠️ To TRZECIE zgłoszenie tego obszaru (R76 → R77 → R86) i za każdym razem
   `claimed`, nie `completed` — czyli user patrzy na stan ZAMKNIĘTY (CLAIM
   kliknięty), nie na „zrobione, czeka na odbiór". R76 poszedł w złą stronę
   (doklejał wygasłe zadania z minionych dni), R77 zawęził zakres do samych
   dziennych. Dlatego teraz piszemy WPROST w interfejsie.

   ⚠️⚠️ STANY ZMIERZONE NA PRODUKCJI (nie założenia):
     daily    : 126 ukończonych /   5 odebranych  → wyszarzone od R77 ✔
     weekly   :   8 ukończonych /   1 odebrany    → NIE było wyszarzone ✘
     season   :  32 ukończonych /   8 odebranych  → brak karty w widoku ✘
     ukończone-nieodebrane: 121 daily + 7 weekly + 24 season → brak informacji ✘
   Czyli reguła z R77 obejmowała 5 z 14 odebranych zadań i nie mówiła nic
   o pozostałych 136 ukończonych. Stąd „ciągle tego nie ma".

   ⚠️ ZASIĘG ROZSZERZONY na WSZYSTKIE `.bp-task-card` (było `#bp-daily-list`).
   Powód: `#bp-daily-list` to kontener KART DZIENNYCH, a weekly renderują się
   w `#bp-weekly-list` — dlatego odebrane tygodniowe nie były wyszarzone.
   Selektory z `body.theme-tactical` (0,2,0 + klasy = 1,3,0) nadal biją
   `site.css` (`.bp-task-card.claimed` = 0,2,0), więc motyw wygrywa.
   ⚠️ Karty premium `.is-premium-gated` nie mają klas `completed`/`claimed`,
   więc reguła ich nie dotyka.

   ⚠️⚠️ PRZYCISK CLAIM MUSI ZOSTAĆ W PEŁNI CZYTELNY. Wyszarzenie całej karty to
   `filter: grayscale()` + `opacity`, a to OBEJMUJE DZIECI — przy `opacity: 0.42`
   złoty przycisk staje się szarym prostokątem i gracz nie widzi, że ma co
   odebrać (dokładnie ten problem naprawiał `BP-R78-CLAIM-VISIBLE` w innym
   miejscu). Dlatego ukończone-nieodebrane dostają:
     • niższą opacity TYLKO na treści (bez footera) — przez `filter` na karcie
       NIE da się tego zrobić, więc idziemy po `:not(:has(...))`? NIE — używamy
       jawnego wyjątku: `filter: none` + `opacity: 1` NA footerze nie zadziała
       (filter na rodzicu nie da się cofnąć dziecku).
   ROZWIĄZANIE: dla `.completed:not(.claimed)` NIE używamy `filter`/`opacity`
   na całej karcie. Zamiast tego przygaszamy SAMĄ TREŚĆ (nagłówek, pasek,
   licznik), a footer z przyciskiem zostaje nietknięty. `:has()` jest tu
   niepotrzebne — wystarczy, że reguła nie dotyka `.bp-task-footer`.
   ========================================================================== */

/* ── 1. UKOŃCZONE, JESZCZE NIEODEBRANE (przycisk CLAIM widoczny) ──────────── */
body.theme-tactical .bp-task-card.completed:not(.claimed) {
  border-color: rgba(94, 245, 168, 0.34);
  background: linear-gradient(180deg, rgba(20, 40, 32, 0.5), rgba(14, 9, 18, 0.62));
}
/* Treść przygaszona — ale BEZ `filter`, żeby nie objąć przycisku CLAIM. */
body.theme-tactical .bp-task-card.completed:not(.claimed) .bp-task-dossier-head,
body.theme-tactical .bp-task-card.completed:not(.claimed) .bp-task-header,
body.theme-tactical .bp-task-card.completed:not(.claimed) .bp-task-items,
body.theme-tactical .bp-task-card.completed:not(.claimed) .bp-task-progress-bar,
body.theme-tactical .bp-task-card.completed:not(.claimed) .bp-task-counter {
  opacity: 0.55;
}
body.theme-tactical .bp-task-card.completed:not(.claimed) .bp-task-name {
  color: #b9c6b6;
}
body.theme-tactical .bp-task-card.completed:not(.claimed) .bp-task-progress-fill {
  background: linear-gradient(90deg, rgba(94, 245, 168, 0.75), rgba(94, 245, 168, 0.3));
}
/* Informacja „TASK COMPLETED" obok przycisku — to jest to, o co prosił user.
   `margin-right: auto` odsuwa ją od prawej krawędzi, żeby nie zlewała się
   z przyciskiem CLAIM. */
body.theme-tactical .bp-task-completed-badge {
  margin-right: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #8ee8b6;
  border: 1px solid rgba(94, 245, 168, 0.42);
  background: rgba(94, 245, 168, 0.1);
  padding: 3px 8px;
}

/* ── 2. UKOŃCZONE I ODEBRANE — pełne wyszarzenie (WSZYSTKIE typy zadań) ───── */
/* ⚠️ Bez `#bp-daily-list`: ta reguła musi objąć takze `#bp-weekly-list`. */
body.theme-tactical .bp-task-card.claimed {
  opacity: 0.42;
  filter: grayscale(0.8);
}
body.theme-tactical .bp-task-card.claimed .bp-task-name {
  text-decoration: line-through;
  text-decoration-color: rgba(150, 160, 175, 0.7);
}
body.theme-tactical .bp-task-card.claimed .bp-task-progress-fill {
  background: linear-gradient(90deg, rgba(120, 130, 145, 0.5), rgba(120, 130, 145, 0.2));
}
/* Reroll na odebranym zadaniu jest i tak zablokowany (`disabled` z serwera) —
   chowamy go, zeby nie wprowadzal w blad, ze da sie jeszcze cos zmienic. */
body.theme-tactical .bp-task-card.claimed .bp-task-reroll {
  display: none;
}
/* ⚠️ Odznaka CLAIMED zostaje CZYTELNA mimo `grayscale` na karcie — podbijamy
   kontrast, bo `filter` nie da sie cofnac dziecku. Celowo NIE uzywamy tu
   koloru (grayscale i tak go zredukuje), a jasnosc i obramowanie. */
body.theme-tactical .bp-task-card.claimed .bp-task-claimed-badge {
  color: #f2ede2;
  border: 1px solid rgba(150, 160, 175, 0.5);
  background: rgba(150, 160, 175, 0.16);
  padding: 2px 8px;
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-MODAL-PRICE] User pkt 9: wieksza czcionka ceny w modalu
   "Show all contents" + ikona waluty (Skulls) przed liczba.
   ========================================================================== */
body.theme-tactical .bp-chest-modal-meta {
  display: flex;
  align-items: center;
  gap: 9px;
}
body.theme-tactical .bp-chest-modal-cost-ico {
  width: 27px;
  height: 27px;
  object-fit: contain;
  filter: drop-shadow(0 0 7px rgba(255, 194, 71, 0.4));
}
body.theme-tactical .bp-chest-modal-meta-v {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.85rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.03em;
  color: #ffc247;
}
body.theme-tactical .bp-chest-modal-meta-k {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 194, 71, 0.72);
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-OPS-HUD] User pkt 1 + 11: pasek progresu Battle Passa
   (na LOKALNYM postepie gracza) + portfel walut (Skulls/Dollars/Crowns/Kredits)
   pod paskiem zakladek. Zastapil lewa kolumne "Event Currency" z panelu
   CONTINENTS — dzieki temu globus i kontynenty maja pelna szerokosc.
   ========================================================================== */
body.theme-tactical .bp-ops-hud {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 14px;
  align-items: stretch;
  margin: 16px auto 6px;
  padding: 16px 18px;
  background:
    linear-gradient(135deg, rgba(46, 16, 40, 0.62), rgba(12, 9, 16, 0.88)),
    radial-gradient(120% 140% at 0% 0%, rgba(255, 77, 146, 0.12), transparent 62%);
  border: 1px solid rgba(255, 77, 146, 0.22);
  border-radius: 14px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.42);
}
body.theme-tactical .bp-ops-progress { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
body.theme-tactical .bp-ops-progress-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
body.theme-tactical .bp-ops-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #a89aa4;
}
body.theme-tactical .bp-ops-pct {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.55rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
  color: #ffc247;
  text-shadow: 0 0 14px rgba(255, 194, 71, 0.3);
}
body.theme-tactical .bp-ops-hud .bp-command-bar {
  height: 12px;
  background: #0a0710;
  border: 1px solid var(--hair);
  border-radius: 3px;
  overflow: hidden;
}
body.theme-tactical .bp-ops-hud .bp-command-bar-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #ff8a2b, #ffc247, #ffd479);
  box-shadow: 0 0 14px rgba(255, 194, 71, 0.45);
}
body.theme-tactical .bp-ops-progress-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  padding-top: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}
body.theme-tactical .bp-ops-stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
body.theme-tactical .bp-ops-stat-wide { margin-left: auto; text-align: right; }
body.theme-tactical .bp-ops-v {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #f1ecf4;
}

body.theme-tactical .bp-ops-wallet {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}
body.theme-tactical .bp-wallet-tile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  min-width: 0;
  border: 1px solid var(--hair);
  border-radius: 10px;
  background: linear-gradient(160deg, rgba(28, 15, 34, 0.7), rgba(10, 8, 13, 0.84));
  transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
}
body.theme-tactical .bp-wallet-tile:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.45);
}
body.theme-tactical .bp-wallet-ico {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  object-fit: contain;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.6));
}
body.theme-tactical .bp-wallet-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
body.theme-tactical .bp-wallet-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.56rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #9d94a2;
}
body.theme-tactical .bp-wallet-v {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.22rem;
  font-weight: 600;
  line-height: 1.05;
  color: #f4f1f6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-wallet-tile.is-skulls { border-color: rgba(255, 194, 71, 0.3); }
body.theme-tactical .bp-wallet-tile.is-skulls .bp-wallet-v { color: #ffd479; }
body.theme-tactical .bp-wallet-tile.is-dollars { border-color: rgba(120, 200, 255, 0.22); }
body.theme-tactical .bp-wallet-tile.is-crowns { border-color: rgba(255, 138, 43, 0.26); }
body.theme-tactical .bp-wallet-tile.is-kredits { border-color: rgba(94, 245, 168, 0.24); }

/* [CHANGE 2026-09-16 BP-CONTINENTS-FULL] Poniewaz lewa kolumna z waluta znikla,
   sekcja kontynentow to juz pojedyncza kolumna na pelna szerokosc. */
body.theme-tactical .bp-continents-layout {
  margin: 16px auto 8px;
  display: block;
}

@media (max-width: 1080px) {
  body.theme-tactical .bp-ops-hud { grid-template-columns: minmax(0, 1fr); }
  body.theme-tactical .bp-ops-stat-wide { margin-left: 0; text-align: left; }
}
@media (max-width: 780px) {
  body.theme-tactical .bp-ops-wallet { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 480px) {
  body.theme-tactical .bp-ops-wallet { grid-template-columns: minmax(0, 1fr); }
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-TASK-REWARDS] User pkt 3 + 4: nagrody przy zadaniach to
   teraz ikona BP XP + liczba ORAZ czaszki z battlepassa. Usuniete: znacznik
   "Item" i slot "pucharow".
   ========================================================================== */
body.theme-tactical .bp-task-rewards {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
body.theme-tactical .bp-task-xp-wrap,
body.theme-tactical .bp-task-skulls {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
body.theme-tactical .bp-task-xp-ico {
  width: 15px;
  height: 15px;
  object-fit: contain;
  filter: drop-shadow(0 0 5px rgba(120, 190, 255, 0.5));
}
body.theme-tactical .bp-task-xp {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.94rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #9fd0ff;
}
body.theme-tactical .bp-task-skull-ico {
  width: 14px;
  height: 14px;
  object-fit: contain;
  filter: drop-shadow(0 0 5px rgba(255, 194, 71, 0.42));
}
body.theme-tactical .bp-task-skull-v {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.88rem;
  font-weight: 600;
  color: #ffd479;
}
/* ─── [CHANGE 2026-09-18 BP-R27E-5-ITEM-REWARD-VISIBLE] NAGRODA PRZEDMIOTOWA ──
   NA KARCIE ZADANIA (dzienne i tygodniowe).
   User: „w step 4 nie ma nagrody w ktorej jest Atlas Weapons!" — nagroda była
   w bazie, ale karta pokazywała tylko XP i czaszki, więc gracz widział zadanie
   bez nagrody.
   ⚠️ [R27e-6] WIERSZ NA CAŁĄ SZEROKOŚĆ, NIE CHIP W PASKU NAGRÓD.
   ─────────────────────────────────────────────────────────────────────────────
   ZGŁOSZENIE USERA: „Jesli chodzi o Broń Atlas na 7d - to pełne nazwy nie są
   widoczne - Ogolnie jesli damy nagrody to nazwy nie będą w pełni widoczne -
   przerób widok".

   BYŁO: chip stał w `.bp-task-rewards` — pasku `inline-flex` z `flex-shrink: 0`
   w POZIOMYM nagłówku karty. Miejsca było tyle, co między tytułem a prawą
   krawędzią, więc mieściła się tylko ikona i skrót „7d", a nazwa żyła w `title`
   (hover). Na dotyku podpowiedź nie istnieje, a „dostajesz broń na 7 dni" bez
   nazwy broni to niepełna informacja o nagrodzie.

   JEST: `.bp-task-items` to OSOBNY wiersz pod nagłówkiem, pełna szerokość karty.
   Nazwa jest zwykłym tekstem i MOŻE się zawinąć — to jedyny sposób, żeby długa
   nazwa („Atlas Advanced Weapons Box", „Atlas PM-84 Glauberyt") była widoczna
   w całości, niezależnie od szerokości kolumny. */
body.theme-tactical .bp-task-items {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 9px;
}
body.theme-tactical .bp-task-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 9px;
  border: 1px solid rgba(120, 190, 255, 0.3);
  background: linear-gradient(90deg, rgba(120, 190, 255, 0.1), rgba(22, 13, 23, 0.5));
  /* ⚠️ `min-width: 0` na elemencie flex jest WARUNKIEM zawijania tekstu:
     bez tego dziecko nie może być węższe niż jego treść i nazwa wychodzi poza
     kafelek zamiast się zawinąć. To ta sama pułapka, co przy podglądzie nagrody
     w panelu węzła. */
  min-width: 0;
}
body.theme-tactical .bp-task-item-ico {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  object-fit: contain;
  filter: drop-shadow(0 0 6px rgba(120, 190, 255, 0.45));
}
body.theme-tactical .bp-task-item-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
body.theme-tactical .bp-task-item-name {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.25;
  color: #dbe9fb;
  /* Zawijanie zamiast wielokropka — pełna nazwa jest ważniejsza od jednakowej
     wysokości kart. `anywhere` łamie też bardzo długie kody bez spacji. */
  overflow-wrap: anywhere;
}
body.theme-tactical .bp-task-item-v {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: #9fd0ff;
}

/* Inspektor: 2 sloty nagrod (XP + Skulls) + ikony-obrazki zamiast SVG-literek. */
body.theme-tactical .bp-quest-rewards-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
body.theme-tactical .bp-quest-reward-icon { width: 40px; height: 40px; padding: 3px; }
body.theme-tactical .bp-quest-reward-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
body.theme-tactical .bp-quest-reward-value {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.06rem;
  font-weight: 600;
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-ACH-UX] User pkt 5: wiersze byly za wysokie, a WSTEGI
   (grafika 512x128 = 4:1) byly wcisniete w kwadrat 116x116, wiec praktycznie
   niewidoczne. Teraz 3 osobne sekcje z layoutem dopasowanym do grafiki:
     • Campaign Stripes — wstegi 4:1 w siatce 2 kolumn
     • Squad Badges / Service Marks — kafelki 1:1
   Hover/kbd-focus: lekkie powiekszenie + subtelna poswiata — POMARANCZOWA gdy
   zablokowane, ZIELONA gdy odblokowane.
   ========================================================================== */
body.theme-tactical .bp-ach-group { margin-top: 24px; }
body.theme-tactical .bp-ach-group-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  padding-bottom: 8px;
  margin-bottom: 12px;
  border-bottom: 1px solid rgba(255, 77, 146, 0.16);
}
body.theme-tactical .bp-ach-group-title {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #ff8ec0;
}
body.theme-tactical .bp-ach-group-note {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  color: #8f8896;
}

/* --- wstegi (kampanie) --- */
body.theme-tactical .bp-ach-strip-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}
body.theme-tactical .bp-ach-strip {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: linear-gradient(150deg, rgba(26, 13, 32, 0.72), rgba(10, 8, 13, 0.9));
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
body.theme-tactical .bp-ach-strip-art {
  position: relative;
  aspect-ratio: 4 / 1;
  overflow: hidden;
  background: #0b0810;
}
body.theme-tactical .bp-ach-strip-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: none; /* [BP-ACH-NOFILTER] grayscale/brightness usuniete - user: kolory byly zbyt szare/wyblakle */
  transition: filter 0.18s ease, transform 0.18s ease;
}
body.theme-tactical .bp-ach-strip.is-unlocked .bp-ach-strip-art img { filter: none; }
body.theme-tactical .bp-ach-strip-state {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 7px;
  border: 1px solid var(--hair-2);
  border-radius: 3px;
  background: rgba(8, 6, 10, 0.78);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.56rem;
  letter-spacing: 0.12em;
  color: #a8a49b;
}
body.theme-tactical .bp-ach-strip.is-unlocked .bp-ach-strip-state {
  border-color: rgba(94, 245, 168, 0.45);
  color: #5ef5a8;
}
body.theme-tactical .bp-ach-strip-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px 12px;
}
body.theme-tactical .bp-ach-strip-title {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.94rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #efe9f2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-ach-strip-meta {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  color: #9b93a1;
}
body.theme-tactical .bp-ach-strip-bar {
  height: 6px;
  background: #0a0710;
  border: 1px solid var(--hair);
  overflow: hidden;
}
body.theme-tactical .bp-ach-strip-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #ff8a2b, #ffc247);
  box-shadow: 0 0 10px rgba(255, 194, 71, 0.4);
}

/* --- kafelki (odznaki + rangi) --- */
body.theme-tactical .bp-ach-tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  gap: 12px;
}
body.theme-tactical .bp-ach-tile {
  display: grid;
  grid-template-rows: auto auto auto auto;
  gap: 6px;
  padding: 14px 12px 12px;
  border: 1px solid var(--hair);
  border-radius: 12px;
  background: linear-gradient(160deg, rgba(26, 13, 32, 0.66), rgba(10, 8, 13, 0.88));
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
body.theme-tactical .bp-ach-tile-art {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 84px;
}
body.theme-tactical .bp-ach-tile-ico {
  width: 72px;
  height: 72px;
  object-fit: contain;
  filter: none; /* [BP-ACH-NOFILTER] grayscale/brightness usuniete - user: kolory byly zbyt szare/wyblakle */
  transition: filter 0.18s ease, transform 0.18s ease;
}
body.theme-tactical .bp-ach-tile.is-unlocked .bp-ach-tile-ico { filter: none; }
body.theme-tactical .bp-ach-tile-title {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #e9e3ee;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-ach-tile-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
body.theme-tactical .bp-ach-tile-meta strong {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.86rem;
  font-weight: 600;
  color: #f0ecf4;
}
body.theme-tactical .bp-ach-tile-meta strong em {
  margin-left: 3px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.54rem;
  font-style: normal;
  letter-spacing: 0.08em;
  color: #8f8896;
}
body.theme-tactical .bp-ach-tile-state {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.54rem;
  letter-spacing: 0.1em;
  color: #8a8490;
}
body.theme-tactical .bp-ach-tile.is-unlocked .bp-ach-tile-state { color: #5ef5a8; }
body.theme-tactical .bp-ach-tile-bar {
  height: 4px;
  background: #0a0710;
  border: 1px solid var(--hair);
  overflow: hidden;
}
body.theme-tactical .bp-ach-tile-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #ff8a2b, #ffc247);
}

/* --- hover / focus: powiekszenie + poswiata zalezna od stanu --- */
body.theme-tactical .bp-ach-strip:hover,
body.theme-tactical .bp-ach-strip:focus-visible,
body.theme-tactical .bp-ach-tile:hover,
body.theme-tactical .bp-ach-tile:focus-visible {
  outline: none;
  transform: translateY(-3px) scale(1.022);
  border-color: rgba(255, 138, 43, 0.42);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5), 0 0 22px rgba(255, 138, 43, 0.16);
}
body.theme-tactical .bp-ach-strip.is-unlocked:hover,
body.theme-tactical .bp-ach-strip.is-unlocked:focus-visible,
body.theme-tactical .bp-ach-tile.is-unlocked:hover,
body.theme-tactical .bp-ach-tile.is-unlocked:focus-visible {
  border-color: rgba(94, 245, 168, 0.42);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5), 0 0 22px rgba(94, 245, 168, 0.18);
}
body.theme-tactical .bp-ach-strip:hover .bp-ach-strip-art img { transform: scale(1.03); }
body.theme-tactical .bp-ach-tile:hover .bp-ach-tile-ico { transform: scale(1.06); }

@media (max-width: 900px) {
  body.theme-tactical .bp-ach-strip-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 620px) {
  body.theme-tactical .bp-ach-tile-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ============================================================================
   [CHANGE 2026-09-16 BP-SECTION-CHANCE] User pkt 7: szansa na trafienie SEKCJI
   w modalu "Show all contents".
   ========================================================================== */
body.theme-tactical .bp-chest-modal-group-chance {
  order: 2;
  padding: 2px 8px;
  border: 1px solid rgba(255, 194, 71, 0.32);
  border-radius: 3px;
  background: rgba(255, 194, 71, 0.06);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  color: #ffc247;
  white-space: nowrap;
}
body.theme-tactical .bp-chest-modal-group-chance.is-unknown {
  border-color: var(--hair-2);
  background: none;
  color: #8a8490;
}
body.theme-tactical .bp-chest-modal-group-line { order: 3; }

/* ============================================================================
   [CHANGE 2026-09-16 BP-R21] Tura 7 — poprawki UX wskazane przez wlasciciela.
   Zawartosc:
     1) PASEK ZAKLADEK   — wieksza czcionka, subtelny gradient, wyrazny hover.
     2) NAGRODY ZADANIA  — wieksze ikony, przesuniete w lewo od krawedzi karty.
     3) IKONA BP XP      — wieksza w widoku kontynentow (brief + preview trasy).
     4) REROLL           — przycisk przelosowania dziennego zadania.
     5) ACHIEVEMENTS     — bez odbarwiania, pelna szerokosc, 2 rzedy odznak, tooltip.
     6) MODAL            — wieksza czcionka "% chance".
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1) PASEK ZAKLADEK — user pkt 6: "powieksz troche czcionke i zrob zeby to sie
   wyrozniało - mozesz nalozyc jakis ładny subtelny gradient pod tło, zeby gracz
   widzial po prostu ze tu moze sobie kliknąć".
   --------------------------------------------------------------------------- */
body.theme-tactical .bp-tabs {
  margin: 20px auto 0;
  padding: 6px 6px 0;
  gap: 4px;
  border-bottom: 0;
  border: 1px solid rgba(255, 77, 146, 0.18);
  border-bottom: 1px solid rgba(255, 77, 146, 0.28);
  border-radius: 14px 14px 0 0;
  background:
    linear-gradient(180deg, rgba(46, 18, 42, 0.66), rgba(14, 10, 18, 0.86) 62%, rgba(10, 8, 13, 0.92)),
    radial-gradient(140% 160% at 0% 0%, rgba(255, 138, 43, 0.1), transparent 58%);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
body.theme-tactical .bp-tab {
  position: relative;
  overflow: hidden;
  border-bottom: 2px solid transparent;
  border-radius: 10px 10px 0 0;
  color: #b3a7b4;
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.06rem;
  font-weight: 600;
  letter-spacing: 0.11em;
  padding: 14px 28px;
  transition: color .16s ease, border-color .16s ease, background .16s ease, transform .16s ease;
}
body.theme-tactical .bp-tab::before {
  content: '';
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 138, 43, 0.16), rgba(255, 77, 146, 0.05) 70%, transparent);
  transition: opacity .16s ease;
}
body.theme-tactical .bp-tab:hover {
  color: #ffe9f2;
  background: rgba(255, 255, 255, 0.03);
}
body.theme-tactical .bp-tab:hover::before { opacity: 1; }
body.theme-tactical .bp-tab:focus-visible {
  outline: 2px solid rgba(255, 138, 43, 0.55);
  outline-offset: -2px;
}
body.theme-tactical .bp-tab.is-active {
  color: #ffc247;
  border-bottom-color: #ffc247;
  background: linear-gradient(180deg, rgba(255, 194, 71, 0.12), rgba(255, 194, 71, 0.02) 75%, transparent);
  text-shadow: 0 0 14px rgba(255, 194, 71, 0.28);
}
body.theme-tactical .bp-tab.is-active::before { opacity: 1; }
body.theme-tactical .bp-tab-badge {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  line-height: 1;
  padding: 4px 7px;
  border-radius: 3px;
  color: var(--ink);
  background: var(--signal);
}
/* [CHANGE 2026-09-17 BP-R25-BADGE-SLASH] SEPARATOR „/" W PLAKIETCE LICZNIKA.
   ─────────────────────────────────────────────────────────────────────────
   User (pkt 3): „Przy zakladce achievements i colections mam 0/21 - ale czcionka
   jest tak niefortunna ze wyglada jakby to bylo 9 - zmien to".

   Przyczyna: IBM Plex Mono rysuje ukośnik wąsko i blisko cyfr, a przy 0.66rem
   `0/21` zlewało sie w jedna bryłę — gracz czytal „0/21" jako „0219" (albo „09").

   Rozwiazanie: ukośnik jest osobnym elementem `<i>` (patrz `battlepass.ejs`),
   ktory dostaje:
     • `font-style: normal` — `<i>` domyslnie jest KURSYWĄ, wiec bez tego mialby
       inny kształt niz reszta i wygladalo to jak bląd skladu;
     • oddech po obu stronach (`margin`) — to on rozdziela cyfry;
     • przygaszony kolor — separator ma byc tlem, nie trescia.
   ⚠️ TEN BLOK MUSI BYC PO `.bp-tab-badge` (motyw laduje sie po `site.css`,
   a przy rownej specyficznosci wygrywa reguła POZNIEJSZA). */
body.theme-tactical .bp-tab-badge > i {
  font-style: normal;
  margin: 0 4px;
  opacity: 0.55;
  font-weight: 400;
}
body.theme-tactical .bp-tab-badge > i::before,
body.theme-tactical .bp-tab-badge > i::after {
  /* Dodatkowa przerwa bez zmiany szerokosci plakietki poza nia — hair space
     nie wchodzi do szerokosci przy `letter-spacing: normal`. */
  content: '';
  display: inline-block;
  width: 1px;
}
body.theme-tactical .bp-tab.is-active .bp-tab-badge {
  background: #ffc247;
  color: #22160a;
}
@media (max-width: 860px) {
  body.theme-tactical .bp-tab { padding: 12px 16px; font-size: 0.94rem; letter-spacing: 0.07em; }
}

/* ---------------------------------------------------------------------------
   2) NAGRODY ZADANIA — user pkt 3: "w taskach obie nagrody xp i skulls są bardzo
   małe, chciałbym żebyś te nagrody (w taskach) przeniósł trochę w lewo i je dość
   powiększył". Blok nagród nie klei sie juz do prawej krawedzi (margin-right),
   ikony 15/14 px -> 26/24 px, liczby 0.94 -> 1.32 rem.
   --------------------------------------------------------------------------- */
body.theme-tactical .bp-task-rewards {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  margin-right: 14px;
  margin-left: auto;
  flex-shrink: 0;
}
body.theme-tactical .bp-task-xp-wrap,
body.theme-tactical .bp-task-skulls {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
body.theme-tactical .bp-task-xp-ico {
  width: 34px;
  height: 34px;
  object-fit: contain;
  filter: drop-shadow(0 0 8px rgba(120, 190, 255, 0.55));
}
body.theme-tactical .bp-task-xp {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.52rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1;
  color: #bfe0ff;
}
body.theme-tactical .bp-task-skull-ico {
  width: 30px;
  height: 30px;
  object-fit: contain;
  filter: drop-shadow(0 0 8px rgba(255, 194, 71, 0.45));
}
body.theme-tactical .bp-task-skull-v {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.42rem;
  font-weight: 600;
  line-height: 1;
  color: #ffd479;
}
/* Karty premium (bez data-task-code) tez uzywaja .bp-task-rewards — nic dodatkowego. */

/* ---------------------------------------------------------------------------
   3) IKONA BP XP / SKULLS W WIDOKU KONTYNENTOW — user pkt 3: "W widoku w
   kontynentach i taskach [...] W głównym podglądzie możesz też je odrobinę
   powiększyć". Dotyczy briefu przy pinezce i preview trasy (Mission Brief).
   --------------------------------------------------------------------------- */
body.theme-tactical .bp-xp-reward-icon {
  width: 34px;
  height: 34px;
  object-fit: contain;
  filter: drop-shadow(0 0 9px rgba(120, 190, 255, 0.5));
}
body.theme-tactical .bp-xp-reward-icon-sm {
  width: 30px;
  height: 30px;
}
body.theme-tactical .bp-route-node-brief-reward {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.16rem;
  font-weight: 600;
}
/* ─── [CHANGE 2026-09-18 BP-R27E-STRIPE-NODE] NAGRODA ZA WĘZEŁ 10 ───────────
   User: „oprócz 320 eXP dodaj pogląd ze belka jeszcze jest". Slot stoi POD
   nagrodami (XP + czaszki), bo jest nagrodą o innym charakterze: nie za zadanie,
   a za CAŁY kontynent. Osobny wiersz to jedyny sposób, żeby to pokazać bez
   sugerowania, że to trzecia moneta obok czaszek.

   [CHANGE 2026-09-18 BP-R27E-3-FINISH-REWARD] DWA RODZAJE NAGRODY, DWIE
   PROPORCJE GRAFIKI. Slot nosi teraz albo wstęgę (~4:1), albo ikonę przedmiotu
   (booster, ~1:1). Trzymanie JEDNEGO rozmiaru dla obu kończyło się tym, że
   kwadratowa ikona boostera była malutka (wpisana w prostokąt), a wstęga
   ścieśniona. Dlatego klasa `is-item` (ustawiana w `battlepass.js`) przestawia
   wymiary obrazka:
     • domyślnie (wstęga)  → 168×42, `object-fit: contain`,
     • `.is-item`          → 52×52,  `object-fit: contain`.
   ⚠️ Wstęgi mają proporcje ~4:1 (512x128), więc NIGDY kwadrat — wciśnięcie ich
   w kwadrat dawało zniekształcenie (naprawiane już raz w panelu Achievements).
   ⚠️ `hidden` MUSI wygrywać z `display: flex` — bez tego pusty slot pokazywałby
   się na węzłach 1-9 (patrz `[hidden] { display: none !important }` niżej). */
body.theme-tactical .bp-route-node-brief-reward-stripe {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 62px;
  padding: 8px 12px;
  border-color: rgba(217, 164, 65, 0.28);
  background: linear-gradient(90deg, rgba(217, 164, 65, 0.1), rgba(22, 13, 23, 0.72));
}
body.theme-tactical .bp-route-node-brief-reward-stripe[hidden] { display: none !important; }
body.theme-tactical .bp-route-node-stripe-img {
  flex: 0 0 auto;
  width: 168px;
  height: 42px;
  object-fit: contain;
  filter: drop-shadow(0 0 6px rgba(217, 164, 65, 0.45));
}
/* Ikona przedmiotu (booster) — osobne wymiary, patrz komentarz wyżej. */
body.theme-tactical .bp-route-node-brief-reward-stripe.is-item .bp-route-node-stripe-img {
  width: 52px;
  height: 52px;
}
/* ═══ [FIX 2026-09-25 BP-R86-STRIPE-LEFT] WYRÓWNANIE NAGRODY PRZEDMIOTOWEJ DO LEWEJ ═══
   ─────────────────────────────────────────────────────────────────────────────
   ZGŁOSZENIE WŁAŚCICIELA (R86 pkt 1): „W Australii i Azji vipy za task 10 są
   wyśrodkowane, a inne itemy wyrównane do lewej - wyrównaj w obu przypadkach
   vipy do lewej".

   PRZYCZYNA (ZMIERZONA, nie zgadnięta — pomiar w przeglądarce na prawdziwych
   arkuszach produkcyjnych): ten slot nosi DWIE klasy — `bp-route-node-brief-reward`
   i `bp-route-node-brief-reward-stripe`. Pierwsza ma w `site.css`:
       .bp-route-node-brief-reward { display:flex; align-items:center;
                                     justify-content:center; text-align:center }
   Obie klasy mają tę samą specyficzność (0,1,0), a `justify-content: center`
   NIE jest nadpisane w warstwie motywu → slot wstęgi/boostera CENTRUJE treść.
   Zmierzone: pierwsze dziecko slotu startowało na 147 px od lewej, podczas gdy
   wiersz nagrody przedmiotowej (`.bp-route-node-item-one`) — na 0 px.
   Dokładnie ta różnica, którą widzi gracz: VIP w Australii/Azji na środku,
   a golden box w pozostałych kontynentach przy lewej krawędzi.

   ⚠️ DLACZEGO `Australia`/`Azja`, a nie „wszystkie taski 10": te dwa kontynenty
   NIE mają wstęgi (`aw_strip_*`), więc ich nagroda za `ct_*_10` renderuje się
   w TYM slocie jako `kind:'item'` (VIP Booster). Na czterech pozostałych
   kontynentach slot pokazuje WSTĘGĘ (4:1 banner), a booster idzie do slotu
   przedmiotowego — czyli do układu, który już jest wyrównany do lewej. Dlatego
   objaw dotyczy wyłącznie Australii i Azji, mimo że reguła jest wspólna.

   ⚠️⚠️ CELOWO TYLKO `.is-item`, NIE CAŁY SLOT. Wstęga (banner ~4:1) jest grafiką
   o innym charakterze niż ikona 52×52 — przy wyrównaniu do lewej zostawiłaby
   pustą przestrzeń po prawej, co dla banera jest poprawne wizualnie tylko przy
   centrowaniu. Właściciel prosił o wyrównanie VIP-ów (przedmiotów), nie o
   przeprojektowanie wstęg. Klasa `is-item` jest ustawiana w `battlepass.js`
   (`updateRouteNodeStripe`) dokładnie wtedy, gdy slot niesie przedmiot.
   ⚠️ `text-align: left` idzie razem — `site.css` ustawia `center`, a em/strong
   wewnątrz `.bp-route-node-stripe-meta` dziedziczyłyby wyśrodkowanie tekstu
   przy zawinięciu długiej nazwy (np. „Mega VIP Booster"). */
body.theme-tactical .bp-route-node-brief-reward-stripe.is-item {
  justify-content: flex-start;
  text-align: left;
}
body.theme-tactical .bp-route-node-brief-reward-stripe.is-item .bp-route-node-stripe-meta {
  /* Ten sam układ co `.bp-route-node-item-meta` w slocie przedmiotowym
     (kolumna: nazwa nad czasem), żeby oba warianty nagrody czytały się identycznie. */
  align-items: flex-start;
  text-align: left;
}
body.theme-tactical .bp-route-node-stripe-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
body.theme-tactical .bp-route-node-stripe-meta strong {
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #e8e2d6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-route-node-stripe-meta em {
  font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.74rem;
  letter-spacing: 0.06em;
  color: var(--signal);
}

/* ─── [CHANGE 2026-09-18 BP-R27E-3-TIP] NAZWA NAGRODY PO NAJECHANIU ──────────
   User: „Jak klikne na node z taskiem i najade na nagrode to chce zeby po
   najechaniu wyswietlała się nazwa tego przedmiotu".
   Podpowiedź rysujemy w CSS (a nie natywnym `title`), bo natywny dymek pojawia
   się po ~1 s, nie da się go ostylować i wychodzi poza estetykę panelu.
   Tekst siedzi w `data-tip` (ustawiany w JS, bo nazwa zależy od kontynentu).
   ⚠️ `:not([data-tip=""])` jest KONIECZNE: slot nagrody ma w szablonie PUSTY
     `data-tip` (nazwę zna dopiero JS). Bez tego warunku nad każdą nagrodą bez
     nazwy wisiałby pusty czarny prostokąt.
   ⚠️ Panel briefu NIE ma `overflow: hidden` (sprawdzone) i `.bp-route-visual-wrap`
     też nie — podpowiedź może wyjść poza panel i nie zostanie obcięta. Gdyby ktoś
     kiedyś dodał tam `overflow`, dymki znikną, a wyglądać to będzie jak „przestało
     działać".
   ⚠️⚠️ [CHANGE 2026-09-18 BP-R27E-4-TIP-POINTER-EVENTS] `pointer-events: auto`
     JEST KONIECZNE — i to był powód zgłoszenia „najade na przedmiot to nie
     wyświetla się jego nazwa".
     BYŁO: `.bp-route-node-brief` ma `pointer-events: none` (panel jest warstwą
     informacyjną nad mapą, więc kliknięcia przechodziły do pinezek). Ale
     `pointer-events: none` działa KASKADOWO na dzieci — także na kafelki nagród.
     Element bez zdarzeń wskaźnika **nigdy nie dostaje `:hover`**, więc
     `::after` nie miał kiedy się pokazać. Podpowiedź była w kodzie, w CSS,
     z przetłumaczonym tekstem — i nie mogła zadziałać z definicji.
     JEST: kafelki z niepustym `data-tip` wracają do zdarzeń wskaźnika.
     ⚠️ Skutek uboczny jest obsłużony w `battlepass.js`: kliknięcie w panel
     ZAMYKA podgląd, żeby kafelek nie zasłaniał na stałe pinezki pod sobą
     (inaczej gracz nie mógłby wybrać węzła, który akurat leży pod panelem). */
body.theme-tactical [data-tip]:not([data-tip=""]) {
  position: relative;
  pointer-events: auto;
}
body.theme-tactical [data-tip]:not([data-tip=""])::after {
  content: attr(data-tip);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%);
  z-index: 40;
  max-width: 260px;
  padding: 6px 10px;
  border-radius: 7px;
  border: 1px solid rgba(255, 194, 71, 0.4);
  background: rgba(8, 6, 11, 0.97);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.6);
  color: #ffe9c4;
  font: 600 11px/1.25 'Rajdhani', 'Exo 2', sans-serif;
  letter-spacing: 0.04em;
  text-transform: none;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease;
}
body.theme-tactical [data-tip]:not([data-tip=""])::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: calc(100% + 3px);
  transform: translateX(-50%) rotate(45deg);
  z-index: 41;
  width: 8px;
  height: 8px;
  border-right: 1px solid rgba(255, 194, 71, 0.4);
  border-bottom: 1px solid rgba(255, 194, 71, 0.4);
  background: rgba(8, 6, 11, 0.97);
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease;
}
body.theme-tactical [data-tip]:not([data-tip=""]):hover::after,
body.theme-tactical [data-tip]:not([data-tip=""]):hover::before {
  opacity: 1;
}
/* ─── [CHANGE 2026-09-18 BP-R27E-5-ITEM-REWARD-VISIBLE] SLOT NAGRODY ─────────
   NAGRODY PRZEDMIOTOWEJ ZADANIA (panel węzła mapy).
   User: „w step 4 nie ma nagrody w ktorej jest Atlas Weapons! Napraw" oraz
   „W Europie za zadnanie za poziom 9 w dalszym ciagu nie ma Atlas Advance
   Weapons Box". Nagroda była w bazie, ale panel pokazywał tylko XP i czaszki.
   Styl celowo inny od wstęgi (inny rodzaj nagrody): ikona kwadratowa +
   nazwa z czasem trwania, na pełnym wierszu.
   ⚠️ `hidden` MUSI wygrywać z `display: flex` — bez tego pusty slot pokazywałby
     się na każdym węźle bez nagrody przedmiotowej. */
body.theme-tactical .bp-route-node-brief-reward-item {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: 58px;
  padding: 7px 12px;
  border-color: rgba(120, 190, 255, 0.3);
  background: linear-gradient(90deg, rgba(120, 190, 255, 0.1), rgba(22, 13, 23, 0.72));
  /* ⚠️⚠️ [CHANGE 2026-09-18 BP-R27E-6-REWARD-NAME-ROW] `grid-column: 1 / -1`
     JEST KONIECZNE — bez tego slot wpada do JEDNEJ z trzech kolumn siatki
     (`.bp-route-node-brief-rewards`, `repeat(3, minmax(0, 1fr))` w `site.css`),
     czyli dostaje ~137 px i nazwa broni zawija się na TRZY linie mimo wolnego
     miejsca w panelu (460 px). Dokładnie ten sam błąd dotknął w R27e-3 wstęgę
     kontynentu — tam naprawa była w `site.css`, tutaj reguła żyje w motywie,
     więc MUSI być powtórzona w tej warstwie. Zmierzone: 137 px -> 426 px. */
  grid-column: 1 / -1;
}
body.theme-tactical .bp-route-node-brief-reward-item[hidden] { display: none !important; }
body.theme-tactical .bp-route-node-item-img {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  object-fit: contain;
  filter: drop-shadow(0 0 6px rgba(120, 190, 255, 0.4));
}
/* [CHANGE 2026-09-24 BP-R84-NODE-MULTI-ITEM] WIERSZ NA JEDNA NAGRODE PRZEDMIOTOWA.
   ───────────────────────────────────────────────────────────────────────────
   Slot `#bp-route-node-item` renderuje teraz KILKA nagród (R84 pkt 4: booster
   ALBO wstęga + `Atlas Golden Box`), więc ikona+nazwa muszą być w osobnym
   wierszu, a wiersze ułożone jeden pod drugim.
   ⚠️ Reguły `grid-column: 1 / -1` i `[hidden]` zostają NA RODZICU — kontener
   nadal jest JEDNYM elementem siatki, niezależnie od liczby dzieci. */
body.theme-tactical .bp-route-node-items {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  align-items: stretch;
}
body.theme-tactical .bp-route-node-item-one {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
body.theme-tactical .bp-route-node-item-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* ⚠️ `min-width: 0` jest WARUNKIEM działania zawijania: bez tego element flex
     nie może być węższy niż jego treść i długa nazwa wyjechałaby poza panel
     zamiast się zawinąć. */
  min-width: 0;
}
/* [CHANGE 2026-09-18 BP-R27E-6-REWARD-NAME-ROW] NAZWA BEZ OBCINANIA.
   BYŁO: `white-space: nowrap; overflow: hidden; text-overflow: ellipsis`.
   ZGŁOSZENIE USERA: „przerób widok — pełne nazwy nie są widoczne".
   Wielokropek na NAZWIE PRZEDMIOTU gubi dokładnie tę część, po którą gracz
   patrzy („Atlas PM-84 Glau…"). Dlatego pozwalamy nazwie zająć do 2 linii:
   panel węzła ma stałą szerokość (~460 px), więc przy 2 liniach nadal mieści
   się w kafelku (`min-height` rośnie razem z treścią, bo to flex-kolumna).
   ⚠️ `overflow-wrap: anywhere` obsługuje też nazwy bez spacji — inaczej długi
   token rozepchnąłby panel w poziomie. */
body.theme-tactical .bp-route-node-item-meta strong {
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.25;
  color: #dbe9fb;
  overflow-wrap: anywhere;
}
body.theme-tactical .bp-route-node-item-meta em {
  font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: #9fd0ff;
}

body.theme-tactical .bp-token-icon {
  width: 30px;
  height: 30px;
}
body.theme-tactical .bp-token-icon-img,
body.theme-tactical .bp-token-icon-fallback {
  width: 26px;
  height: 26px;
  object-fit: contain;
  filter: drop-shadow(0 0 8px rgba(255, 194, 71, 0.42));
}
body.theme-tactical .bp-route-task-token-row strong,
body.theme-tactical .bp-route-task-tile strong {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.24rem;
  font-weight: 600;
}
/* Inspektor zadania (zakladka TASKS) — nagrody wieksze. */
body.theme-tactical .bp-quest-reward-icon { width: 52px; height: 52px; padding: 4px; }
body.theme-tactical .bp-quest-reward-value {
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.34rem;
  font-weight: 600;
}

/* ---------------------------------------------------------------------------
   4) REROLL — user pkt 4: przelosowanie dziennego zadania. Pierwsze dnia FREE,
   kolejne 50 / 60 / 70... Skulls.
   --------------------------------------------------------------------------- */
body.theme-tactical .bp-task-reroll {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid rgba(255, 138, 43, 0.4);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255, 138, 43, 0.16), rgba(255, 138, 43, 0.05));
  color: #ffc247;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform .14s ease, box-shadow .14s ease, border-color .14s ease, background .14s ease;
}
body.theme-tactical .bp-task-reroll:hover:not(:disabled) {
  transform: translateY(-1px);
  border-color: rgba(255, 194, 71, 0.75);
  box-shadow: 0 0 16px rgba(255, 194, 71, 0.26);
}
body.theme-tactical .bp-task-reroll:focus-visible {
  outline: 2px solid rgba(255, 194, 71, 0.6);
  outline-offset: 2px;
}
body.theme-tactical .bp-task-reroll:disabled,
body.theme-tactical .bp-task-reroll.is-locked {
  opacity: 0.42;
  cursor: not-allowed;
  border-color: var(--hair-2);
  background: none;
  color: var(--muted);
}
body.theme-tactical .bp-task-reroll-ico { font-size: 0.9rem; line-height: 1; }
body.theme-tactical .bp-task-reroll-cost.is-free {
  color: #5ef5a8;
}
body.theme-tactical .bp-task-reroll.is-busy { opacity: 0.6; pointer-events: none; }
body.theme-tactical .bp-task-reroll.is-error {
  border-color: rgba(255, 96, 96, 0.7);
  color: #ffb9b9;
}
body.theme-tactical .bp-badge-reroll {
  background: rgba(255, 194, 71, 0.08);
  border-color: rgba(255, 194, 71, 0.28);
  color: #ffc247;
}
body.theme-tactical .bp-section-badge strong { color: #ffe6b0; }

/* [CHANGE 2026-09-17 BP-R25-BADGE-SLASH] Ten sam separator co w `.bp-tab-badge`
   (user pkt 3: „0/21 wyglada jak 9"). Plakietki sekcji („2/9 COMPLETE",
   „4/21 UNLOCKED") mialy identyczny problem — cyfry zlewaly sie z ukosnikiem.
   ⚠️ Reguła MUSI stac PO `.bp-section-badge` (linia ~2219 dla tla/obramowania),
   bo przy rownej specyficznosci rozstrzyga kolejnosc w arkuszu. */
body.theme-tactical .bp-section-badge > i {
  font-style: normal;
  margin: 0 4px;
  opacity: 0.55;
  font-weight: 400;
}

/* ---------------------------------------------------------------------------
   5) ACHIEVEMENTS — user pkt 5: "kolory są bardzo takie przytłumione / szare /
   wyblakłe - usuń ten filtr [...]. Dodatkowo wykorzystaj całe miejsce dostępne
   dla squad badges / service marks." + 5b) "Squad Badges - jest ich dość sporo -
   możesz to rozbić na dwie linijki".
   Filtry `grayscale/brightness` ZDJETE. Siatki wypelniaja cala szerokosc:
   odznaki 5 kolumn (9 szt. = 5 + 4 = dwa rzedy), rangi 4 kolumny (8 szt. = 2 rzedy).
   --------------------------------------------------------------------------- */
body.theme-tactical .bp-ach-strip-art img,
body.theme-tactical .bp-ach-tile-ico {
  filter: none;
}
body.theme-tactical .bp-ach-strip.is-unlocked .bp-ach-strip-art img,
body.theme-tactical .bp-ach-tile.is-unlocked .bp-ach-tile-ico { filter: none; }
/* Delikatne rozjasnienie TYLKO dla niezablokowanych — bez odbarwiania. */
body.theme-tactical .bp-ach-strip:not(.is-unlocked) .bp-ach-strip-art img { opacity: 0.82; }
body.theme-tactical .bp-ach-tile:not(.is-unlocked) .bp-ach-tile-ico { opacity: 0.78; }
body.theme-tactical .bp-ach-strip.is-unlocked .bp-ach-strip-art img,
body.theme-tactical .bp-ach-tile.is-unlocked .bp-ach-tile-ico { opacity: 1; }

body.theme-tactical .bp-ach-group[data-ach-group="badge"] .bp-ach-tile-grid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
body.theme-tactical .bp-ach-group[data-ach-group="mark"] .bp-ach-tile-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
body.theme-tactical .bp-ach-tile {
  padding: 16px 14px 14px;
}
body.theme-tactical .bp-ach-tile-art { height: 104px; }
body.theme-tactical .bp-ach-tile-ico { width: 92px; height: 92px; }
body.theme-tactical .bp-ach-tile-title { font-size: 0.9rem; }
@media (max-width: 1180px) {
  body.theme-tactical .bp-ach-group[data-ach-group="badge"] .bp-ach-tile-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  body.theme-tactical .bp-ach-group[data-ach-group="mark"] .bp-ach-tile-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  body.theme-tactical .bp-ach-group[data-ach-group="badge"] .bp-ach-tile-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  body.theme-tactical .bp-ach-group[data-ach-group="mark"] .bp-ach-tile-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  body.theme-tactical .bp-ach-group[data-ach-group="badge"] .bp-ach-tile-grid,
  body.theme-tactical .bp-ach-group[data-ach-group="mark"] .bp-ach-tile-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.theme-tactical .bp-ach-tile-ico { width: 64px; height: 64px; }
  body.theme-tactical .bp-ach-tile-art { height: 76px; }
}

/* 5a) TOOLTIP — "po najechaniu na dany achievement niech wyskakuje takie lekkie
   małe okienko informujące co gracz powinien zrobić żeby zdobyć dany achievment". */
body.theme-tactical .bp-ach-strip,
body.theme-tactical .bp-ach-tile { position: relative; }
body.theme-tactical .bp-ach-tip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 10px);
  transform: translateX(-50%) translateY(6px);
  z-index: 30;
  width: max-content;
  max-width: 320px;
  padding: 10px 12px;
  border: 1px solid rgba(255, 138, 43, 0.42);
  border-radius: 10px;
  background: rgba(10, 7, 13, 0.97);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.62), 0 0 20px rgba(255, 138, 43, 0.14);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .16s ease, transform .16s ease, visibility .16s ease;
}
body.theme-tactical .bp-ach-tip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 7px solid transparent;
  border-top-color: rgba(255, 138, 43, 0.42);
}
body.theme-tactical .bp-ach-tip-k {
  display: block;
  margin-bottom: 4px;
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #ffc247;
}
body.theme-tactical .bp-ach-tip-v {
  display: block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  line-height: 1.5;
  color: #cfc7d4;
}
body.theme-tactical .bp-ach-strip:hover .bp-ach-tip,
body.theme-tactical .bp-ach-strip:focus-visible .bp-ach-tip,
body.theme-tactical .bp-ach-tile:hover .bp-ach-tip,
body.theme-tactical .bp-ach-tile:focus-visible .bp-ach-tip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}
/* Kafelki w drugim rzedzie maja tooltip pod spodem, zeby nie uciekal za ekran
   przy pierwszym rzedzie (tam miejsca jest malo). */
body.theme-tactical .bp-ach-strip-grid .bp-ach-tip,
body.theme-tactical .bp-ach-tile-grid .bp-ach-tip { bottom: calc(100% + 10px); }

/* ---------------------------------------------------------------------------
   6) MODAL — user pkt 7: "W show full content daj większe % chance np Main
   Rewards 2% chance - te 2% chance chce żeby było większe - podobnie niżej
   boosters & currencies - chance żeby było większe".
   --------------------------------------------------------------------------- */
body.theme-tactical .bp-chest-modal-group-chance {
  padding: 5px 14px;
  border-width: 1px;
  border-radius: 4px;
  font-family: 'Oswald', 'Rajdhani', sans-serif;
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: #ffd479;
  background: rgba(255, 194, 71, 0.1);
  text-shadow: 0 0 12px rgba(255, 194, 71, 0.22);
}
body.theme-tactical .bp-chest-modal-group-chance.is-unknown {
  font-size: 0.8rem;
  color: #8a8490;
  text-shadow: none;
}
body.theme-tactical .bp-chest-modal-group-title {
  font-size: 1.12rem;
}

/* ===========================================================================
   [CHANGE 2026-09-16 BP-R22]  Ekonomia + UX „topowego battle passa":
     • licznik litosci (pity) na kartach skrzyn,
     • zestawy kolekcjonerskie,
     • animowana odslona nagrody (reward reveal),
     • CLAIM ALL dla zadan, historia portfela,
     • mobile + focus-visible (dostepnosc).
   Motyw: Tactical Black (`body.theme-tactical`), paleta jak reszta BP.
   =========================================================================== */

/* ---- 0. PANEL MODALU = kontekst pozycjonowania odslony nagrody ------------- */
/* Odslona (`.bp-chest-reveal`) jest `position:absolute; inset:0`, wiec panel
   musi byc `position:relative` i przycinac zawartosc (`overflow:hidden`),
   inaczej animacja wychodzi poza ramke. */
body.theme-tactical .bp-chest-modal-panel { position: relative; overflow: hidden; }

/* ---- 1. CLAIM ALL (naglowek sekcji TASKS) -------------------------------- */
body.theme-tactical .bp-claim-all {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-left: 6px;
  padding: 5px 12px 5px 10px;
  font-family: 'Oswald', sans-serif;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: #1a1206;
  background: linear-gradient(180deg, #ffc247, #f0a01f);
  border: 1px solid #ffd97a;
  box-shadow: 0 0 14px rgba(255, 194, 71, 0.28);
  cursor: pointer;
  transition: transform 0.14s ease, box-shadow 0.18s ease, filter 0.18s ease;
}
body.theme-tactical .bp-claim-all:hover {
  transform: translateY(-1px);
  box-shadow: 0 0 20px rgba(255, 194, 71, 0.5);
  filter: brightness(1.05);
}
body.theme-tactical .bp-claim-all:active { transform: translateY(0); }
body.theme-tactical .bp-claim-all[disabled] { opacity: 0.6; cursor: progress; transform: none; }
body.theme-tactical .bp-claim-all-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  background: rgba(14, 10, 4, 0.82);
  color: #ffc247;
}

/* ---- 2. LICZNIK LITOSCI (PITY) NA KARCIE SKRZYNI ------------------------- */
body.theme-tactical .bp-chest-pity {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  margin: 8px 0 2px;
  padding: 5px 8px;
  background: rgba(10, 7, 16, 0.72);
  border: 1px solid var(--hair);
}
body.theme-tactical .bp-chest-pity-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.52rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
body.theme-tactical .bp-chest-pity-bar {
  display: block;
  height: 5px;
  background: #0a0710;
  border: 1px solid var(--hair);
  overflow: hidden;
}
body.theme-tactical .bp-chest-pity-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #6b4a86, #c07ad6);
  transition: width 0.4s ease;
}
body.theme-tactical .bp-chest-pity-v {
  font-family: 'Oswald', sans-serif;
  font-size: 0.82rem;
  color: #d8b4e8;
  min-width: 18px;
  text-align: right;
}
/* Gwarancja gotowa do uzycia — pasek i licznik zaploniete. */
body.theme-tactical .bp-chest-pity.is-ready { border-color: #ffc247; background: rgba(46, 30, 4, 0.6); }
body.theme-tactical .bp-chest-pity.is-ready .bp-chest-pity-fill { background: linear-gradient(90deg, #f0a01f, #ffd97a); }
body.theme-tactical .bp-chest-pity.is-ready .bp-chest-pity-v { color: #ffc247; }
body.theme-tactical .bp-chest-pity.is-ready .bp-chest-pity-k { color: #ffc247; }

/* ---- 3. ZESTAWY KOLEKCJONERSKIE ----------------------------------------- */
body.theme-tactical .bp-sets { margin-top: 26px; }
body.theme-tactical .bp-sets-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
  gap: 12px;
}
body.theme-tactical .bp-set-card {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 13px 14px;
  background: linear-gradient(155deg, rgba(30, 14, 38, 0.6), rgba(12, 9, 16, 0.82));
  border: 1px solid var(--hair);
  border-left: 2px solid #3a2b45;
  transition: border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
body.theme-tactical .bp-set-card.is-complete {
  border-left-color: #ffc247;
  box-shadow: 0 0 18px rgba(255, 194, 71, 0.14);
}
body.theme-tactical .bp-set-card.is-claimed { opacity: 0.72; border-left-color: #5ef5a8; }
body.theme-tactical .bp-set-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
body.theme-tactical .bp-set-title {
  font-family: 'Oswald', sans-serif;
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #f4e9f6;
}
body.theme-tactical .bp-set-count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  color: var(--muted);
  white-space: nowrap;
}
body.theme-tactical .bp-set-bar { height: 5px; background: #0a0710; border: 1px solid var(--hair); overflow: hidden; }
body.theme-tactical .bp-set-fill { display: block; height: 100%; background: linear-gradient(90deg, #ff4081, #ff8ec0); transition: width 0.4s ease; }
body.theme-tactical .bp-set-card.is-complete .bp-set-fill { background: linear-gradient(90deg, #f0a01f, #ffd97a); }
body.theme-tactical .bp-set-items {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 0;
  padding: 0;
  list-style: none;
}
body.theme-tactical .bp-set-item { display: flex; align-items: center; gap: 7px; min-width: 0; }
body.theme-tactical .bp-set-item-dot {
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  background: #3a2b45;
  border: 1px solid var(--hair-2);
}
body.theme-tactical .bp-set-item.is-owned .bp-set-item-dot { background: #5ef5a8; border-color: #5ef5a8; box-shadow: 0 0 7px rgba(94, 245, 168, 0.6); }
body.theme-tactical .bp-set-item-name {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  color: #7d7789;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-set-item.is-owned .bp-set-item-name { color: #d6f7e6; }
body.theme-tactical .bp-set-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--hair);
}
body.theme-tactical .bp-set-reward { display: inline-flex; align-items: center; gap: 5px; color: #f0a0c0; }
body.theme-tactical .bp-set-reward strong { font-family: 'Oswald', sans-serif; font-size: 0.94rem; color: #ff8ec0; }
body.theme-tactical .bp-set-claimed,
body.theme-tactical .bp-set-locked {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.56rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-set-claimed { color: #5ef5a8; }

/* ---- 4. ODSLONA NAGRODY (REWARD REVEAL) --------------------------------- */
/* [CHANGE 2026-09-18 BP-R27-REVEAL-OUTSIDE-MODAL] `absolute` -> `fixed`.
   ─────────────────────────────────────────────────────────────────────────
   BYLO: `position: absolute; inset: 0` — odsłona pozycjonowała się wobec
   `.bp-chest-modal-panel`, a ponieważ siedziała W ŚRODKU `#bp-chest-modal`
   (który ma `hidden`), przycisk „OPEN FOR X SKULLS" na karcie skrzyni
   przyznawał nagrodę, ale odsłony NIE DAŁO SIĘ zobaczyć aż do otwarcia
   „Show all contents". Zgłoszenie pkt 3 tury 14.

   JEST: odsłona jest rodzeństwem modala (patrz `battlepass.ejs`) i pokrywa
   CAŁY viewport niezależnie od tego, czy modal jest otwarty. `z-index: 1260`
   jest POWYŻEJ modala (1200), więc ta sama odsłona obsługuje obie ścieżki:
   przycisk na karcie i rolkę w modalu. */
body.theme-tactical .bp-chest-reveal {
  position: fixed;
  inset: 0;
  z-index: 1260;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 42%, rgba(58, 18, 52, 0.92), rgba(6, 5, 10, 0.97));
  backdrop-filter: blur(3px);
}
body.theme-tactical .bp-chest-reveal[hidden] { display: none; }
body.theme-tactical .bp-chest-reveal-rays {
  position: absolute;
  width: 420px;
  height: 420px;
  opacity: 0;
  pointer-events: none;
  background: conic-gradient(from 0deg,
    rgba(255, 64, 129, 0.3), transparent 12%,
    rgba(255, 194, 71, 0.26), transparent 26%,
    rgba(255, 64, 129, 0.3), transparent 40%,
    rgba(192, 122, 214, 0.28), transparent 56%,
    rgba(255, 64, 129, 0.3), transparent 72%,
    rgba(255, 194, 71, 0.26), transparent 88%,
    rgba(255, 64, 129, 0.3));
  mask-image: radial-gradient(circle, #000 30%, transparent 72%);
  -webkit-mask-image: radial-gradient(circle, #000 30%, transparent 72%);
}
body.theme-tactical .bp-chest-reveal.is-live .bp-chest-reveal-rays { animation: bpRevealRays 1.5s ease-out 1 both; }
body.theme-tactical .bp-chest-reveal-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 26px 34px 22px;
  min-width: 262px;
  text-align: center;
  background: linear-gradient(160deg, rgba(38, 16, 46, 0.94), rgba(10, 8, 14, 0.97));
  border: 1px solid #ff4d92;
  box-shadow: 0 0 40px rgba(255, 64, 129, 0.34), inset 0 0 40px rgba(255, 64, 129, 0.08);
  opacity: 0;
}
/* [CHANGE 2026-09-20 BP-R29-RARITY] ODSLONA NAGRODY W KOLORZE RZADKOSCI.
   ─────────────────────────────────────────────────────────────────────────
   Te same kolory co na kafelkach modalu i w rolce (zielony rare / niebieski
   ultra), zeby gracz rozpoznal rzadkosc po kolorze NIEZALEZNIE od tego, gdzie
   ja zobaczy.
   ⚠️ KOLEJNOSC MA ZNACZENIE: te reguly stoja PRZED `.is-pity` (wiersz nizej),
   bo przy gwarancji obie klasy sa na odslonie jednoczesnie. Zlota poswiata
   gwarancji jest informacja nadrzedna — mowi „dlaczego", nie „co" — wiec
   wygrywa dzieki temu, ze stoi POZNEJ w arkuszu. */
body.theme-tactical .bp-chest-reveal.is-rare .bp-chest-reveal-card { border-color: #3ddc84; box-shadow: 0 0 46px rgba(61, 220, 132, 0.45), inset 0 0 40px rgba(61, 220, 132, 0.1); }
body.theme-tactical .bp-chest-reveal.is-ultra .bp-chest-reveal-card { border-color: #4d9fff; box-shadow: 0 0 50px rgba(77, 159, 255, 0.5), inset 0 0 40px rgba(77, 159, 255, 0.12); }
/* [CHANGE 2026-09-20 BP-R29d-GOLD-PITY] Odslona z GWARANCJI ZLOTA — najmocniejsza
   poswiata. Stoi PO `.is-pity`, wiec przy zbiegu klas wygrywa zloto (rzadsze). */
body.theme-tactical .bp-chest-reveal.is-gold .bp-chest-reveal-card { border-color: #ffc247; box-shadow: 0 0 60px rgba(255, 194, 71, 0.6), inset 0 0 46px rgba(255, 194, 71, 0.16); }
body.theme-tactical .bp-chest-reveal.is-live .bp-chest-reveal-card { animation: bpRevealPop 0.46s cubic-bezier(0.2, 1.3, 0.4, 1) 0.05s 1 both; }
body.theme-tactical .bp-chest-reveal.is-pity .bp-chest-reveal-card { border-color: #ffc247; box-shadow: 0 0 46px rgba(255, 194, 71, 0.45), inset 0 0 40px rgba(255, 194, 71, 0.1); }
body.theme-tactical .bp-chest-reveal-kicker {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.56rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-chest-reveal-art {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 148px;
  height: 148px;
  margin: 2px 0 4px;
  background: radial-gradient(circle, rgba(255, 64, 129, 0.16), transparent 68%);
}
body.theme-tactical .bp-chest-reveal-art img {
  max-width: 132px;
  max-height: 132px;
  object-fit: contain;
  filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.6));
}
body.theme-tactical .bp-chest-reveal-name {
  font-family: 'Oswald', sans-serif;
  font-size: 1.32rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  line-height: 1.16;
  color: #fff;
}
body.theme-tactical .bp-chest-reveal-amount {
  font-family: 'Oswald', sans-serif;
  font-size: 1.06rem;
  color: #ffc247;
}
/* [CHANGE 2026-09-18 BP-R27-DURATION] Jawny czas trwania nagrody.
   Odsłona mówi „6 hours" tak samo jak rolka i magazyn — wcześniej milczała,
   a jedyną liczbą, jaką gracz widział, było wymyślone „30 days" w magazynie. */
body.theme-tactical .bp-chest-reveal-duration {
  padding: 3px 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #cfe8ff;
  background: rgba(14, 30, 50, 0.7);
  border: 1px solid rgba(120, 180, 255, 0.4);
}
body.theme-tactical .bp-chest-reveal-duration[hidden] { display: none; }
body.theme-tactical .bp-chest-reveal-tag {
  margin-top: 2px;
  padding: 3px 9px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  color: #ffd97a;
  background: rgba(58, 34, 4, 0.72);
  border: 1px solid rgba(255, 194, 71, 0.5);
}
body.theme-tactical .bp-chest-reveal-tag[hidden] { display: none; }
body.theme-tactical .bp-chest-reveal-ok { margin-top: 10px; min-width: 128px; }

@keyframes bpRevealPop {
  0%   { opacity: 0; transform: scale(0.72) translateY(16px); }
  62%  { opacity: 1; transform: scale(1.06) translateY(0); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes bpRevealRays {
  0%   { opacity: 0; transform: rotate(0deg) scale(0.7); }
  38%  { opacity: 0.92; }
  100% { opacity: 0; transform: rotate(150deg) scale(1.18); }
}
@media (prefers-reduced-motion: reduce) {
  body.theme-tactical .bp-chest-reveal.is-live .bp-chest-reveal-card { animation: none; opacity: 1; }
  body.theme-tactical .bp-chest-reveal.is-live .bp-chest-reveal-rays { animation: none; }
}

/* ---- 5. HISTORIA PORTFELA ----------------------------------------------- */
body.theme-tactical .bp-wallet-history {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: stretch;
  padding: 0 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  background: rgba(10, 7, 16, 0.6);
  border: 1px solid var(--hair);
  cursor: pointer;
  transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}
body.theme-tactical .bp-wallet-history:hover {
  color: #ffc247;
  border-color: rgba(255, 194, 71, 0.5);
  background: rgba(46, 30, 4, 0.5);
}

/* ---- 6. PLANSZA OSIAGNIEC — plakietka „nagrody" ------------------------- */
body.theme-tactical .bp-badge-ach-reward { color: #ffc247; border-color: rgba(255, 194, 71, 0.4); }

/* ---- 7. DOSTEPNOSC: widoczny focus dla klawiatury ----------------------- */
body.theme-tactical .bp-tab:focus-visible,
body.theme-tactical .bp-claim-all:focus-visible,
body.theme-tactical .bp-task-reroll:focus-visible,
body.theme-tactical .bp-set-claim:focus-visible,
body.theme-tactical .bp-open-chest:focus-visible,
body.theme-tactical .bp-chest-contents-all:focus-visible,
body.theme-tactical .bp-wallet-history:focus-visible,
body.theme-tactical .bp-ach-strip:focus-visible,
body.theme-tactical .bp-ach-tile:focus-visible,
body.theme-tactical .bp-chest-reveal-ok:focus-visible {
  outline: 2px solid #ffc247;
  outline-offset: 2px;
}

/* ---- 8. MOBILE ---------------------------------------------------------- */
@media (max-width: 760px) {
  body.theme-tactical .bp-claim-all { width: 100%; margin: 8px 0 0; justify-content: center; }
  body.theme-tactical .bp-sets-grid { grid-template-columns: 1fr; }
  body.theme-tactical .bp-chest-pity { grid-template-columns: auto minmax(0, 1fr) auto; }
  body.theme-tactical .bp-chest-reveal-card { min-width: 0; width: min(88vw, 300px); padding: 20px 18px 18px; }
  body.theme-tactical .bp-chest-reveal-art { width: 116px; height: 116px; }
  body.theme-tactical .bp-chest-reveal-art img { max-width: 104px; max-height: 104px; }
  body.theme-tactical .bp-chest-reveal-name { font-size: 1.14rem; }
  body.theme-tactical .bp-wallet-history { width: 100%; justify-content: center; padding: 7px 12px; }
}

/* ---- 9. HISTORIA PORTFELA (modal) ---------------------------------------- */
body.theme-tactical.bp-modal-open { overflow: hidden; }
body.theme-tactical .bp-history-modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
body.theme-tactical .bp-history-modal[hidden] { display: none; }
body.theme-tactical .bp-history-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(4, 3, 7, 0.78);
  backdrop-filter: blur(4px);
}
body.theme-tactical .bp-history-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(560px, 94vw);
  max-height: min(76vh, 640px);
  background: linear-gradient(160deg, rgba(28, 14, 34, 0.97), rgba(9, 8, 12, 0.98));
  border: 1px solid #ff4d92;
  box-shadow: 0 0 40px rgba(255, 64, 129, 0.28);
}
body.theme-tactical .bp-history-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--hair);
}
body.theme-tactical .bp-history-title {
  font-family: 'Oswald', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ffe3f0;
}
body.theme-tactical .bp-history-close {
  width: 28px;
  height: 28px;
  font-size: 0.9rem;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--hair-2);
  cursor: pointer;
}
body.theme-tactical .bp-history-close:hover { color: #ff8ec0; border-color: #ff4d92; }
body.theme-tactical .bp-history-body {
  display: flex;
  flex-direction: column;
  gap: 1px;
  overflow-y: auto;
  padding: 4px 0;
}
body.theme-tactical .bp-history-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  padding: 9px 16px;
  border-bottom: 1px solid rgba(34, 40, 52, 0.5);
}
body.theme-tactical .bp-history-row-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  color: #cfc7d8;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-history-row-date {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  color: var(--muted);
  white-space: nowrap;
}
body.theme-tactical .bp-history-row-delta {
  font-family: 'Oswald', sans-serif;
  font-size: 0.94rem;
  color: #5ef5a8;
  min-width: 62px;
  text-align: right;
}
body.theme-tactical .bp-history-row.is-minus .bp-history-row-delta { color: #ff8a8a; }
body.theme-tactical .bp-history-loading { padding: 18px 16px; color: var(--muted); }
body.theme-tactical .bp-history-empty { margin: 0; padding: 22px 16px; text-align: center; color: var(--muted); font-size: 0.74rem; }

/* ---- 10. TABELA SEZONU --------------------------------------------------- */
body.theme-tactical .bp-board { margin-top: 28px; }
body.theme-tactical .bp-board[hidden] { display: none; }
body.theme-tactical .bp-board-head-row,
body.theme-tactical .bp-board-row {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) 74px 110px;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
}
body.theme-tactical .bp-board-head-row {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.54rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--hair-2);
}
body.theme-tactical .bp-board-list { margin: 0; padding: 0; list-style: none; }
body.theme-tactical .bp-board-row {
  border-bottom: 1px solid rgba(34, 40, 52, 0.45);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  color: #cfc7d8;
}
body.theme-tactical .bp-board-row:hover { background: rgba(255, 64, 129, 0.05); }
body.theme-tactical .bp-board-rank { font-family: 'Oswald', sans-serif; font-size: 0.86rem; color: var(--muted); }
body.theme-tactical .bp-board-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
body.theme-tactical .bp-board-level,
body.theme-tactical .bp-board-xp { text-align: right; }
body.theme-tactical .bp-board-xp { font-family: 'Oswald', sans-serif; font-size: 0.9rem; color: #ffc247; }
body.theme-tactical .bp-board-row.is-top-1 .bp-board-rank { color: #ffd97a; text-shadow: 0 0 10px rgba(255, 217, 122, 0.5); }
body.theme-tactical .bp-board-row.is-top-2 .bp-board-rank { color: #d9dee6; }
body.theme-tactical .bp-board-row.is-top-3 .bp-board-rank { color: #d99a6a; }
body.theme-tactical .bp-board-row.is-top .bp-board-name { color: #fff; }
body.theme-tactical .bp-board-empty { margin: 0; padding: 16px 14px; color: var(--muted); font-size: 0.72rem; }

@media (max-width: 760px) {
  body.theme-tactical .bp-board-head-row,
  body.theme-tactical .bp-board-row { grid-template-columns: 44px minmax(0, 1fr) 54px 84px; gap: 6px; padding: 7px 10px; }
  body.theme-tactical .bp-history-row { grid-template-columns: minmax(0, 1fr) auto; gap: 8px; }
  body.theme-tactical .bp-history-row-date { grid-column: 1; grid-row: 2; }
}

body.theme-tactical .bp-reroll-next { font-family: 'IBM Plex Mono', monospace; font-style: normal; font-size: 0.54rem; letter-spacing: 0.08em; color: var(--muted); margin-left: 4px; }
body.theme-tactical .bp-badge-reroll:hover .bp-reroll-next { color: #ffc247; }

/* ===========================================================================
   [CHANGE 2026-09-16 BP-R23]  Poprawki zgloszone przez wlasciciela:
     • 2 — modal: grupowanie wariantow w jednym wierszu + poprawna etykieta czasu
     • 3 — Collections: wieksza czcionka (bylo nieczytelne)
     • 4 — Leaderboard: zakladka + tabela nagrod
     • 8 — symulacja otwarc + licznik litosci w modalu
     • 9 — mobilny pasek nawigacji nie moze zajmowac pol ekranu
     • 10 — pasek XP nad aktualnym slotem wyzszy po osi Y
   =========================================================================== */

/* ---- 2. MODAL: wiersz „typ + warianty" ---------------------------------- */
body.theme-tactical .bp-chest-modal-grid.is-rows {
  grid-template-columns: 1fr;
  gap: 6px;
}
body.theme-tactical .bp-chest-loot.is-group {
  flex-direction: row;
  align-items: center;
  gap: 14px;
  padding: 9px 14px;
  min-height: 0;
}
body.theme-tactical .bp-chest-loot.is-group .bp-chest-loot-ico { width: 44px; height: 44px; flex: 0 0 44px; }
body.theme-tactical .bp-chest-loot.is-group .bp-chest-loot-name {
  /* ⚠️ `min-width` NIE moze byc 0: przy dlugich listach wariantow nazwa byla
     sciskana do 0 px i gracz widzial sam ikone, chipy i sume — bez informacji,
     CO w ogole dropi. 120 px gwarantuje czytelna nazwe (z elipsa). */
  flex: 1 1 auto;
  min-width: 120px;
  text-align: left;
  font-size: 0.86rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-chest-variants {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  justify-content: flex-end;
}
/* [CHANGE 2026-09-17 BP-R24-CHIP-SIZE] User: „te kwadraciki np (2h 2.45%) daj
   większe". Chip wariantu byl 2 px / 7 px i fontem 0.6 / 0.76 rem — na duzym
   ekranie praktycznie nieczytelny mimo ze to NAJWAZNIEJSZA liczba w wierszu.
   Teraz: wiekszy padding, 0.72 / 0.9 rem i wyrazna ramka dla etykiety. */
body.theme-tactical .bp-chest-variant {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 5px 10px;
  background: rgba(10, 7, 16, 0.8);
  border: 1px solid var(--hair-2);
}
body.theme-tactical .bp-chest-variant-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  font-weight: 600;
  color: #cfc7d8;
}
body.theme-tactical .bp-chest-variant-p {
  font-family: 'Oswald', sans-serif;
  font-size: 0.9rem;
  color: #ffc247;
}
/* [CHANGE 2026-09-17 BP-R25-TOTAL-SIMPLE] User pkt 6: „Total - powinno być po
   prostu okienkiem [...] usuwamy zbędne rzeczy". Etykieta „RAZEM" zniknęła —
   suma wariantów to teraz zwykłe okienko (jak pozostałe szanse w wierszu),
   wyróżnione tylko kolorem ramki, żeby dało się odróżnić sumę od pojedynczej szansy.

   ⚠️ `width: auto` JEST KRYTYCZNE: bazowa reguła `.bp-chest-loot-chance` ustawia
   `width: 100%` (bo w ukladzie KOLUMNOWYM ma byc pelnowymiarowym paskiem pod
   kafelkiem). W wierszu `flex-basis: auto` bierze te wlasnie 100%, wiec okienko
   rozciagalo sie na cala szerokosc i DOSLOWNIE było paskiem — a dodatkowo
   sciskalo nazwe przedmiotu do 0 px. To bylo zrodlo zgloszenia usera. */
body.theme-tactical .bp-chest-loot.is-group .bp-chest-loot-chance.is-total {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: auto;
  max-width: none;
  min-width: 74px;
  margin: 0 0 0 4px;
  padding: 6px 12px;
  border: 1px solid rgba(255, 77, 146, 0.5);
  border-top: 1px solid rgba(255, 77, 146, 0.5);
  font-family: 'Oswald', sans-serif;
  font-size: 0.98rem;
  font-weight: 600;
  color: #ffd9e8;
  background: rgba(58, 16, 44, 0.55);
}
body.theme-tactical .bp-chest-loot.is-group .bp-chest-loot-chance.is-total em {
  font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.56rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  color: rgba(255, 217, 232, 0.7);
}
/* Etykieta „1h"/„3h"/„6h" w chipie wariantu — wyśrodkowana i w kolumnie, zeby
   każdy booster miał identyczny układ „3 hours" + „x%". */
body.theme-tactical .bp-chest-variant-k { text-align: center; }
/* Na mobile warianty schodza pod nazwe (inaczej sciskaja sie do nieczytelnosci). */
@media (max-width: 620px) {
  body.theme-tactical .bp-chest-loot.is-group { flex-wrap: wrap; gap: 8px; }
  body.theme-tactical .bp-chest-loot.is-group .bp-chest-loot-name { flex: 1 1 calc(100% - 60px); }
  body.theme-tactical .bp-chest-variants { flex: 1 1 100%; justify-content: flex-start; }
}

/* ---- 3. ZESTAWY KOLEKCJONERSKIE — WIEKSZA CZCIONKA ---------------------- */
/* User: „czcionka jest tak mała że cięzko się to czyta". Podnosimy WSZYSTKIE
   teksty karty zestawu (wczesniej 0.6–0.88rem = 9–14 px). */
body.theme-tactical .bp-sets-grid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
body.theme-tactical .bp-set-card { padding: 16px 17px; gap: 11px; }
body.theme-tactical .bp-set-title { font-size: 1.06rem; letter-spacing: 0.04em; }
body.theme-tactical .bp-set-count { font-size: 0.82rem; }
body.theme-tactical .bp-set-item-name { font-size: 0.78rem; }
body.theme-tactical .bp-set-item-dot { width: 8px; height: 8px; }
body.theme-tactical .bp-set-reward strong { font-size: 1.14rem; }
body.theme-tactical .bp-set-claimed,
body.theme-tactical .bp-set-locked { font-size: 0.7rem; }
body.theme-tactical .bp-set-claim { font-size: 0.78rem; padding: 6px 14px; }
body.theme-tactical .bp-set-bar { height: 7px; }

/* ---- 3c. ZESTAWY KOLEKCJONERSKIE — WYKORZYSTANIE MIEJSCA (R26) ---------- */
/* [CHANGE 2026-09-17 BP-R26-COLLECTIONS-BIGGER] User pkt 1 tury 13:
   „Collections - zrób większe te kwadraty i nazwy i odstępy - to jest dedykowana
   strona od tego mozemy fajnie wykorzystać miejsce".

   KLUCZOWE ZDANIE W ZGŁOSZENIU: „to jest DEDYKOWANA strona od tego". Karta
   zestawu nie jest już dodatkiem pod listą skrzyń (jak w R22) — ma własną
   zakładkę i całą szerokość, więc nie ma powodu, żeby jej elementy były
   rozmiaru „przypisu". Poprzednie powiększenie (0.6 -> 0.78rem) było
   połowiczne: nadal czytało się to jak mikro-tekst.

   ⚠️ „KWADRATY" ZE ZGŁOSZENIA TO `bp-set-item-dot`. To 8 px kropka, która przy
   4 pozycjach w rzędzie wygląda jak przypadkowy pyłek, a niesie najważniejszą
   informację na karcie (posiadane / brak). Robimy z niej PLAMKĘ 14 px z jasnym
   obramowaniem — czytelną na pierwszy rzut oka, bez czytania nazw.

   ⚠️ KOLEJNOŚĆ: te reguły muszą być PO bloku „WIEKSZA CZCIONKA" powyżej
   (równa specyficzność -> wygrywa późniejsza). Stąd osobna, numerowana sekcja. */
body.theme-tactical .bp-sets-grid {
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: 20px;
}
body.theme-tactical .bp-set-card { padding: 22px 24px; gap: 15px; }
body.theme-tactical .bp-set-title { font-size: 1.3rem; }
body.theme-tactical .bp-set-count { font-size: 0.98rem; }
body.theme-tactical .bp-set-items { gap: 9px; }
body.theme-tactical .bp-set-item { gap: 12px; }
body.theme-tactical .bp-set-item-dot {
  width: 14px;
  height: 14px;
  border-width: 2px;
  border-radius: 2px;   /* „kwadrat", nie kropka — tak to nazwał user */
}
body.theme-tactical .bp-set-item-name { font-size: 1rem; }
body.theme-tactical .bp-set-bar { height: 10px; }
body.theme-tactical .bp-set-foot { padding-top: 13px; }
body.theme-tactical .bp-set-reward strong { font-size: 1.42rem; }
body.theme-tactical .bp-set-claimed,
body.theme-tactical .bp-set-locked { font-size: 0.84rem; }
body.theme-tactical .bp-set-claim { font-size: 0.92rem; padding: 9px 20px; }
/* Na wąskim ekranie kafle musza sie zmiescic — inaczej 360 px wymusza przewijanie
   poziome. To NIE jest „cofanie" powiekszenia, tylko jego granica. */
@media (max-width: 780px) {
  body.theme-tactical .bp-sets-grid { grid-template-columns: 1fr; gap: 14px; }
  body.theme-tactical .bp-set-card { padding: 16px 17px; gap: 11px; }
  body.theme-tactical .bp-set-title { font-size: 1.06rem; }
  body.theme-tactical .bp-set-item-name { font-size: 0.86rem; }
  body.theme-tactical .bp-set-item-dot { width: 11px; height: 11px; }
}

/* ---- 3d. MAGAZYN BATTLE PASSA (karta „Inventory") ----------------------- */
/* [CHANGE 2026-09-17 BP-R26-INVENTORY-CSS] User pkt 9 tury 13 — karta magazynu.
   ─────────────────────────────────────────────────────────────────────────
   Układ: siatka auto-fill (jak Collections), bo magazyn to zbiór
   RÓWNORZĘDNYCH przedmiotów. Tabela zmusiłaby do czytania kolumn; tutaj
   gracz rozpoznaje rzeczy po grafice.

   ⚠️ `grid-template-rows` NIE jest ustawiane sztywno — karta ma `min-height`,
   więc wysokość wyrównuje się w rzędzie, ale nie rozciąga sztucznie przy
   krótkiej nazwie. To ten sam błąd, który w R24 ścisnął nazwę przedmiotu
   w modalu skrzyń (patrz [CSS-MOBILE-GUARD]). */
body.theme-tactical .bp-inv { margin-top: 22px; }
body.theme-tactical .bp-inv-note {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 0 0 18px;
  padding: 11px 14px;
  background: rgba(14, 24, 40, 0.55);
  border: 1px solid var(--hair-2);
  border-left: 2px solid var(--signal);
  font-family: 'IBM Plex Mono', monospace;
  /* [CHANGE 2026-09-18 BP-R27E-4-INV-NOTE-FONT] 0.78 -> 0.92rem.
     User: „Inventory: Napis These iitems stay in your account - powieksz czcionkę".
     ⚠️ `IBM Plex Mono` przy tym samym `font-size` wygląda na MNIEJSZY niż
     proporcjonalny font (monospace ma niższą wysokość x), więc sam wzrost
     rozmiaru do 0.86rem nie wystarczyłby optycznie — dlatego 0.92rem plus
     nieco luźniejsza interlinia. */
  font-size: 0.92rem;
  line-height: 1.55;
  color: #c4bcd0;
}
body.theme-tactical .bp-inv-note svg { flex: 0 0 auto; margin-top: 2px; color: var(--signal); }
/* ─── [CHANGE 2026-09-18 BP-R27E-TRANSFER-STATUS] HISTORIA WYSYŁEK ─────────
   Zgłoszenie tury 18: „Jak wysyłam sobie przedmiot z inventory do gry - to
   ressurection coin przyszedł ale HE Grenade - nie dostałem powiadomień".
   ─────────────────────────────────────────────────────────────────────────
   Blok jest ŚWIADOMIE cichy wizualnie (hairline, mały monospace): to dziennik
   stanu, a nie ozdoba. Kropka po lewej niesie stan kolorem, żeby dało się go
   odczytać jednym spojrzeniem, bez czytania tekstu — bo gracz sprawdza to
   w pośpiechu, tuż po wysłaniu.
   ⚠️ Kropki `is-sub` (zamiennik) i `is-failed` mają OSOBNE kolory. Zamiennik
   to nie błąd (nagroda dotarła), ale i nie sukces (dotarła inna) — dlatego
   żółty sygnał, a nie zieleń ani czerwień. */
body.theme-tactical .bp-inv-tr {
  margin: 0 0 18px;
  padding: 12px 14px;
  background: rgba(11, 17, 28, 0.6);
  border: 1px solid var(--hair);
}
/* ─── [CHANGE 2026-09-18 BP-R27E-4-COLLAPSIBLE] ROZWIJANA HISTORIA WYSYŁEK ──
   User: „Recent Deliveries to game - przerób to żeby to było w formie rozwijanej
   listy - ma być defaultowo zwinięta - dodatkowo zwiększ czionke dla nagłówka".
   Użyty natywny `<details>`/`<summary>` (patrz widok). Stylujemy:
     • `summary` jako pełny wiersz klikalny (list-style: none zdejmuje natywną
       strzałkę, bo rysujemy własną — inaczej wygląda inaczej w każdej przeglądarce),
     • obrót strzałki sterowany atrybutem `[open]`, więc stan jest widoczny
       bez odwoływania się do JS.
   ⚠️ `cursor: pointer` i `user-select: none` — bez tego nagłówek wygląda jak
     zwykły tekst i nikt nie próbuje w niego kliknąć. */
body.theme-tactical .bp-inv-tr-summary {
  display: flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  user-select: none;
  list-style: none;
}
body.theme-tactical .bp-inv-tr-summary::-webkit-details-marker { display: none; }
body.theme-tactical .bp-inv-tr-caret {
  flex: 0 0 auto;
  width: 0;
  height: 0;
  border-left: 6px solid var(--signal);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform .15s ease;
}
body.theme-tactical .bp-inv-tr[open] .bp-inv-tr-caret { transform: rotate(90deg); }
body.theme-tactical .bp-inv-tr-count {
  margin-left: auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.8rem;
  color: var(--signal);
}
body.theme-tactical .bp-inv-tr[open] .bp-inv-tr-list { margin-top: 9px; }
body.theme-tactical .bp-inv-tr-title {
  margin: 0;
  font-family: 'IBM Plex Mono', monospace;
  /* [CHANGE 2026-09-18 BP-R27E-4-TR-TITLE-FONT] 0.68 -> 0.9rem.
     User: „dodatkowo zwiększ czionke dla nagłówka Recent Deliveries to game".
     Nagłówek jest teraz UCHWYTEM rozwijanej listy, więc musi wyglądać jak
     element klikalny, a nie jak etykieta pomocnicza. */
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #cbc2d6;
}
body.theme-tactical .bp-inv-tr-list { margin: 0; padding: 0; list-style: none; }
body.theme-tactical .bp-inv-tr-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.76rem;
  color: #d8d3ca;
}
body.theme-tactical .bp-inv-tr-row:first-child { border-top: 0; }
body.theme-tactical .bp-inv-tr-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--muted);
}
body.theme-tactical .bp-inv-tr-dot.is-pending { background: #d9a441; box-shadow: 0 0 6px rgba(217, 164, 65, 0.55); }
body.theme-tactical .bp-inv-tr-dot.is-sent { background: #4fb477; box-shadow: 0 0 6px rgba(79, 180, 119, 0.5); }
body.theme-tactical .bp-inv-tr-dot.is-na { background: #4fb477; }
body.theme-tactical .bp-inv-tr-dot.is-failed { background: #d1544f; box-shadow: 0 0 6px rgba(209, 84, 79, 0.55); }
body.theme-tactical .bp-inv-tr-dot.is-sub { background: #d9a441; }
body.theme-tactical .bp-inv-tr-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.theme-tactical .bp-inv-tr-amt { font-style: normal; color: var(--signal); }
body.theme-tactical .bp-inv-tr-status {
  flex: 0 0 auto;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--muted);
  text-align: right;
}
body.theme-tactical .bp-inv-tr-row[data-tr-status="sent"] .bp-inv-tr-status,
body.theme-tactical .bp-inv-tr-row[data-tr-status="na"] .bp-inv-tr-status { color: #6fc794; }
body.theme-tactical .bp-inv-tr-row[data-tr-status="failed"] .bp-inv-tr-status { color: #e0837e; }
body.theme-tactical .bp-inv-tr-time {
  flex: 0 0 auto;
  font-size: 0.68rem;
  color: #6d7484;
}
/* ⚠️ BŁYSK JEST JEDYNĄ „POWIADOMIENIEM" — dlatego musi być ograniczony w czasie.
   Zgłoszenie: „HE Grenade - nie dostałem powiadomień". Wiersz, który właśnie
   rozstrzygnął się NIE po myśli gracza (nie dostarczono albo dostarczono
   zamiennik), dostaje `is-alert` na 6 sekund. Stałe podświetlenie byłoby tłem,
   a nie sygnałem; brak podświetlenia oznaczałby, że gracz tego nie zauważy.
   `prefers-reduced-motion` wyłącza animację, ale podświetlenie ZOSTAJE —
   dostępność nie może zabierać informacji, tylko ruch. */
body.theme-tactical .bp-inv-tr-row.is-alert {
  animation: bp-tr-alert 1.1s ease-in-out 4;
}
@keyframes bp-tr-alert {
  0%, 100% { background: transparent; }
  50% { background: rgba(209, 84, 79, 0.16); }
}
@media (prefers-reduced-motion: reduce) {
  body.theme-tactical .bp-inv-tr-row.is-alert {
    animation: none;
    background: rgba(209, 84, 79, 0.16);
  }
}
/* ⚠️ Na wąskim ekranie nazwa przedmiotu MUSI się zawinąć, a nie zostać
   ucięta: „Ressurection Coin" vs „HE Grenade" to jedyna informacja, po której
   gracz rozpoznaje pozycję. Kropka i czas zostają w jednej linii. */
@media (max-width: 560px) {
  body.theme-tactical .bp-inv-tr-row { flex-wrap: wrap; }
  body.theme-tactical .bp-inv-tr-name { flex: 1 1 100%; white-space: normal; }
  body.theme-tactical .bp-inv-tr-status { text-align: left; }
}
/* ─── [CHANGE 2026-09-18 BP-R27-INV-SEARCH] WYSZUKIWARKA (pkt 16) ──────────
   User pkt 16 tury 14: „Inventory - chcę żebys dodał pole do wyszukiwania
   itemów".
   ─────────────────────────────────────────────────────────────────────────
   Pole siedzi NAD siatką i jest przyklejone do szerokości sekcji (nie do
   szerokości pojedynczego kafla) — wyszukiwanie dotyczy CAŁEGO magazynu, więc
   jego zasięg musi być widoczny.
   Licznik `shown / total` pojawia się TYLKO przy aktywnym filtrze: bez niego
   powtarzałby liczbę, która jest już w nagłówku sekcji, i robiłby szum. */
body.theme-tactical .bp-inv-search-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
}
body.theme-tactical .bp-inv-search {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 9px;
  /* [CHANGE 2026-09-18 BP-R27E-5-SEARCH-PAD] 9px 13px -> 11px 18px.
     User: „Wyszukiwarka w inventory - zrób lekki margines dla tekstu ktory
     wpisujemy bo jest zbyt bardzo przyklejony do lewej".
     ⚠️ Sama zmiana `padding` na polu nie wystarczyła: pole jest `flex: 1 1 auto`
     wewnątrz kontenera, a `padding-left` na polu przesunęłoby tylko jego treść,
     zostawiając kursor przy samej ikonie. Margines musi być na KONTENERZE,
     bo to on wyznacza odległość ikony (i zaraz za nią tekstu) od krawędzi. */
  padding: 11px 18px;
  background: rgba(10, 14, 22, 0.75);
  border: 1px solid var(--hair);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
body.theme-tactical .bp-inv-search:focus-within {
  border-color: rgba(255, 138, 43, 0.55);
  box-shadow: 0 0 0 1px rgba(255, 138, 43, 0.18);
}
body.theme-tactical .bp-inv-search svg { flex: 0 0 auto; color: var(--muted); }
body.theme-tactical .bp-inv-search-input {
  flex: 1 1 auto;
  min-width: 0;
  /* [CHANGE 2026-09-18 BP-R27E-5-SEARCH-PAD] Dodatkowy odstęp między ikoną
     a wpisywanym tekstem (sam `gap` kontenera dawał 9px, co przy ikonie 15px
     wyglądało na sklejenie). */
  padding: 0 0 0 3px;
  background: transparent;
  border: 0;
  outline: none;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.82rem;
  color: #e7e2d8;
}
body.theme-tactical .bp-inv-search-input::placeholder { color: #6d7484; }
/* Natywny „×" pola `type="search"` w ciemnym motywie bywa niewidoczny. */
body.theme-tactical .bp-inv-search-input::-webkit-search-cancel-button {
  filter: invert(0.7);
  cursor: pointer;
}
body.theme-tactical .bp-inv-search-count {
  flex: 0 0 auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  color: var(--muted);
}
body.theme-tactical .bp-inv-search-empty {
  margin: 0 0 16px;
  padding: 11px 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  color: var(--muted);
  border: 1px dashed var(--hair-2);
}
body.theme-tactical .bp-inv-search-empty[hidden] { display: none; }
/* ⚠️ `hidden` MUSI być obsłużone jawnie. `.bp-inv-card` ma `display: grid`
   z autora, a `[hidden]` z arkusza przeglądarki ma TĘ SAMĄ specyficzność —
   wygrywa arkusz autora, więc bez tej reguły odfiltrowane kafle zostałyby
   na ekranie. Ten sam trap co `.bp-item-popover[hidden]`. */
body.theme-tactical .bp-inv-card[hidden] { display: none; }
body.theme-tactical .bp-inv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}
body.theme-tactical .bp-inv-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-items: center;
  gap: 6px 16px;
  padding: 16px 18px;
  background: linear-gradient(155deg, rgba(24, 18, 34, 0.62), rgba(12, 9, 16, 0.85));
  border: 1px solid var(--hair);
  border-left: 2px solid var(--signal);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
body.theme-tactical .bp-inv-card:hover { border-color: var(--hair-2); box-shadow: 0 0 16px rgba(255, 138, 43, 0.12); }
/* Przedmiot czasowy dostaje inny akcent, bo jego wartość UPŁYWA — to jedyna
   informacja na karcie, która zmienia się bez działania gracza. */
body.theme-tactical .bp-inv-card.is-temporary { border-left-color: #ffc247; }
body.theme-tactical .bp-inv-card.is-blocked { border-left-color: #3a2b45; opacity: 0.68; }
body.theme-tactical .bp-inv-card-art {
  position: relative;
  grid-row: 1 / span 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  background: radial-gradient(circle at 50% 40%, rgba(255, 138, 43, 0.1), transparent 70%);
  border: 1px solid var(--hair);
}
body.theme-tactical .bp-inv-card-art img { width: 72px; height: 72px; object-fit: contain; }
/* Licznik sztuk na grafice — stacki idą w setki, więc liczba musi być widoczna
   od razu, a nie dopiero po przeczytaniu sekcji z ilością. */
body.theme-tactical .bp-inv-qty {
  position: absolute;
  right: -6px;
  bottom: -6px;
  padding: 2px 7px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--signal);
  border: 1px solid rgba(0, 0, 0, 0.35);
}
body.theme-tactical .bp-inv-card-body { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
body.theme-tactical .bp-inv-card-name {
  font-family: 'Oswald', sans-serif;
  font-size: 1.06rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: #f4e9f6;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.theme-tactical .bp-inv-card-meta { display: flex; flex-wrap: wrap; gap: 6px; }
body.theme-tactical .bp-inv-kind,
body.theme-tactical .bp-inv-days {
  font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  padding: 3px 8px;
  background: rgba(10, 7, 16, 0.7);
  border: 1px solid var(--hair-2);
  color: #a8a49b;
}
body.theme-tactical .bp-inv-days { color: #ffd97a; border-color: rgba(255, 194, 71, 0.32); }
body.theme-tactical .bp-inv-card-foot {
  grid-column: 2;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}
body.theme-tactical .bp-inv-amount { display: flex; align-items: center; gap: 7px; }
body.theme-tactical .bp-inv-amount-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-inv-amount-input {
  width: 84px;
  padding: 6px 9px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.84rem;
  text-align: right;
  color: #f4e9f6;
  background: #0a0710;
  border: 1px solid var(--hair-2);
}
body.theme-tactical .bp-inv-amount-input:focus { outline: none; border-color: var(--signal); }
body.theme-tactical .bp-inv-send { min-width: 132px; }

/* ═══════════════════════════════════════════════════════════════════════════
   [CHANGE 2026-09-21 BP-R37-INV-VIEW-LIST] WIDOK LISTY + PRZEŁĄCZNIK
   ═══════════════════════════════════════════════════════════════════════════
   ZGLOSZENIE (pkt 5): „Chcę żebyś dodał button do przełączania trybu wyświetlania
   przedmiotów - bo teraz tylko są w formie kafelków - to daj button co zmieni na
   liste + Przygotuj pełne wdrożenie widoku listy".

   ⚠️⚠️ MECHANIZM: JEDEN ATRIBUT, CZTERY REGUŁY. Tryb niesie
   `data-inv-view` NA SIATCE (`.bp-inv-grid`), a nie klasa na każdej karcie:
     • stan jest w JEDNYM węźle (brak możliwości rozjazdu między kartami),
     • JS przestawia jeden atrybut (patrz `applyInventoryView`),
     • CSS nie potrzebuje `!important` ani nadpisywania stylów kafelka.
   ⚠️ Karta w trybie listy ZOSTAJE tym samym elementem (`article.bp-inv-card`) ze
   wszystkimi `data-*` i przyciskami — zmienia się WYŁĄCZNIE układ. Dzięki temu
   wyszukiwarka, wysyłka, otwieranie skrzyń i licznik ilości działają bez zmian
   i nie ma drugiej ścieżki do utrzymania.
   ⚠️ W trybie listy karta musi przejść z siatki 2-kolumnowej na 3-kolumnową
   (grafika | opis | akcje), bo w układzie kafelka przyciski były POD opisem —
   a lista bez akcji w tym samym wierszu nie jest listą. */

/* ── PRZEŁĄCZNIK: dwa przyciski z ikonami ───────────────────────────────── */
body.theme-tactical .bp-inv-viewswitch {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--hair-2);
  background: rgba(10, 7, 16, 0.7);
}
body.theme-tactical .bp-inv-view-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  padding: 0;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-right: 1px solid var(--hair-2);
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}
body.theme-tactical .bp-inv-view-btn:last-child { border-right: 0; }
body.theme-tactical .bp-inv-view-btn:hover { color: #f4e9f6; background: rgba(255, 138, 43, 0.1); }
body.theme-tactical .bp-inv-view-btn.is-on { color: var(--ink); background: var(--signal); }
body.theme-tactical .bp-inv-view-btn:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }

/* ── TRYB LISTY ─────────────────────────────────────────────────────────── */
body.theme-tactical .bp-inv-grid[data-inv-view="list"] {
  /* Jedna kolumna — lista ma się czytać wierszami, a nie porównywać kafelki. */
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
}
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card {
  /* grafika | opis | akcje — przyciski w TYM SAMYM wierszu co nazwa */
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: auto;
  align-items: center;
  gap: 10px 16px;
  padding: 10px 14px;
}
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card-art {
  grid-row: 1;
  width: 52px;
  height: 52px;
  /* W liscie grafika jest znakiem rozpoznawczym, a nie trescia — poswiata
     kafelka zabieralaby kontrast nazwie obok. */
  background: none;
}
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card-art img { width: 44px; height: 44px; }
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-qty {
  /* W liscie róg grafiki jest zbyt ciasny na plakietkę — przenosimy ją na
     prawy dolny róg KAFLI grafiki (52px), z mniejszym odsadzeniem. */
  right: -5px;
  bottom: -5px;
  font-size: 0.68rem;
  padding: 1px 5px;
}
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card-body {
  /* Nazwa i meta w JEDNEJ linii — wiersz listy nie może rosnąć w pionie. */
  flex-direction: row;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 12px;
}
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card-name {
  font-size: 0.98rem;
  /* Bez elipsy: w liscie nazwa ma PEŁNĄ szerokość, więc obcinanie jej byłoby
     cofnięciem jedynej przewagi tego widoku nad kafelkami. */
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: anywhere;
}
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card-meta { align-items: center; }
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card-foot {
  grid-column: 3;
  align-items: center;
  flex-wrap: nowrap;
}
/* W liscie pole ilości zwężamy — 84px na liczbę do 1000 jest nadmiarem i
   odbiera miejsce nazwie przedmiotu. */
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-amount-input { width: 64px; padding: 4px 7px; }
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-send,
body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-open { min-width: 108px; }
/* Poniżej 640 px lista musi się złożyć z powrotem w pion — trzy kolumny nie
   zmieszczą się na telefonie, a poziomy scroll byłby gorszy od kafelków. */
@media (max-width: 640px) {
  body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto;
  }
  body.theme-tactical .bp-inv-grid[data-inv-view="list"] .bp-inv-card-foot {
    grid-column: 1 / -1;
    flex-wrap: wrap;
  }
}
/* [CHANGE 2026-09-20 BP-R30-GEAR-OPEN] WIRTUALNA SKRZYNIA „ATLAS Gear".
   Przycisk OPEN dostaje ten sam minimalny rozmiar co SEND, żeby kafle w siatce
   nie skakały (jedna akcja zamiast drugiej = ta sama szerokość). Złote tło
   odróżnia „otwórz" od „wyślij" — to różne operacje i gracz musi to widzieć
   przed kliknięciem, a nie dopiero po. */
body.theme-tactical .bp-inv-open { min-width: 132px; }
/* [CHANGE 2026-09-21 BP-R31-INV-RANDBOX-COLOR] SKRZYNIA W MAGAZYNIE — NIE ZŁOTA.
   ─────────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R31, pkt 4): „skrzynia Atlas Gear (Temporary) ma taki bardziej
   zoltawy kolor [...] zmien kolor + wpis na randombox".

   BYŁO: `border-left-color: #d8b266` + `color: #ffe3b0` (bursztyn). To ten sam
   kolor, ktorym oznaczamy rzeczy ZŁOTE — a skrzynia złota nie jest. Dodatkowo
   myliła sie z prawdziwie złotą skrzynią `gear_perm_s01_01` (tier „gold"),
   ktora leży w tej samej siatce.
   JEST: fioletowy akcent (ten sam, ktorego uzywa sekcja Chests dla skrzyń do
   otwarcia) — odroznia sie od zlota i od zwyklych przedmiotow, a przycisk OPEN
   nadal jest złoty (bo to AKCJA, nie cecha przedmiotu). */
body.theme-tactical .bp-inv-card.is-openable { border-left-color: #b07cff; }
/* [CHANGE 2026-09-21 BP-R33-INV-ONE-TONE] JEDEN ODCIEN TEKSTU NA KARCIE „OPENABLE".
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R33, pkt 4): „Atlas Gear (Temporary) w skrzynce Atlas Basic Box -
   ma dalej kolor tekstu jasniejszy i ciemniejszy".

   PRZYCZYNA: nazwa karty i chip rodzaju mialy DWA ROZNE odcienie tego samego
   fioletu — `#e2d4ff` (nazwa) i `#d9c6ff` (chip). Obie reguly pochodzily z tej
   samej zmiany (R30), wiec „poprawka koloru" z R31 niczego nie zmienila: gracz
   nadal widzial na jednej karcie dwa — jasniejszy i ciemniejszy — kolory tekstu.
   JEST: JEDNA wspolna regula dla obu elementow, wiec nie moga sie rozjechac
   przy zadnej nastepnej zmianie. Chip zachowuje wlasne tlo i obramowanie
   (to on odroznia go od zwyklego przedmiotu), ale TEKST ma ten sam kolor. */
body.theme-tactical .bp-inv-card.is-openable .bp-inv-card-name,
body.theme-tactical .bp-inv-card.is-openable .bp-inv-kind {
  color: #e2d4ff;
}
body.theme-tactical .bp-inv-card.is-openable .bp-inv-kind {
  border-color: rgba(176, 124, 255, 0.42);
  background: rgba(60, 34, 110, 0.4);
}
/* Komunikat o nagrodzie, gdy kafel zniknął (otwarto ostatnią skrzynię).
   ⚠️ Musi być widoczny POZA siatką — kafel, który go niósł, został usunięty. */
body.theme-tactical .bp-inv-open-note {
  margin: 10px 0 0;
  padding: 10px 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.76rem;
  color: #ffe3b0;
  background: rgba(64, 44, 6, 0.35);
  border: 1px solid rgba(255, 194, 71, 0.4);
}
body.theme-tactical .bp-inv-blocked {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.05em;
  color: #8a92a3;
}
/* Status po wysłaniu (sukces / błąd / limit tempa) — jedna linia pod kartą.
   `is-error` jest czerwone, bo przy 429 gracz musi wiedzieć, że ma POCZEKAĆ,
   a nie że coś jest zepsute. */
body.theme-tactical .bp-inv-status {
  grid-column: 1 / -1;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  line-height: 1.4;
  color: #5ef5a8;
}
body.theme-tactical .bp-inv-status.is-error { color: #ff9a9a; }
body.theme-tactical .bp-inv-status.is-sending { color: #ffd97a; }

/* ═══ [CHANGE 2026-09-25 BP-R87-INV-TRANSIENT-BUBBLE] DYMEK POTWIERDZENIA WYSYŁKI ═══
   ZGLOSZENIE (R87, pkt 1): „Przy wysyłaniu przedmiotów z inventory do gry - jest
   zielony napis: Queued... - Chce zeby to bylo w formie dymka ktory znika po
   kilku sekundach a nie prawie odrazu".

   ⚠️⚠️ „PRAWIE OD RAZU" NIE BYŁO PRZESADĄ — TO BŁĄD UMIEJSCOWIENIA.
   Napis wstawiano DO KAFLI (`setStatus(card, …)`), a chwilę później
   `refreshInventoryGrid()` podmienia `innerHTML` CAŁEJ sekcji magazynu — węzeł
   z napisem przestawał istnieć, więc komunikat zniknął po ułamku sekundy.
   Ten sam mechanizm był już diagnozowany w R32 przy odsłonie nagrody z Gear
   („tekst ktory po niecałej sekundy znika") — wtedy naprawiono KOLEJNOŚĆ, ale
   nie samą przyczynę: napis żył w węźle, który zaraz ginął.

   JEST: dymek doczepiony do `<body>` i pozycjonowany `fixed` nad przyciskiem.
   Podmiana magazynu go NIE DOTYCZY, więc czas życia odmierzamy w JS (4,2 s),
   a nie długością animacji CSS — dzięki temu `prefers-reduced-motion`
   (wyłączone przejścia) nie zamienia go w komunikat widoczny wiecznie.

   ⚠️ Selektor BEZ `body.theme-tactical`, tak samo jak `.bp-item-popover`:
   dymek jest dzieckiem `<body>`, a klasa motywu na `<body>` jest warunkowa
   (`if (tacticalTheme)`) — wersja z motywem „na wszelki wypadek" nie ma sensu,
   bo arkusz i tak jest ładowany tylko z tym motywem.
   ⚠️ Stan widoczności to klasa `is-visible`, NIE atrybut `hidden` — `display`
   z arkusza autora wygrywa z `[hidden]` z arkusza przeglądarki (ta sama pułapka
   co `.bp-item-popover[hidden]`). */
.bp-inv-bubble {
  position: fixed;
  z-index: 1300;
  max-width: min(420px, calc(100vw - 24px));
  padding: 9px 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.76rem;
  line-height: 1.4;
  letter-spacing: 0.03em;
  color: #d9ffe9;
  background: rgba(10, 34, 22, 0.94);
  border: 1px solid rgba(94, 245, 168, 0.52);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5), 0 0 14px rgba(94, 245, 168, 0.14);
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  transition: opacity 0.28s ease, transform 0.28s ease;
}
.bp-inv-bubble.is-visible { opacity: 1; transform: translateY(0); }
.bp-inv-bubble.is-error {
  color: #ffdede;
  background: rgba(46, 10, 10, 0.94);
  border-color: rgba(255, 154, 154, 0.55);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5), 0 0 14px rgba(255, 154, 154, 0.14);
}
.bp-inv-bubble.is-sending {
  color: #fff2cf;
  background: rgba(46, 36, 6, 0.94);
  border-color: rgba(255, 217, 122, 0.55);
}
@media (prefers-reduced-motion: reduce) {
  .bp-inv-bubble { transition: none; }
}
@media (max-width: 640px) {
  body.theme-tactical .bp-inv-grid { grid-template-columns: 1fr; gap: 12px; }
  body.theme-tactical .bp-inv-card { grid-template-columns: auto minmax(0, 1fr); padding: 13px 14px; gap: 6px 12px; }
  body.theme-tactical .bp-inv-card-art { width: 64px; height: 64px; }
  body.theme-tactical .bp-inv-card-art img { width: 54px; height: 54px; }
  body.theme-tactical .bp-inv-card-name { font-size: 0.94rem; }
  body.theme-tactical .bp-inv-send { min-width: 0; width: 100%; }
  body.theme-tactical .bp-inv-card-foot { grid-column: 1 / -1; }
}

/* ---- 3e. ŁAŃCUCH DZIENNY (domknięcie tablicy zadań) --------------------- */
/* [CHANGE 2026-09-17 BP-R25-DAILY-CHAIN] Pigułka w nagłówku sekcji TASKS:
   pasek postępu „1 / 2" + nagroda + przycisk ODBIERZ (po serwerowej weryfikacji
   kompletu). Zadania dzienne przestają być niezależne — domknięcie tablicy to
   jeden konkretny cel dnia. */
body.theme-tactical .bp-daily-chain {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 30px;
  padding: 5px 11px;
  background: rgba(14, 24, 40, 0.7);
  border: 1px solid var(--hair-2);
}
body.theme-tactical .bp-daily-chain.is-ready {
  border-color: rgba(255, 194, 71, 0.6);
  box-shadow: 0 0 16px rgba(255, 194, 71, 0.16);
}
body.theme-tactical .bp-daily-chain.is-claimed { opacity: 0.72; }
body.theme-tactical .bp-daily-chain-k {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
body.theme-tactical .bp-daily-chain-bar {
  width: 68px;
  height: 6px;
  background: #0a0710;
  border: 1px solid var(--hair);
  overflow: hidden;
}
body.theme-tactical .bp-daily-chain-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #6b4a86, #c07ad6);
  transition: width 0.3s ease;
}
body.theme-tactical .bp-daily-chain.is-ready .bp-daily-chain-fill { background: linear-gradient(90deg, #f0a01f, #ffd97a); }
body.theme-tactical .bp-daily-chain-v { font-family: 'Oswald', sans-serif; font-size: 0.84rem; color: #e8e3ee; }
body.theme-tactical .bp-daily-chain-reward {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: 'Oswald', sans-serif;
  font-size: 0.84rem;
  color: #ffc247;
}
body.theme-tactical .bp-daily-chain-reward img { width: 14px; height: 14px; object-fit: contain; }
body.theme-tactical .bp-daily-chain-state.is-done {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #5ef5a8;
}
body.theme-tactical .bp-daily-chain-claim {
  padding: 4px 11px;
  font-family: 'Rajdhani', sans-serif;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ---- 3d. NAGRODA ZA OSIĄGNIĘCIE (kafelek + wstęga) ---------------------- */
/* [CHANGE 2026-09-17 BP-R25-ACH-REWARD] Osiągnięcia nie miały żadnej nagrody —
   po zdobyciu zostawały odznaką bez wartości. Teraz każdy kafelek i każda wstęga
   pokazuje nagrodę w Skull Coinach oraz stan: ODBIERZ (gotowe) / ODEBRANE /
   zablokowane. Przycisk pojawia się dopiero, gdy SERWER potwierdzi odblokowanie. */
body.theme-tactical .bp-ach-tile-reward {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  margin-top: 7px;
  padding-top: 7px;
  border-top: 1px solid rgba(34, 40, 52, 0.6);
}
body.theme-tactical .bp-ach-tile-reward-v {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: 'Oswald', sans-serif;
}
body.theme-tactical .bp-ach-tile-reward-v img { width: 16px; height: 16px; object-fit: contain; }
body.theme-tactical .bp-ach-tile-reward-v strong { font-size: 0.94rem; color: #ffc247; }
body.theme-tactical .bp-ach-tile-claimed {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #5ef5a8;
}
body.theme-tactical .bp-ach-tile-locked {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-ach-claim {
  padding: 5px 12px;
  font-family: 'Rajdhani', sans-serif;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* Gotowe do odebrania — delikatna poswiata, żeby gracz zauważył bez szukania. */
body.theme-tactical .bp-ach-tile.is-claimable {
  border-color: rgba(255, 194, 71, 0.55);
  box-shadow: inset 0 0 22px rgba(255, 194, 71, 0.12);
}
body.theme-tactical .bp-ach-tile.is-claimed { opacity: 0.82; }
/* Wstęga: nagroda w osobnym wierszu pod paskiem postępu (ma własny, szerszy układ). */
body.theme-tactical .bp-ach-strip-body .bp-ach-tile-reward { margin-top: 8px; }
body.theme-tactical .bp-ach-tip-r {
  margin-top: 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #ffc247;
}

/* ---- 3c. PODGLAD PRZEDMIOTU W ZESTAWACH (hover/focus) ------------------- */
/* [CHANGE 2026-09-17 BP-R24-COLLECTION-PREVIEW] User pkt 3: „Collections - chce
   zebys zrobił system ze jak sie najedzie na nazwe to zeby pokazywało małe
   okienko z wygladem danej rzeczy".
   Pozycja zestawu staje sie interaktywna (hover + focus), a `battlepass.js`
   pokazuje `.bp-item-popover` — dolaczony do `<body>`, pozycjonowany `fixed`.
   Nie uzywamy `::after`/`title`, bo karta zestawu ma `overflow`, wiec okienko
   byloby obciete. */
body.theme-tactical .bp-set-item {
  cursor: help;
  padding: 2px 4px;
  margin: 0 -4px;
  border-radius: 2px;
  transition: background 0.14s ease, color 0.14s ease;
}
body.theme-tactical .bp-set-item:hover,
body.theme-tactical .bp-set-item:focus-visible {
  background: rgba(255, 194, 71, 0.12);
  outline: none;
}
body.theme-tactical .bp-set-item:hover .bp-set-item-name { color: #ffe3b0; }

/* [CHANGE 2026-09-18 BP-R27-POPOVER-BIGGER] POWIĘKSZONY PODGLĄD PRZEDMIOTU.
   ─────────────────────────────────────────────────────────────────────────
   User pkt 2 tury 14: „Collections - powiększyłeś te kwadraty dobrze - ale
   musisz tez powiększyć podgląd po najechaniu na daną opcje".

   PRZYCZYNA: w R26 powiększono kafle zestawów (`.bp-set-item-dot` 8 -> 14 px,
   nazwy 1rem, karta 22/24 px paddingu), ale POPOVER został na starych
   wymiarach (178 px, art 104 px, ikona 92 px) — czyli okienko było teraz
   MNIEJSZE od samej pozycji, na którą się najeżdża. Wyglądało to jak resztka
   starego designu, a nie jak podgląd.

   NOWE WYMIARY (skok ~1,4x, wspólny mnożnik dla wszystkich elementów, żeby
   proporcje zostały te same): szerokość 248 px, kadr 150 px, ikona 132 px,
   nazwa 1.08rem. Limit wysokości kadru jest brany z najdłuższej nazwy, więc
   karta nadal nie skacze przy przełączaniu między przedmiotami. */
.bp-item-popover {
  position: fixed;
  z-index: 95;
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 248px;
  max-width: calc(100vw - 16px);
  padding: 13px 15px 14px;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(20, 15, 30, 0.98), rgba(8, 6, 12, 0.99));
  border: 1px solid var(--hair-2, #2c3442);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.66);
  animation: bpItemPop 0.14s ease-out 1 both;
}
@keyframes bpItemPop {
  0% { opacity: 0; transform: translateY(4px); }
  100% { opacity: 1; transform: translateY(0); }
}
.bp-item-popover .bp-item-pop-art {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 150px;
  margin-bottom: 7px;
  background: radial-gradient(circle at 50% 45%, rgba(255, 194, 71, 0.16), transparent 68%);
  border: 1px solid rgba(34, 40, 52, 0.7);
}
.bp-item-popover .bp-item-pop-art img { width: 132px; height: 132px; object-fit: contain; }
.bp-item-popover .bp-item-pop-noart { font-family: 'Oswald', sans-serif; font-size: 2.2rem; color: var(--muted, #8a92a3); }
.bp-item-popover .bp-item-pop-name {
  font-family: 'Oswald', sans-serif;
  font-size: 1.08rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: #fff;
}
.bp-item-popover .bp-item-pop-set {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted, #8a92a3);
}
.bp-item-popover .bp-item-pop-state {
  align-self: flex-start;
  margin-top: 5px;
  padding: 3px 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid currentColor;
}
.bp-item-popover .bp-item-pop-state.is-owned { color: #5ef5a8; }
.bp-item-popover .bp-item-pop-state.is-missing { color: #ffa1a1; }
/* ⚠️ Atrybut `hidden` MUSI byc obsluzony jawnie: reguła autora
   (`.bp-item-popover { display: flex }`) ma taka sama specyficznosc jak
   `[hidden]` z arkusza przegladarki, a arkusze autora wygrywaja — bez tej linii
   okienko byloby widoczne non stop. */
.bp-item-popover[hidden] { display: none; }

/* ---- 4. TABELA SEZONU (osobna zakladka) --------------------------------- */
/* [CHANGE 2026-09-17 BP-R24-BOARD-WIDE] User: „zrób to okienko troche szersze".
   Panel nagrod mial 268 px i przy dwoch kolumnach (`5,000` + ikona waluty)
   lamal/uciskal wartosci. Teraz 330 px, a siatka tabeli dostala wiecej miejsca
   na kolumne nagrody (nazwa gracza i tak ma `minmax(0,1fr)`). */
body.theme-tactical .bp-league-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 330px;
  gap: 18px;
  align-items: start;
}
body.theme-tactical .bp-league-board {
  background: linear-gradient(155deg, rgba(30, 14, 38, 0.55), rgba(12, 9, 16, 0.8));
  border: 1px solid var(--hair);
  border-left: 2px solid #3a2b45;
  padding: 4px 0 8px;
}
body.theme-tactical .bp-board-head-row,
body.theme-tactical .bp-board-row {
  grid-template-columns: 56px minmax(0, 1fr) 68px 104px 132px;
}
body.theme-tactical .bp-board-head-row { padding: 9px 16px; font-size: 0.58rem; }
body.theme-tactical .bp-board-row { padding: 9px 16px; font-size: 0.72rem; }
body.theme-tactical .bp-board-row .bp-board-reward {
  text-align: right;
  font-family: 'Oswald', sans-serif;
  font-size: 0.78rem;
  color: #ffc247;
  white-space: nowrap;
}
body.theme-tactical .bp-league-prizes {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* [CHANGE 2026-09-18 BP-R27E-4-PRIZE-BOX] OKNO WIĘKSZE + SZERSZE KOLUMNY.
     User: „zrób większe to okienko season prizes bo przez to ikony są bardzo
     małe i kiepsko je widać".
     ⚠️ Sama zmiana rozmiaru IKONY nie wystarczyłaby: wiersz jest `space-between`
     w kolumnie o stałej szerokości, więc większa ikona ściskałaby liczbę.
     Zwiększamy więc: padding, minimalną szerokość kolumny wartości (`max-content`
     na `strong` — niżej) i rozmiar ikon. Dopiero razem daje to czytelny wiersz. */
  padding: 20px 22px;
  min-width: 0;
  background: linear-gradient(155deg, rgba(46, 30, 4, 0.4), rgba(12, 9, 16, 0.85));
  border: 1px solid var(--hair);
  border-left: 2px solid #ffc247;
}
body.theme-tactical .bp-league-prizes-k {
  font-family: 'Oswald', sans-serif;
  font-size: 0.94rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #ffc247;
}
body.theme-tactical .bp-league-prize-list { display: flex; flex-direction: column; gap: 5px; margin: 0; padding: 0; list-style: none; }
body.theme-tactical .bp-league-prize {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid rgba(34, 40, 52, 0.45);
}
body.theme-tactical .bp-league-prize:last-child { border-bottom: 0; }
body.theme-tactical .bp-league-prize-rank { font-family: 'IBM Plex Mono', monospace; font-size: 0.84rem; color: var(--muted); }
body.theme-tactical .bp-league-prize-v { display: inline-flex; align-items: center; gap: 9px; }
/* ─── [CHANGE 2026-09-18 BP-R27E-PRIZE-ICONS] IKONA OSIĄGNIĘCIA W NAGRODZIE ──
   Nagroda za miejsce to nie tylko waluta — od tej tury dochodzi osiągnięcie
   (1./2./3. miejsce, top 10). Ikona stoi OBOK liczby kredytów, bo to ta sama
   nagroda, a nie osobna pozycja listy.
   ⚠️ `flex: 0 0 auto` + stały rozmiar: gdyby ikona się rozciągała, wiersz
   „#1–1" i „#35–50" miałyby różną wysokość i lista przestałaby się czytać
   kolumnowo (`strong` z liczbą jest wyrównany do prawej w wąskiej kolumnie). */
body.theme-tactical .bp-league-prize-ach {
  flex: 0 0 auto;
  /* [CHANGE 2026-09-18 BP-R27E-4-PRIZE-BOX] 20 -> 28 px. Prośba usera: „ikony
     są bardzo małe i kiepsko je widać". 28 px to nadal mniej niż ikona waluty
     w nagłówku, więc hierarchia zostaje zachowana. */
  width: 28px;
  height: 28px;
  object-fit: contain;
  filter: drop-shadow(0 0 6px rgba(217, 164, 65, 0.45));
}
body.theme-tactical .bp-league-prize-v strong {
  font-family: 'Oswald', sans-serif;
  font-size: 1.14rem;
  font-weight: 600;
  color: #ffc247;
  /* Liczba nie może się łamać — ikony obok rosną, więc kolumna wartości musi
     być odporna na ściskanie. */
  white-space: nowrap;
}
/* [CHANGE 2026-09-17 BP-R24-CROWN-COLOR] User pkt 4: „zmień kolor czcionki dla
   1000 i 500 koron (nagrody za miejsca 21-34 i 35-50)". Poprzedni #d8b4e8 byl
   wyprany i zlewal sie z szarym tlem panelu. Teraz wyrazny fiolet z poswiata,
   jednoznacznie rozny od zlota Kredytow — gracz od razu widzi, ze to INNA
   waluta, a nie inny prog. */
/* [CHANGE 2026-09-18 BP-R27-CROWN-GOLD] User pkt 14 tury 14: „W Leaderboard dla
   miejsc 21-34 i 35-50 jest fioletowy kolor czcionki - zmien go na złoty".
   ─────────────────────────────────────────────────────────────────────────
   POPRZEDNIO (R24, BP-R24-CROWN-COLOR): #b98cff z fioletowa poswiata — mialo to
   odroznic Korony od Kredytow i faktycznie odroznialo, ale user uznal fiolet za
   niepasujacy do palety strony („zle sie to kojarzy"). Wracamy do ZLOTA, czyli
   koloru, ktorym na tej stronie mowi sie o nagrodach (kolumna „REWARD", cena
   skrzyni, nagroda za zestaw).
   ⚠️ Rozroznienie Kredyty/Korony NIE ZNIKA — niesie je IKONA waluty obok
   wartosci (patrz `.bp-league-prize-v`), wiec gracz nadal widzi, ze miejsca
   21-50 placa w innej walucie niz 1-20. Kolor przestaje byc jedynym nosnikiem
   tej informacji i to jest zmiana na lepsze (dostepnosc). */
body.theme-tactical .bp-league-prize.is-crowns .bp-league-prize-v strong {
  color: #ffc247;
  text-shadow: 0 0 12px rgba(255, 194, 71, 0.4);
}
body.theme-tactical .bp-league-prize.is-crowns .bp-league-prize-rank { color: #d8c79a; }
body.theme-tactical .bp-league-note { margin: 4px 0 0; font-size: 0.68rem; line-height: 1.5; color: var(--muted); }
/* [CHANGE 2026-09-17 BP-R24-BOARD-PAYOUT-NOTE] Wyrózniona informacja o tym, KIEDY
   nagrody trafia do gry. User: „dopisz ze nagrody zostaną rozesłane po zakonczeniu
   wydarzenia". Nie moze to byc szary przypis — od tego zalezy, czy gracz sprawdza
   portfel od razu, czy po sezonie. */
body.theme-tactical .bp-league-payout {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 4px 0 0;
  padding: 9px 11px;
  font-size: 0.72rem;
  line-height: 1.5;
  color: #ffe3b0;
  background: rgba(52, 36, 6, 0.5);
  border: 1px solid rgba(255, 194, 71, 0.35);
}
body.theme-tactical .bp-league-payout svg { flex: 0 0 auto; margin-top: 1px; color: #ffc247; }

@media (max-width: 900px) {
  body.theme-tactical .bp-league-layout { grid-template-columns: 1fr; }
  body.theme-tactical .bp-board-head-row,
  body.theme-tactical .bp-board-row { grid-template-columns: 42px minmax(0, 1fr) 48px 78px 92px; gap: 6px; padding: 8px 10px; font-size: 0.66rem; }
}

/* ---- 8. ROLKA OTWIERANIA (styl CS2) ------------------------------------- */
/* [CHANGE 2026-09-17 BP-R24-REEL] Zastapila tabele symulacji (usunieta razem
   z blokiem CSS). User pkt 5: „Przygotuj taki system gdzie jak nastepuje
   losowanie to się przesuwają ikonki jak skrzynki w cs2 - starą symulacje usun".
   Rolka: poziomy pasek ikon przewijajacy sie pod nieruchomym znacznikiem. */
body.theme-tactical .bp-reel {
  margin: 14px 0 0;
  padding: 14px 16px 16px;
  background: linear-gradient(180deg, rgba(14, 10, 22, 0.85), rgba(8, 6, 12, 0.95));
  border: 1px solid var(--hair);
  border-top: 2px solid rgba(255, 194, 71, 0.4);
}
body.theme-tactical .bp-reel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}
body.theme-tactical .bp-reel-title {
  font-family: 'Oswald', sans-serif;
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  color: #ffc247;
}
body.theme-tactical .bp-reel-controls { display: inline-flex; align-items: center; gap: 8px; }
body.theme-tactical .bp-reel-btn { min-width: 104px; }

/* Okno rolki: przycina pasek i trzyma znacznik na srodku. */
body.theme-tactical .bp-reel-window {
  position: relative;
  margin: 13px 0 0;
  height: 132px;
  overflow: hidden;
  background:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.02) 0 1px, transparent 1px 100%),
    linear-gradient(180deg, rgba(6, 5, 10, 0.9), rgba(16, 11, 24, 0.9));
  border: 1px solid var(--hair);
}
body.theme-tactical .bp-reel-strip {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 100%;
  padding: 0 8px;
  will-change: transform;
}
body.theme-tactical .bp-reel-card {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 104px;
  height: 116px;
  padding: 6px;
  background: linear-gradient(180deg, rgba(28, 20, 40, 0.9), rgba(12, 9, 18, 0.95));
  border: 1px solid var(--hair-2);
}
body.theme-tactical .bp-reel-card img {
  width: 56px;
  height: 56px;
  object-fit: contain;
  pointer-events: none;
}
body.theme-tactical .bp-reel-card-name {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.56rem;
  line-height: 1.25;
  text-align: center;
  /* ⚠️⚠️ [CHANGE 2026-09-21 BP-R34-REEL-NAME-LIGHT] JASNY KOLOR NA WSZYSTKICH
     KAFELKACH ROLKI — NAPRAWA ZGLOSZENIA POWTARZANEGO SZESC RAZY.
     ─────────────────────────────────────────────────────────────────────────
     ZGLOSZENIE (R34, pkt 2): „na rolce Atlas Gear (Temporary) w skrzynce Atlas
     Basic Box - ma dalej kolor tekstu jasniejszy i ciemniejszy - na rolce jest
     kilka pozycji tego boxa i czesc z nich ma ciemniejsza czcionke".

     PRZYCZYNA — TO BYLO TUTAJ, NIE W KARCIE MAGAZYNU.
     Baza `.bp-reel-card-name` miala `color: #9c93a8` (CIEMNY szary), a jasne byly
     wylacznie dwa warianty nadpisujace:
       • `.is-main  .bp-reel-card-name { color: #e8e2f2; }`  (nagroda glowna)
       • `.is-gold  .bp-reel-card-name { color: #ffe6b0; }`  (zloty przedmiot)
     Klasy `is-rare` / `is-ultra` ustawialy TYLKO dolna krawedz i cien — koloru
     nazwy NIE dotykaly. Efekt: na jednej rolce kafle glówne byly JASNE, a cala
     reszta (w tym rare/ultra) zostawala CIEMNA. Dokladnie „jasniejszy
     i ciemniejszy" — dwa odcienie tego samego tekstu na jednym pasku.

     ⚠️ DLACZEGO POPRZEDNIE POPRAWKI NIE DZIALALY: szesc razy zmienialem kolor
     w `.bp-inv-card` (KARTA MAGAZYNU) i `.bp-inv-kind` — a zgloszenie dotyczylo
     ROLKI (`.bp-reel-card`, pasek przewijania w modalu). To dwa rozne komponenty
     („inv" = inventory, „reel" = rolka), wiec poprawka nie mogla zadzialac.
     Zgloszenie mowilo wprost „na rolce" od samego poczatku.

     JEST: baza jest JASNA (`#e8e2f2`) — czyli kolor, ktory mialy dotad tylko
     kafle glowne. Teraz KAZDY kafelek rolki ma jasny tekst. Zloto zostaje
     odroznione kolorem bursztynowym (`#ffe6b0`), a nie jasnoscia. */
  color: #e8e2f2;
  max-height: 2.5em;
  overflow: hidden;
}
body.theme-tactical .bp-reel-card-amt {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  text-align: center;
  color: #d8d0e2;
}

/* [CHANGE 2026-09-17 BP-R26-REEL-GOLD] MAIN REWARDS NA ZŁOTO W ROLCE.
   ─────────────────────────────────────────────────────────────────────────
   User pkt 5 tury 13: „Chests - Main rewards przy opening range powinny byc
   podswietlane na złoto".

   PO CO: w pasku przewijają się dziesiątki identycznych kafli i moment
   zatrzymania na głównej nagrodzie wyglądał tak samo jak na dowolnej ozdobie.
   Gracz nie odróżniał „trafiłem coś wartosciowego" od „rolka się przesypała".

   DLACZEGO ZŁOTO: na tej stronie złoto jest JUŻ zarezerwowane dla rzeczy
   wyjątkowych — plakietka BEST VALUE, ukończony zestaw, tor premium, przyciski
   CLAIM. Użycie go dla głównej nagrody jest spójne, a nie „kolejnym kolorem".

   ⚠️ Kolor MUSI być widoczny także w ruchu, dlatego nie ograniczamy się do
   delikatnego tła: dochodzi obramowanie, wewnętrzny poblask i jasna nazwa.
   Nagroda leci przez środek ekranu w ~2 s, więc sygnał musi być mocny.
   ⚠️ NIE ruszamy `border-color` bazowej reguły — `.is-main` nadpisuje je
   w całości, a plik ma już ostrzeżenie o kolejności arkuszy (patrz
   [CSS-MOBILE-GUARD] w `site.css`). */
/* ⚠️⚠️ [CHANGE 2026-09-21 BP-R30-REEL-MAIN-NOT-GOLD] „MAIN REWARDS" TO NIE „ZLOTE".
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R30, pkt 6): „W Atlas Skull Coin Box - podswietlaja sie na zloto
   takze inne skulls niz skull coin x1000 - zbadaj to".

   ZBADANE — przyczyna NIE lezy w zadnej regule `.is-gold` (tych jest kilka
   i wszystkie sa poprawne). Zloto pochodzilo z `.bp-reel-card.is-main`, ktora
   malowala na zloto CALA sekcje „Main Rewards" — obwodke, tlo i nazwe.
   A w Atlas Skull Coin Box sekcja Main Rewards to WSZYSTKIE 15 skull coinow
   (`skull_coin_10` ... `skull_coin_1000`), bo `entryBucket` klasyfikuje kazdy
   `skull_coin*` do sekcji 0. Efekt: zlote byly 10/20/.../750, choc zloty jest
   tylko 1000 (`payload.tier = "gold"`).
   ⚠️ Klasa rzadkosci tego NIE gasila: `.bp-reel-card.is-rare` ustawia tylko
   `border-bottom` i cien od dolu, wiec zlota obwodka i zlote tlo z `.is-main`
   zostawaly nietkniete. Dwie reguly opisujace ROZNE wlasciwosci — obie
   „widoczne", wiec latwo przeoczyc, ze jedna z nich koloruje nie to, co trzeba.

   DECYZJA: zlote podswietlenie jest zarezerwowane WYLACZNIE dla zlotych
   przedmiotow (`tier === "gold"`), tak samo jak w modalu „Show all contents".
   ⚠️ To SWIADOMIE zastepuje zyczenie z tury 13 („Main rewards przy opening range
   powinny byc podswietlane na zloto") — wlasciciel doprecyzowal je w R30 pkt 6,
   a sprzecznosc rozwiazujemy na korzysc NAJNOWSZEJ decyzji. Dodatkowo modal
   i rolka MUSZA pokazywac ten sam kolor tego samego przedmiotu; w modalu zloto
   dostawal wylacznie `skull_coin_1000`, wiec rolka byla jedynym miejscem,
   w ktorym 10/20/.../750 swiecily na zloto.
   ⚠️ Klasa `is-main` ZOSTAJE w DOM (niesie informacje „to nagroda glowna",
   uzywana przez inne selektory i testy), ale nie koloruje juz niczego na zloto. */
body.theme-tactical .bp-reel-card.is-main {
  /* Subtelne wyroznienie nagrody glownej BEZ zlota: sama jasniejsza nazwa
     i ciemniejsze tlo, zeby kafel nie zlewal sie z przesypywanka. */
  background: linear-gradient(180deg, rgba(30, 22, 44, 0.95), rgba(14, 10, 22, 0.97));
}
body.theme-tactical .bp-reel-card.is-main .bp-reel-card-name { color: #e8e2f2; }

/* [CHANGE 2026-09-21 BP-R30-REEL-GOLD-ITEM] ZLOTO NA KAFELKU TYLKO DLA ZLOTEGO
   PRZEDMIOTU. Klasa `is-gold` jest teraz nadawana w `reelCard` na podstawie
   `item.tier === "gold"` (dokladnie to samo zrodlo, z ktorego korzysta modal),
   a NIE na podstawie przynaleznosci do sekcji Main Rewards. */
body.theme-tactical .bp-reel-card.is-gold {
  background: linear-gradient(180deg, rgba(58, 40, 12, 0.92), rgba(24, 16, 6, 0.96));
  border-color: rgba(255, 194, 71, 0.72);
  box-shadow: inset 0 0 18px rgba(255, 194, 71, 0.16), 0 0 14px rgba(255, 194, 71, 0.22);
}
body.theme-tactical .bp-reel-card.is-gold .bp-reel-card-name { color: #ffe6b0; }
body.theme-tactical .bp-reel-card.is-gold .bp-reel-card-amt { color: #ffd97a; }

/* [CHANGE 2026-09-20 BP-R29-RARITY] RZADKOSC NA KAFELKACH PASKA ROLKI.
   ─────────────────────────────────────────────────────────────────────────
   Zgloszenie tury 19: „zmienily sie w podgladzie kolorki rare/ultra rare ale
   nie zmienilo sie to w pogladzie w rolkach". Pasek rolki jest pelnym podgladem
   zawartosci skrzyni (`reelFillerItems` ciagnie z `box.items`), wiec bez tego
   bloku ten sam przedmiot mial kolor na karcie skrzyni i w modalu, a w rolce
   byl bez koloru.

   ⚠️ DLACZEGO PASEK NA DOLE, A NIE CALA OBWODKA: `.bp-reel-card.is-main`
   (nagroda glowna, R27) ustawia ZLOTA obwodke skrótem `border-color`, a reguly
   sa w tym samym pliku — nadpisanie calego obramowania zalezaloby od kolejnosci
   i ktoregos sygnalu by zabraklo. Pasek `border-bottom` + delikatna poswiata
   od dolu dodaje rzadkosc NIEZALEZNIE od zlota nagrody glownej, wiec oba
   sygnaly widac jednoczesnie.
   ⚠️ `height` kafelka jest staly (116 px), wiec zmiana grubosci dolnej krawedzi
   NIE zmienia `offsetWidth` — przesuniecie paska liczone w `reelBuild`
   pozostaje poprawne. */
body.theme-tactical .bp-reel-card.is-rare {
  border-bottom: 3px solid rgba(61, 220, 132, 0.78);
  box-shadow: inset 0 -14px 20px -14px rgba(61, 220, 132, 0.4);
}
body.theme-tactical .bp-reel-card.is-ultra {
  border-bottom: 3px solid rgba(77, 159, 255, 0.85);
  box-shadow: inset 0 -14px 20px -14px rgba(77, 159, 255, 0.45);
}
/* [CHANGE 2026-09-20 BP-R29d-GOLD-PITY] Kafel paska rolki dla przedmiotu
   z GWARANCJI ZLOTA. Stoi po `is-rare`/`is-ultra`, wiec zloto wygrywa. */
body.theme-tactical .bp-reel-card.is-gold {
  border-bottom: 3px solid rgba(255, 194, 71, 0.95);
  box-shadow: inset 0 -14px 20px -14px rgba(255, 194, 71, 0.55);
}

/* [CHANGE 2026-09-18 BP-R27C-REEL-WINNER] WYGRANY KAFELEK — JEDNOZNACZNY WSKAŹNIK.
   ─────────────────────────────────────────────────────────────────────────
   Złota linia znacznika NIE wskazuje już wygranej: w turze 14 wygrana dostała
   losowe odchylenie (życzenie właściciela: „nie zawsze na środku, większe
   emocje"), a przy kafelku 104 px + odstępie 10 px odchylenie sięga 1,8 kafelka.
   Pod linią staje więc losowy kafelek-przesypywanka — i gracz czyta go jako
   swój wynik. Zgłoszenia: „linia zatrzymała się na atlas smoke a dało xp
   booster który jest 1 kafelek w prawo".

   Dlatego wynik oznaczamy na KAFELKU, nie linią: po zatrzymaniu wygrany dostaje
   złotą obwódkę z pulsowaniem, a resztę paska przygaszamy (`.is-settled`), żeby
   50 kafelków nie konkurowało o wzrok z tym jednym. Kolejność jest istotna:
   klasy dodaje `reelPlayOne` PO `transitionend`, więc podświetlenie nie
   zdradza wyniku w trakcie jazdy.
   ⚠️ Animujemy wyłącznie `box-shadow`/`filter`/`transform` bez zmiany rozmiaru
   (`scale` NIE wpływa na `offsetWidth`), bo przesunięcie paska policzone jest
   z szerokości kafelka przed animacją — zmiana szerokości rozjechałaby stop. */
body.theme-tactical .bp-reel-strip.is-settled .bp-reel-card { opacity: 0.32; transition: opacity 0.22s ease; }
body.theme-tactical .bp-reel-strip.is-settled .bp-reel-card.is-winner {
  opacity: 1;
  border-color: #ffc247;
  box-shadow: inset 0 0 20px rgba(255, 194, 71, 0.22), 0 0 22px rgba(255, 194, 71, 0.45);
  animation: bp-reel-win 1.5s ease-in-out infinite;
}
body.theme-tactical .bp-reel-strip.is-settled .bp-reel-card.is-winner .bp-reel-card-name { color: #ffe6b0; }
@keyframes bp-reel-win {
  0%   { box-shadow: inset 0 0 20px rgba(255, 194, 71, 0.22), 0 0 14px rgba(255, 194, 71, 0.35); }
  50%  { box-shadow: inset 0 0 24px rgba(255, 194, 71, 0.34), 0 0 30px rgba(255, 194, 71, 0.65); }
  100% { box-shadow: inset 0 0 20px rgba(255, 194, 71, 0.22), 0 0 14px rgba(255, 194, 71, 0.35); }
}
/* Ograniczenie ruchu: pulsowanie zamieniamy na statyczną złotą obwódkę.
   Wskaźnik MUSI zostać — bez niego wynik znowu jest nieczytelny. */
@media (prefers-reduced-motion: reduce) {
  body.theme-tactical .bp-reel-strip.is-settled .bp-reel-card.is-winner { animation: none; }
}
/* Znacznik — pionowa linia w srodku okna; to pod nim zatrzymuje się wygrana. */
body.theme-tactical .bp-reel-marker {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: linear-gradient(180deg, rgba(255, 194, 71, 0), #ffc247 18%, #ffc247 82%, rgba(255, 194, 71, 0));
  box-shadow: 0 0 14px rgba(255, 194, 71, 0.7);
  pointer-events: none;
}
body.theme-tactical .bp-reel-marker::before,
body.theme-tactical .bp-reel-marker::after {
  content: "";
  position: absolute;
  left: -5px;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
}
body.theme-tactical .bp-reel-marker::before { top: -1px; border-top: 9px solid #ffc247; }
body.theme-tactical .bp-reel-marker::after { bottom: -1px; border-bottom: 9px solid #ffc247; }
/* Mgielki po bokach — maskuja „urwane" kafelki i skupiaja wzrok na srodku. */
body.theme-tactical .bp-reel-fade {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 64px;
  pointer-events: none;
}
body.theme-tactical .bp-reel-fade.is-left { left: 0; background: linear-gradient(90deg, #0a0710 12%, rgba(10, 7, 16, 0)); }
body.theme-tactical .bp-reel-fade.is-right { right: 0; background: linear-gradient(270deg, #0a0710 12%, rgba(10, 7, 16, 0)); }

/* Wynik pod rolka — pojawia się dopiero po zatrzymaniu. */
body.theme-tactical .bp-reel-result {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 12px 0 0;
  padding: 0;
  min-height: 0;
}
body.theme-tactical .bp-reel-result:empty { display: none; }
body.theme-tactical .bp-reel-result.is-live {
  padding: 9px 12px;
  background: rgba(20, 14, 30, 0.75);
  border: 1px solid var(--hair);
  border-left: 2px solid #8a92a3;
  animation: bpReelResult 0.4s ease-out 1 both;
}
body.theme-tactical .bp-reel-result.is-rare { border-left-color: #3ddc84; background: rgba(8, 46, 27, 0.6); }
/* [CHANGE 2026-09-20 BP-R29-RARITY] Ultra rare = niebieski (spojnie z kafelkami). */
body.theme-tactical .bp-reel-result.is-ultra { border-left-color: #4d9fff; background: rgba(10, 32, 66, 0.6); }
/* [CHANGE 2026-09-20 BP-R29d-GOLD-PITY] Wynik z gwarancji zlota — zlota krawedz. */
body.theme-tactical .bp-reel-result.is-gold { border-left-color: #ffc247; background: rgba(58, 40, 12, 0.65); }
body.theme-tactical .bp-reel-result.is-pity { border-left-color: #ffc247; background: rgba(52, 36, 6, 0.6); }
@keyframes bpReelResult {
  0% { opacity: 0; transform: translateY(6px); }
  100% { opacity: 1; transform: translateY(0); }
}
body.theme-tactical .bp-reel-result-ico { width: 44px; height: 44px; object-fit: contain; flex: 0 0 44px; }
/* [CHANGE 2026-09-17 BP-R25-REEL-LABELS] Wynik rolki w kolumnie: nazwa nad
   etykieta ilosci („3 hours"), obie wysrodkowane wzgledem siebie. Wczesniej
   `×3` bylo doklejone OBOK nazwy, wiec przy dluzszych nazwach („Mega VIP
   Booster") rozjezdzalo sie i gubilo znaczenie. */
body.theme-tactical .bp-reel-result-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  min-width: 0;
}
body.theme-tactical .bp-reel-result-name {
  font-family: 'Oswald', sans-serif;
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #fff;
}
body.theme-tactical .bp-reel-result-amt {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: #cfc7d8;
}
body.theme-tactical .bp-reel-result-chance {
  margin-left: auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--muted);
}
body.theme-tactical .bp-reel-tag {
  padding: 2px 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid currentColor;
}
body.theme-tactical .bp-reel-tag.is-rare { color: #3ddc84; }
/* [CHANGE 2026-09-20 BP-R29-RARITY] Etykieta „ULTRA RARE" — niebieska. */
body.theme-tactical .bp-reel-tag.is-ultra { color: #4d9fff; }
/* [CHANGE 2026-09-20 BP-R29d-GOLD-PITY] Etykieta „GOLD" — gwarancja 100 otworow. */
body.theme-tactical .bp-reel-tag.is-gold { color: #ffc247; font-weight: 700; }
body.theme-tactical .bp-reel-tag.is-pity { color: #ffc247; }
body.theme-tactical .bp-reel-tag.is-dup { color: #8a92a3; }
/* [CHANGE 2026-09-18 BP-R27C-CURRENCY-VISIBLE] „TO GAME" — waluta zewnętrzna.
   Zielony (kolor „gotowe" na tej stronie), bo to informacja o SUKCESIE: nagroda
   jest już zakolejkowana do gry. Bez tego oznaczenia gracz nie widział dolarów
   ani koron nigdzie — nie trafiają do magazynu (tam są przedmioty) ani do
   portfela (tam są tylko Skull Coiny). */
body.theme-tactical .bp-reel-tag.is-togame { color: #5ef5a8; }
body.theme-tactical .bp-reel-result.is-error {
  padding: 9px 12px;
  font-size: 0.74rem;
  color: #ff8a8a;
  border: 1px solid rgba(255, 138, 138, 0.4);
}

/* Licznik litosci + licznik sesji + historia wynikow. */
/* [CHANGE 2026-09-21 BP-R35-GUARANTEE-UNDER-REEL] DWA LICZNIKI POD ROLKĄ.
   ─────────────────────────────────────────────────────────────────────────
   ZGLOSZENIE (R35, pkt 1c): „pod rolką (Opening Range) Ciągle jest tylko
   guaranteed rare in.. ale nie ma tam guaratteed gold".
   ⚠️ Ten kontener jest teraz ULOKOWANIEM pionowym na DWA wiersze liczników,
   ktore uzywaja tych samych klas co karta skrzyni (`.bp-chest-pity`,
   `.bp-chest-gold`) — dlatego NIE ma tu juz wlasnych `-k/-bar/-fill/-v`.
   Zostawienie ich znaczyloby trzy zestawy stylow dla jednego licznika. */
body.theme-tactical .bp-reel-pity {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 11px 0 0;
}
body.theme-tactical .bp-reel-pity:empty { display: none; }
body.theme-tactical .bp-reel-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 12px 0 0;
}
body.theme-tactical .bp-reel-stat {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  padding: 5px 10px;
  background: rgba(10, 7, 16, 0.7);
  border: 1px solid var(--hair);
}
body.theme-tactical .bp-reel-stat em {
  font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}
body.theme-tactical .bp-reel-stat strong { font-family: 'Oswald', sans-serif; font-size: 0.98rem; color: #e8e3ee; }
body.theme-tactical .bp-reel-stat.is-rare strong { color: #3ddc84; }
body.theme-tactical .bp-reel-history {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  margin: 12px 0 0;
  min-height: 0;
}
body.theme-tactical .bp-reel-history:empty { display: none; }
body.theme-tactical .bp-reel-history-k {
  margin-right: 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  color: var(--muted);
}
body.theme-tactical .bp-reel-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  background: rgba(10, 7, 16, 0.7);
  border: 1px solid var(--hair);
}
body.theme-tactical .bp-reel-tile img { width: 26px; height: 26px; object-fit: contain; }
/* [CHANGE 2026-09-20 BP-R29-RARITY] Kafel historii rolki kolorowany RZADKOSCIA
   z danych (`payload.rarity`), a nie progiem szansy. Kolory MUSZA byc te same
   co na kaflach modalu (`.bp-chest-loot.is-rare` = zielony, `.is-ultra` =
   niebieski) — inaczej ten sam przedmiot ma inny kolor w podgladzie zawartosci
   i w rolce, co tura 19 zglosila jako „nie zmienilo sie to w podgladzie
   w rolkach". Stary rozowy `is-rare` (rgba(255,77,146)) byl reliktem R27,
   sprzed wprowadzenia podzialu rare/ultra. */
body.theme-tactical .bp-reel-tile.is-rare { border-color: rgba(61, 220, 132, 0.7); background: rgba(8, 46, 27, 0.65); }
body.theme-tactical .bp-reel-tile.is-ultra { border-color: rgba(77, 159, 255, 0.75); background: rgba(10, 32, 66, 0.7); }
/* [CHANGE 2026-09-20 BP-R29d-GOLD-PITY] ZLOTA GWARANCJA — najrzadsza nagroda
   (1/100 otworow), wiec ma NAJMOCNIEJSZY wskaznik: zlota ramka + poswiata.
   Kolejnosc: ta regula stoi PO `is-rare`/`is-ultra`, wiec przy nakladaniu sie
   klas (przedmiot z gwarancji moze miec tez tier) wygrywa zloto. */
body.theme-tactical .bp-reel-tile.is-gold {
  border-color: rgba(255, 194, 71, 0.95);
  background: rgba(58, 40, 12, 0.8);
  box-shadow: 0 0 8px rgba(255, 194, 71, 0.5);
}
body.theme-tactical .bp-reel-tile.is-pity { border-color: rgba(255, 194, 71, 0.65); }
body.theme-tactical .bp-reel.is-rolling .bp-reel-btn { opacity: 0.6; }

@media (max-width: 620px) {
  body.theme-tactical .bp-reel-window { height: 112px; }
  body.theme-tactical .bp-reel-card { width: 84px; height: 98px; }
  body.theme-tactical .bp-reel-card img { width: 44px; height: 44px; }
  body.theme-tactical .bp-reel-fade { width: 40px; }
  body.theme-tactical .bp-reel-btn { min-width: 0; }
}

/* ---- 2b. WARIANT W KARCIE SKRZYNI (lista CONTENTS) ---------------------- */
/* [CHANGE 2026-09-17 BP-R26-VARIANT-ALIGN] WYRÓWNANIE KOLUMNY WARIANTÓW.
   ─────────────────────────────────────────────────────────────────────────
   User pkt 8 tury 13: „Jak patrze na Atlas Boosters Box - to tam pokazujemy
   boostery na 6h - ale to 6h jest wyswietlane każdy w innej pozycji - wez to
   jakos wyrównaj żeby ładnie wyglądało".

   BYŁO: `<em>` z wariantem stał W PRZEPŁYWIE nazwy (`name + ' ' + em`), więc
   jego pozycja zależała od długości nazwy. W Atlas Boosters Box nazwy mają
   różną długość („XP Booster 15%" / „Dollars Booster 15%" / „VIP Booster" /
   „Mega VIP Booster"), więc „(6h)" lądowało w czterech różnych kolumnach.

   JEST: górny wiersz wpisu to siatka o DWÓCH kolumnach — nazwa (elastyczna,
   z elipsą) i wariant (stała szerokość, dosunięty do prawej). Warianty
   wyrównują się między wierszami niezależnie od długości nazw.

   ⚠️ `auto` w drugiej kolumnie bierze szerokość NAJSZERSZEGO wariantu w danym
   wierszu, więc gdyby wiersze miały różne zestawy („(1h)" vs „(6h)"), kolumny
   nadal mogłyby się różnić. Dlatego `min-width` na wariancie wymusza wspólną
   szerokość — wystarczającą dla „(6h)" i „×10". */
body.theme-tactical .bp-chest-entry-top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 8px;
}
body.theme-tactical .bp-chest-entry-variant {
  font-style: normal;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  font-weight: 600;
  color: #cfc7d8;
  min-width: 46px;
  text-align: right;
  white-space: nowrap;
}
/* Nazwa jest osobnym elementem (nie tekstem obok `<em>`), żeby mogła być
   osobną kolumną siatki — na gołym tekście ani `text-overflow`, ani zawijanie
   w obrębie kolumny nie zadziałają.
   ⚠️ [CHANGE 2026-09-18 BP-R27E-6-REWARD-NAME-ROW] ELIPSA ZAMIENIONA NA ZAWIJANIE.
   ─────────────────────────────────────────────────────────────────────────────
   ZGŁOSZENIE USERA (ogólne, nie o jednym zadaniu): „Ogolnie jesli damy nagrody to
   nazwy nie będą w pełni widoczne - przerób widok".
   Ta reguła dotyczy WSZYSTKICH nazw w kartach skrzyń (zawartość skrzyni, karta
   skrzyni z zadania), więc obcinanie wielokropkiem jest tu najszerszym źródłem
   problemu: „Atlas Advanced Weapons Box" jest dłuższe niż kolumna i gracz widzi
   „Atlas Advanced Weap…". Wiersz ma stałą wysokość, ale 2 linie mieszczą się
   bez rozjeżdżania siatki (sąsiednie wiersze i tak są niezależne).
   ⚠️ `overflow-wrap: anywhere` łamie też nazwy bez spacji. */
body.theme-tactical .bp-chest-entry-label {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* ---- 3b. Lista dzienna: plynna podmiana po rerollu ---------------------- */
body.theme-tactical .bp-tasks-list.is-flash { animation: bpListFlash 0.7s ease-out 1 both; }
@keyframes bpListFlash {
  0%   { background: rgba(255, 194, 71, 0.16); box-shadow: inset 3px 0 0 #ffc247; }
  100% { background: transparent; box-shadow: inset 0 0 0 transparent; }
}
body.theme-tactical .bp-tasks-list.is-swapping { opacity: 0.55; transition: opacity 0.12s ease; }
body.theme-tactical .bp-badge-reroll .bp-reroll-next { transition: color 0.16s ease; }
body.theme-tactical .bp-reroll-cost-badge.is-free,
body.theme-tactical #bp-reroll-cost-badge.is-free { color: #5ef5a8; }

/* ---- 10. PASEK XP NAD AKTUALNYM SLOTEM — SZERSZY PO OSI Y --------------- */
/* User: „ten mały pasek expa nad aktualnym slotem z levelem zrób szerszy po
   osi Y". Wysokosc kontenera rosnie z 16 px do 26 px, sam pasek z 14 do 24 px,
   a tekst z 0.5rem do 0.68rem — inaczej powiekszony pasek mialby nieczytelny
   podpis. Placeholder w kazdej kolumnie zachowuje te sama wysokosc, wiec
   numery poziomow nadal sie nie rozjezdzaja. */
body.theme-tactical .bp-track-xpinline { height: 26px; margin: 0 0 5px; }
body.theme-tactical .bp-track-xpinline-bar { height: 24px; border-radius: 4px; }
body.theme-tactical .bp-track-xpinline-txt { font-size: 0.68rem; letter-spacing: 0.03em; }
body.theme-tactical .bp-track-xpinline-fill { box-shadow: 0 0 12px rgba(255, 138, 43, 0.55); }
