/* =========================================================================
   WoWPoPolsku — STYLE WŁASNE
   -------------------------------------------------------------------------
   Wszystko, czego Tailwind nie da klasą narzędziową: ziarno tła, ścięte
   narożniki, animacje, zachowanie <dialog>.

   W Django: static/css/wlasne.css, wciągane w base.html przez {% static %}.

   Zasada: tu trafia tylko to, co jest naprawdę wielokrotnego użytku.
   Jednorazowe odstępy i kolory robimy klasami Tailwinda w szablonie —
   inaczej wracamy do wielkiego arkusza CSS, którego nikt nie rusza ze strachu.
   ========================================================================= */

html { scroll-behavior: smooth; }


/* ═══════════════ ZIARNO TŁA ═══════════════
   Delikatny szum nałożony na całą stronę. Bez niego duże gradienty wyglądają
   jak plastik. SVG w data-uri, więc zero dodatkowych żądań do serwera. */
.ziarno::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: .3;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
}


/* ═══════════════ ŚCIĘTE NAROŻNIKI ═══════════════
   Dwa przeciwległe rogi ucięte pod 45°. Daje „kuty" charakter bez ani jednej
   grafiki. Uwaga: clip-path przycina też obramowanie, więc ramkę robimy
   przez zagnieżdżenie — element zewnętrzny z tłem-gradientem i p-px,
   wewnętrzny z właściwym tłem. Oba muszą mieć tę samą klasę klipu. */
.klip   { clip-path: polygon(20px 0, 100% 0, 100% calc(100% - 20px), calc(100% - 20px) 100%, 0 100%, 0 20px); }
.klip-s { clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px); }

/* Wspólny panel wkładu.

   Aura (.twoj-wklad-aura, .twoj-wklad i klatki twoj-wklad-mgla) odeszła razem
   z przejściem panelu na ramy ornamentalne - oprawę robi teraz rama-nad-hero.
   Reguły stały tu jeszcze kilka commitów jako martwy CSS; jeśli szukasz ich
   w historii, wyszły przy sprzątaniu po tym przejściu. */
.ikona-wkladu {
  box-shadow:
    inset 0 0 14px rgba(91, 141, 224, .08),
    0 0 13px rgba(123, 95, 217, .16);
}


/* ═══════════════ TYPOGRAFIA ═══════════════ */
.nadpis { letter-spacing: .34em; }

/* Cyfry nowoczesne (lining) w całym interfejsie.

   Kroje o rodowodzie książkowym domyślnie składają cyfry mediewalne: 3, 4, 5,
   7 i 9 schodzą pod linię pisma, 6 i 8 wystają w górę. W książce to zaleta,
   w tabeli postępu — wygląda jak błąd składu. Dotyczy to zarówno Georgii
   z awaryjnego stosu, jak i samego Cinzela.

   font-variant-numeric jest dziedziczone, więc dla zwykłych elementów starczy
   jedna reguła na <body>. Wyjątkiem są elementy z tailwindowym `tabular-nums`:
   ta klasa składa całą własność z osobnych zmiennych i nadpisałaby wartość
   odziedziczoną. Dlatego druga reguła dokłada cyfry lining do jej zmiennej,
   zamiast walczyć o specyficzność. */
body          { font-variant-numeric: lining-nums; }
.tabular-nums { --tw-numeric-figure: lining-nums; }


/* ═══════════════ ANIMACJE ═══════════════ */
@keyframes obrot        { to        { transform: rotate(360deg); } }
@keyframes obrot-wstecz { to        { transform: rotate(-360deg); } }
@keyframes tetno        { 0%,100%   { opacity:.45; transform:scale(1); }     50% { opacity:.85; transform:scale(1.045); } }
@keyframes unos         { 0%,100%   { transform:translateY(0); opacity:.25; } 50% { transform:translateY(-16px); opacity:.9; } }
@keyframes zorza        { 0%,100%   { opacity:.35; }                          50% { opacity:.7; } }
@keyframes zjazd        { 0%,100%   { transform:translateY(0); opacity:.45; } 50% { transform:translateY(7px); opacity:1; } }

.a-obrot  { animation: obrot 70s linear infinite;        transform-origin: center; }
.a-wstecz { animation: obrot-wstecz 48s linear infinite; transform-origin: center; }
.a-tetno  { animation: tetno 5.5s ease-in-out infinite;  transform-origin: center; }
.a-zorza  { animation: zorza 8s ease-in-out 1 both; }
.a-unos   { animation: unos 7s ease-in-out infinite; }
.a-zjazd  { transform-origin: center; }
.group:hover .a-zjazd,
.group:focus-visible .a-zjazd { animation: zjazd 2.4s ease-in-out infinite; }

/* Krąg bohatera ożywa, gdy użytkownik ogląda ilustrację. W spoczynku trzy
   duże warstwy kompozytowe nie potrzebują osobnych klatek przez całą sesję. */
.hero-ruch .a-obrot,
.hero-ruch .a-wstecz,
.hero-ruch .a-tetno { animation: none; }
.hero-ruch:hover .a-obrot { animation: obrot 70s linear infinite; }
.hero-ruch:hover .a-wstecz { animation: obrot-wstecz 48s linear infinite; }
.hero-ruch:hover .a-tetno { animation: tetno 5.5s ease-in-out infinite; }

/* WARSTWA KOMPOZYTORA DLA RUCHU CIĄGŁEGO

   Chrome potrafi animować `transform` i `opacity` poza głównym wątkiem, ale
   TYLKO na elemencie, który dostał własną warstwę. Bez tej podpowiedzi
   animacja idzie co klatkę przez przeliczenie stylów, układ i malowanie -
   i to właśnie widać było w profilu jako dominujące „Renderowanie".

   Zakładaj tę klasę wyłącznie na elementy, które NAPRAWDĘ ruszają się bez
   przerwy. Każda warstwa kosztuje pamięć karty graficznej, więc rozdawanie
   jej na zapas potrafi zaszkodzić bardziej, niż pomóc.

   Uwaga na SVG: transformacji WEWNĄTRZ `<svg>` (na `<g>` czy `<circle>`)
   Chrome nie skompozytuje - `will-change` nic tam nie da. Dlatego elementy
   z ciągłym ruchem wyciągamy do osobnych warstw HTML, a w środku zostaje
   nieruchoma grafika. Patrz hero na `redakcja/powitanie.html`. */
.warstwa-ruchu { will-change: transform; }

/* Zorza to `opacity` na kole z `blur-[140px]`. Z własną warstwą rozmycie
   rasteryzuje się RAZ, a każda klatka zmienia już tylko krycie gotowej
   tekstury. Bez tego rozmycie potrafi liczyć się od nowa co klatkę. */
.a-zorza { will-change: opacity; }

@media (prefers-reduced-motion: reduce) {
  .warstwa-ruchu,
  .a-zorza { will-change: auto; }
}


/* ═══════════════ ISKRY OGNISKA ═══════════════
   Żar unoszący się od dołu kadru, jakby ognisko stało poza ekranem.
   Rysowaniem zajmuje się wspolne/iskry.js na elemencie <canvas data-iskry>;
   tutaj zostaje tylko ułożenie warstwy. Powód wyboru canvasu: losowe tory,
   boczne znoszenie i migotanie kilkudziesięciu cząstek nie dają się zrobić
   w statycznym CSS bez setek linii inline — i tak wyszłyby regularne.

   Bez JavaScriptu canvas jest pusty, więc strona pozostaje poprawna:
   iskry są ozdobą, nie treścią. Skrypt sam też pilnuje reduced-motion,
   reguła niżej jest drugim zabezpieczeniem. */
.iskry {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;

  /* WŁASNA WARSTWA KOMPOZYTORA - to nie jest kosmetyka.

     Canvas przemalowuje się 60 razy na sekundę. Bez własnej warstwy każde
     jego przemalowanie unieważnia to, co leży pod spodem w tym samym
     kontenerze: pełnoekranowe tło i dwa koła z `blur-[140px]`. Rozmycie
     tej wielkości jest drogie w rasteryzacji, a liczyło się od nowa przy
     każdej klatce iskier.

     Profil DevTools pokazywał to jako „Renderowanie" ponad dwa razy większe
     od „Skryptów" - czyli koszt siedział NIE w pętli cząstek, tylko w tym,
     co ta pętla wymuszała dookoła siebie. `contain` domyka sprawę: mówi
     przeglądarce, że nic z wnętrza canvasu nie wpływa na układ reszty. */
  will-change: transform;
  contain: layout paint;
}
@media (prefers-reduced-motion: reduce) {
  /* Bez ruchu nie ma czego izolować, a warstwa kosztowałaby pamięć GPU. */
  .iskry { display: none; will-change: auto; contain: none; }
}


/* ═══════════════ PORTAL ═══════════════
   Zasłona teleportu między ekranem Obszarów a katalogiem. Znacznik:
   redakcja/_portal.html, logika czasu: static/js/portal.js.

   Domyślnie display: none. Pokazują ją WYŁĄCZNIE klasy na <html>, które
   dokłada JavaScript — bez skryptów zasłona nie istnieje i nie ma jak
   przykryć platformy komuś, kto ma je wyłączone. */
.portal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 30px;
  background: #04070F;
}
html.portal-wejscie .portal,
html.portal-wyjscie .portal { display: flex; }

/* Wejście gaśnie szybko: użytkownik właśnie kliknął i czeka na reakcję.
   Wyjście schodzi wolniej, bo tam nikt już na nic nie czeka — strona pod
   spodem jest gotowa i chodzi tylko o to, żeby się wyłoniła, a nie mrugnęła. */
html.portal-wejscie .portal { animation: portal-zaslona .3s ease forwards; }
html.portal-koniec  .portal { animation: portal-odslona .68s ease forwards; }

@keyframes portal-zaslona { from { opacity: 0; } to { opacity: 1; } }
@keyframes portal-odslona { from { opacity: 1; } to { opacity: 0; } }

.portal-scena {
  width: min(86vw, 620px);
  height: auto;
}

/* Wejście: kamera wjeżdża w scenę. Wyjście: przelatuje przez bramę i scena
   ucieka na boki — oko czyta to jako przejście PRZEZ nią, a nie jako drugie
   odtworzenie tej samej animacji. */
html.portal-wejscie .portal-scena { animation: scena-wjazd .85s cubic-bezier(.2,.85,.25,1) both; }
html.portal-koniec  .portal-scena { animation: scena-przelot .68s cubic-bezier(.4,0,.7,1) both; }

@keyframes scena-wjazd   { from { transform: scale(1.14); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes scena-przelot { from { transform: scale(1); opacity: 1; }    to { transform: scale(1.7); opacity: 0; } }

/* Wszystkie ruchy w scenie mają wspólny punkt zbiegu: środek bramy w układzie
   viewBoksu, czyli 200 140. Bez `transform-box: view-box` przeglądarka liczy
   początek układu z pudełka KAŻDEGO elementu osobno i pierścienie rozjeżdżają
   się w różne strony zamiast rozchodzić z jednego miejsca. */
.portal-tunel path,
.portal-pyl circle {
  transform-box: view-box;
  transform-origin: 200px 140px;
}

/* Pierścienie tunelu: od punktu zbiegu na widza. Krycie rośnie na starcie
   i gaśnie na końcu, więc nie widać ani skąd wyskakują, ani gdzie znikają. */
.portal-tunel path {
  animation: portal-nadciaga 5.5s linear infinite;
}
@keyframes portal-nadciaga {
  0%   { transform: scale(.05); opacity: 0; }
  12%  { opacity: .5; }
  70%  { opacity: .22; }
  100% { transform: scale(3.4); opacity: 0; }
}

/* Pył leci wyraźnie szybciej niż pierścienie — ta różnica prędkości jest tu
   całą głębią. Gdyby obie warstwy szły w tym samym tempie, tunel spłaszczyłby
   się do pulsującego wzoru. */
.portal-pyl circle {
  animation: portal-pyl 2.6s linear infinite;
}
@keyframes portal-pyl {
  0%   { transform: scale(.2); opacity: 0; }
  18%  { opacity: .85; }
  100% { transform: scale(4.2); opacity: 0; }
}

.portal-brama  { animation: tetno 2.8s ease-in-out infinite; transform-origin: center; }
.portal-iskra  { animation: tetno 1.9s ease-in-out infinite; transform-origin: center; }

/* Kołysanie postaci. Okres 0,62 s to tempo spokojnego marszu: szybciej czyta
   się jako bieg, wolniej jako brodzenie. Amplituda celowo mikroskopijna —
   przy większej sylwetka zaczyna podskakiwać zamiast iść. */
.portal-postac {
  transform-box: view-box;
  transform-origin: center;
  animation: portal-krok .62s ease-in-out infinite;
}
@keyframes portal-krok {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-1.6px); }
}

.portal-podpis {
  width: min(86vw, 620px);
  margin: 0;
  text-align: center;
}
.portal-nazwa {
  display: block;
  min-height: 1.2em;
  overflow: hidden;
  font-family: Cinzel, Georgia, serif;
  font-size: clamp(20px, 3vw, 30px);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: .06em;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #EDE8DC;
}
.portal-nota {
  display: block;
  margin-top: 9px;
  font-family: Inter, sans-serif;
  font-size: 10px;
  letter-spacing: .34em;
  text-transform: uppercase;
  color: #C9A227;
}
html.portal-wejscie .portal-podpis { animation: portal-podpis-wejscie .7s ease .12s both; }
@keyframes portal-podpis-wejscie { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }

/* Kto ma wyłączony ruch, ten nie dostaje portalu w ogóle — portal.js pomija
   wtedy przejmowanie kliknięć. Reguła niżej jest drugim zabezpieczeniem na
   wypadek, gdyby klasa trafiła na <html> inną drogą. */
@media (prefers-reduced-motion: reduce) {
  html.portal-wejscie .portal,
  html.portal-wyjscie .portal { display: none; }
}


/* ═══════════════ MAPA ŚWIATA ═══════════════
   Rysowana od zera w SVG przez static/js/mapa-swiata.js. Kształt lądu jest
   jednocześnie obszarem klikalnym, więc nie ma osobnej warstwy hotspotów,
   która mogłaby się rozjechać z podkładem.

   Bez JavaScriptu plansza zostaje ukryta, a widoczna jest tabela z tymi
   samymi liczbami — mapa jest sposobem oglądania danych, nie jedynym
   do nich dostępem. */
#mapa-swiata {
  background: #04070F;
  overflow: hidden;
}
.mw-gora {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
  padding: 11px 18px; border-bottom: 1px solid #1E2E4F; background: #070C1A;
}
.mw-slad { display: flex; align-items: center; gap: 7px; font-size: 12px; }
.mw-slad button {
  background: none; border: 0; color: #C9A227; cursor: pointer; font: inherit;
  padding: 2px 3px; text-decoration: underline;
  text-decoration-color: rgba(201, 162, 39, .3); text-underline-offset: 3px;
}
.mw-slad button:hover { color: #F0D284; }
.mw-slad .teraz { font-family: Cinzel, Georgia, serif; letter-spacing: .05em; color: #EDE8DC; }
.mw-slad .strz { color: #5E6980; }
.mw-leg { display: flex; gap: 13px; font-size: 10px; color: #8794AE; flex-wrap: wrap; }
.mw-leg i {
  display: inline-block; width: 8px; height: 8px; margin-right: 5px; vertical-align: -1px;
  clip-path: polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
}
.mw-scena { position: relative; }
.mw-svg { display: block; width: 100%; height: auto; }
.mw-svg[hidden], .mw-region[hidden], .mw-szczegoly[hidden],
.mw-region-pozostale[hidden] { display: none !important; }
.mw-swiat { cursor: pointer; }
.mw-swiat-sfera, .mw-swiat-orbita, .mw-swiat-blask, .mw-swiat-lad {
  transition: stroke .22s, stroke-width .22s, opacity .22s, filter .22s;
}
.mw-swiat:hover .mw-swiat-sfera,
.mw-swiat:focus-visible .mw-swiat-sfera {
  stroke: #F0D284; stroke-width: 1.9; opacity: 1;
  filter: drop-shadow(0 0 12px rgba(240, 210, 132, .45));
}
.mw-swiat:hover .mw-swiat-blask,
.mw-swiat:focus-visible .mw-swiat-blask { opacity: .26; }
.mw-swiat:hover .mw-swiat-orbita,
.mw-swiat:focus-visible .mw-swiat-orbita { opacity: .75; stroke: #F0D284; }
.mw-swiat:focus-visible { outline: none; }
.mw-swiat-kropka { stroke: #04070F; stroke-width: 1.2px; }
/* Łuk postępu bywa cienki na tle jasnej mgławicy — ciemny cień go odkleja. */
.mw-swiat-postep { filter: drop-shadow(0 0 3px rgba(4, 7, 15, .9)); }
.mw-swiat-nazwa {
  fill: #F4F0E7; font-family: Cinzel, Georgia, serif; font-size: 12px;
  font-weight: 700; letter-spacing: .09em; text-anchor: middle;
  paint-order: stroke; stroke: rgba(4, 7, 15, .98); stroke-width: 4px;
  pointer-events: none;
}
.mw-swiat:hover .mw-swiat-nazwa,
.mw-swiat:focus-visible .mw-swiat-nazwa { fill: #F0D284; }

/* Obrót planety. Trzy identyczne kopie pasm chmur stoją co --przesuw, więc
   przesunięcie o dokładnie ten okres wraca do stanu początkowego i pętla
   nie ma szwu. Sto pięćdziesiąt sekund to ruch na granicy zauważalności —
   mapa ma żyć, a nie migać komuś przed oczami przez cały dzień pracy. */
.mw-swiat-chmury {
  transform-box: view-box;
  transform-origin: 0 0;
  animation: mw-chmury 150s linear infinite;
}
@keyframes mw-chmury { to { transform: translateX(var(--przesuw, 0px)); } }

/* Firefox kosztownie przemalowuje filtry SVG podczas animacji. JS ustawia ten
   sam tryb także dla systemowego `prefers-reduced-motion`; ograniczenie dotyczy
   wyłącznie dekoracyjnego ruchu wewnątrz mapy, nie jej interakcji. */
#mapa-swiata[data-ruch="ograniczony"] * {
  animation: none !important;
  transition: none !important;
}

/* Mapa na powitaniu leży poniżej pierwszego ekranu. JS wznawia ruch dopiero,
   gdy komponent rzeczywiście przecina viewport; zachowujemy bieżącą klatkę,
   zamiast resetować animacje przez `animation: none`. */
#mapa-swiata[data-ruch-aktywny="0"] * {
  animation-play-state: paused !important;
}

/* Płyty Rubieży dryfują każda we własnym rytmie (czas i opóźnienie nadaje
   skrypt), bo zgodny ruch wyglądałby jak drganie całego obrazka. */
.mw-odlamek {
  transform-box: view-box;
  transform-origin: 0 0;
  animation: mw-dryf 15s ease-in-out infinite;
}
@keyframes mw-dryf {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(1.4px, -2.6px); }
}

/* Gwiazdy: bazowa jasność siedzi w --jasnosc, więc każda mruga we własnym
   zakresie zamiast wszystkie do tej samej wartości. */
.mw-gwiazda-mruga { animation: mw-mruganie 5s ease-in-out infinite; }
@keyframes mw-mruganie {
  0%, 100% { opacity: calc(var(--jasnosc, .6) * .3); }
  50%      { opacity: var(--jasnosc, .6); }
}
.mw-gwiazda-blask { animation: mw-rozblysk 7s ease-in-out infinite; }
@keyframes mw-rozblysk { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }

.mw-mglawica-a { animation: mw-oddech-mgly 46s ease-in-out infinite; }
.mw-mglawica-b { animation: mw-oddech-mgly 62s ease-in-out infinite reverse; }
@keyframes mw-oddech-mgly { 0%, 100% { opacity: .72; } 50% { opacity: 1; } }

.mw-maelstrom-nazwa {
  fill: #E7C39A; font-family: Cinzel, Georgia, serif; font-size: 8px;
  font-weight: 700; letter-spacing: .12em; text-anchor: middle;
  paint-order: stroke; stroke: #04070F; stroke-width: 3px;
}
/* UWAGA: obrotu wiru NIE ma w tym pliku i to jest decyzja, nie przeoczenie.
   Wcześniej stał tu `animation` z `transform-box: view-box; transform-origin:
   0 0`, co miało obracać pasma wokół środka wiru. Wir jest jednak rysowany
   w grupie przesuniętej przez `translate(...)`, a przy takim ustawieniu punkt
   odniesienia bierze się z viewBoksu całego SVG, nie z lokalnego układu
   grupy — pasma zataczały więc wielkie koło wokół lewego górnego narożnika
   mapy i po chwili wyjeżdżały za kadr. Bez żadnego błędu w konsoli: wir po
   prostu „nie kręcił się", bo tego, co się kręciło, nie było widać.

   Obrót robi teraz SMIL (`<animateTransform>`) dokładany w mapa-swiata.js.
   W SVG obraca się on wokół (0,0) własnego układu współrzędnych elementu,
   więc nie ma tu żadnej dwuznaczności. Skrypt sam pomija animację przy
   `prefers-reduced-motion`, bo CSS-owa reguła z końca tego pliku nie
   zatrzymuje SMIL-a.

   Zostają za to animacje niezależne od punktu odniesienia (przesunięcia
   i krycie) — te są w CSS bezpieczne. */

.mw-region { background: #060A13; }
.mw-region-kadr {
  position: relative; overflow: hidden; isolation: isolate; background: #070C1A;
}
.mw-region-svg { display: block; width: 100%; height: auto; }
.mw-region-kartusz {
  position: absolute; left: 2.2%; top: 2.7%; z-index: 3; max-width: min(42%, 330px);
  padding: 10px 15px 9px; color: #EDE8DC; border: 1px solid rgba(201, 162, 39, .42);
  border-left: 3px solid #C9A227; background: rgba(4, 7, 15, .88);
  clip-path: polygon(0 0, calc(100% - 11px) 0, 100% 11px, 100% 100%, 0 100%);
}
.mw-region-kartusz strong {
  display: block; font-family: Cinzel, Georgia, serif; font-size: clamp(12px, 1.7vw, 21px);
  line-height: 1.16; letter-spacing: .08em;
}
.mw-region-kartusz span {
  display: block; margin-top: 3px; color: #AEB8CB; font-family: Inter, sans-serif;
  font-size: clamp(8px, .9vw, 11px); letter-spacing: .035em;
}
.mw-region-lad { pointer-events: none; }
.mw-region-kontur {
  opacity: .9; filter: drop-shadow(0 0 5px rgba(91, 141, 224, .42));
}
.mw-region-biomy { opacity: .88; }
.mw-tunele-animy { mix-blend-mode: screen; }
.mw-tunel-animy path { vector-effect: non-scaling-stroke; }
.mw-tunel-animy-zew {
  filter: drop-shadow(0 0 13px rgba(117, 98, 181, .62));
}
.mw-tunel-animy-powloka {
  filter: drop-shadow(0 0 7px rgba(110, 169, 218, .7));
}
.mw-tunel-animy-rdzen {
  filter: drop-shadow(0 0 4px rgba(201, 223, 255, .9));
}
.mw-tunel-animy-czastka { filter: url(#rTunelBlask); }
.mw-zlowroga-anima {
  mix-blend-mode: screen;
  filter: url(#rCzeluscBlask);
}
.mw-zlowroga-anima-aura,
.mw-zlowroga-anima-zyla {
  vector-effect: non-scaling-stroke;
}
.mw-zlowroga-anima-zyla {
  filter: drop-shadow(0 0 5px rgba(190, 16, 48, .82));
}
.mw-zlowroga-anima-iskra {
  filter: drop-shadow(0 0 5px rgba(255, 59, 84, .95));
}
.mw-cienie-emblematy { mix-blend-mode: screen; }
.mw-cienie-torghast {
  filter: drop-shadow(0 0 7px rgba(166, 29, 59, .72));
}
.mw-cienie-nekropolia {
  filter: drop-shadow(0 0 7px rgba(130, 173, 99, .58));
}
.mw-cienie-nathria {
  filter: drop-shadow(0 0 7px rgba(164, 38, 68, .62));
}
.mw-cienie-swiatynia_kyrian {
  filter: drop-shadow(0 0 8px rgba(225, 215, 156, .62));
}
.mw-cienie-serce_lasu {
  filter: drop-shadow(0 0 9px rgba(102, 137, 204, .68));
}
.mw-cienie-swiatlik {
  filter: drop-shadow(0 0 5px rgba(199, 220, 255, .95));
}
.mw-cienie-pierwsi {
  filter: drop-shadow(0 0 7px rgba(211, 203, 163, .6));
}
.mw-draenor-wojna { mix-blend-mode: screen; }
.mw-draenor-szlak-cien,
.mw-draenor-szlak-zelazo,
.mw-draenor-szlak-zar {
  vector-effect: non-scaling-stroke;
}
.mw-draenor-szlak-zelazo {
  filter: drop-shadow(0 0 5px rgba(157, 67, 32, .72));
}
.mw-draenor-szlak-zar {
  filter: drop-shadow(0 0 4px rgba(255, 139, 50, .95));
}
.mw-draenor-szlak-iskra {
  filter: drop-shadow(0 0 5px rgba(255, 166, 66, .95));
}
.mw-draenor-oddzial {
  filter:
    drop-shadow(0 2px 2px rgba(0, 0, 0, .9))
    drop-shadow(0 0 2px rgba(196, 103, 56, .42));
}
.mw-draenor-kuznia {
  filter: drop-shadow(0 0 8px rgba(220, 79, 31, .68));
}
.mw-draenor-portal {
  filter: drop-shadow(0 0 10px rgba(214, 53, 27, .82));
}
.mw-draenor-dym { vector-effect: non-scaling-stroke; }
.mw-draenor-landmarki { mix-blend-mode: screen; }
.mw-draenor-oshugun {
  filter: drop-shadow(0 0 8px rgba(154, 146, 218, .62));
}
.mw-draenor-dusza {
  filter: drop-shadow(0 0 5px rgba(220, 230, 255, .94));
}
.mw-draenor-iglice_araku {
  filter: drop-shadow(5px 7px 7px rgba(0, 0, 0, .7));
}
.mw-draenor-auchindoun {
  filter: drop-shadow(0 0 7px rgba(141, 193, 233, .58));
}
.mw-draenor-karabor {
  filter: drop-shadow(0 0 6px rgba(112, 132, 198, .52));
}
.mw-strefa { cursor: pointer; }
.mw-strefa.bez-danych { cursor: default; }
.mw-strefa-plyta { opacity: .84; transition: filter .22s, opacity .22s; }
.mw-strefa-stan {
  opacity: .8; stroke-linejoin: round;
  transition: fill .22s, stroke .22s, stroke-width .22s;
}
.mw-strefa:hover .mw-strefa-plyta,
.mw-strefa:focus-visible .mw-strefa-plyta,
.mw-strefa.aktywny .mw-strefa-plyta {
  opacity: 1; filter: drop-shadow(0 0 11px rgba(240, 210, 132, .45));
}
.mw-strefa:hover .mw-strefa-stan,
.mw-strefa:focus-visible .mw-strefa-stan,
.mw-strefa.aktywny .mw-strefa-stan {
  stroke: #F0D284; stroke-width: 2.25;
}
.mw-strefa:focus-visible { outline: none; }
.mw-strefa-nazwa {
  fill: #F4F0E7; font-family: Inter, sans-serif; font-size: 12px;
  font-weight: 700; letter-spacing: .025em; text-anchor: middle;
  paint-order: stroke; stroke: rgba(4, 7, 15, .98); stroke-width: 4.5px;
  stroke-linejoin: round; pointer-events: none;
}
.mw-strefa-nazwa.dluga { font-size: 10.5px; letter-spacing: .012em; }
.mw-strefa.bez-danych .mw-strefa-nazwa { fill: #AEB8CB; }
.mw-strefa:hover .mw-strefa-nazwa,
.mw-strefa:focus-visible .mw-strefa-nazwa,
.mw-strefa.aktywny .mw-strefa-nazwa { fill: #F0D284; }
.mw-strefa-punkt {
  pointer-events: none; stroke: #F8F5EC; stroke-width: 1;
  filter: drop-shadow(0 0 4px rgba(4, 7, 15, .95));
}
.mw-region-pozostale {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 18px; border-top: 1px solid #1E2E4F; background: #070C1A;
}
.mw-region-pozostale > span {
  color: #8794AE; font-family: Inter, sans-serif; font-size: 10px;
  letter-spacing: .04em;
}
.mw-region-pozostale > div { display: flex; gap: 7px; flex-wrap: wrap; }
.mw-region-poboczna {
  --stan: #5E6980;
  padding: 5px 8px 5px 10px; border: 1px solid #29395D;
  border-left: 3px solid var(--stan); background: rgba(4, 7, 15, .76);
  color: #AEB8CB; cursor: pointer; font-family: Inter, sans-serif; font-size: 10px;
}
.mw-region-poboczna:hover, .mw-region-poboczna:focus-visible,
.mw-region-poboczna.aktywny {
  border-color: #C9A227; border-left-color: var(--stan); color: #F0D284;
}

.mw-szczegoly {
  padding: 22px 24px 25px; border-top: 1px solid #C9A227;
  background:
    linear-gradient(135deg, rgba(201, 162, 39, .055), transparent 36%),
    #070C1A;
}
.mw-szczegoly-gora {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; margin-bottom: 17px;
}
.mw-nadpis {
  color: #C9A227; font-family: Inter, sans-serif; font-size: 9px;
  font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
}
.mw-szczegoly h3 {
  margin: 4px 0 3px; color: #EDE8DC; font-family: Cinzel, Georgia, serif;
  font-size: clamp(20px, 2.5vw, 30px); line-height: 1.14; letter-spacing: .025em;
}
.mw-szczegoly-gora p {
  margin: 0; color: #8794AE; font-family: Inter, sans-serif; font-size: 12px;
}
.mw-katalog {
  flex: 0 0 auto; padding: 9px 13px; border: 1px solid #C9A227;
  background: rgba(201, 162, 39, .09); color: #F0D284; cursor: pointer;
  font-family: Inter, sans-serif; font-size: 11px; font-weight: 650;
  clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%);
}
.mw-katalog:hover, .mw-katalog:focus-visible { background: rgba(201, 162, 39, .17); }
.mw-kpi { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; margin-bottom: 20px; }
.mw-kpi-kafel {
  padding: 10px 12px; border: 1px solid #1E2E4F; background: rgba(4, 7, 15, .72);
}
.mw-kpi-kafel span {
  color: #8794AE; font-family: Inter, sans-serif; font-size: 9px;
  letter-spacing: .08em; text-transform: uppercase;
}
.mw-kpi-kafel strong {
  display: block; margin-top: 2px; color: #EDE8DC; font-family: Cinzel, Georgia, serif;
  font-size: 20px; font-variant-numeric: tabular-nums;
}
.mw-kpi-tor { height: 3px; margin-top: 7px; overflow: hidden; background: #111B36; }
.mw-kpi-tor i { display: block; height: 100%; }
.mw-tabela-przewijana {
  overflow-x: auto; overflow-y: visible;
  overscroll-behavior-x: contain; overscroll-behavior-y: auto;
}
.mw-tabela-fabul { min-width: 900px; font-family: Inter, sans-serif; }
.mw-tabela-fabul caption { color: #AEB8CB; }
.mw-tabela-fabul thead th { text-align: left; }
.mw-tabela-fabul th, .mw-tabela-fabul td { vertical-align: middle; }
.mw-tabela.mw-tabela-fabul tbody th {
  min-width: 255px; max-width: 380px; color: #EDE8DC; font-family: Inter, sans-serif;
  font-size: 11.5px; font-weight: 600; line-height: 1.35; overflow-wrap: anywhere;
}
.mw-tabela-fabul tbody td { color: #AEB8CB; text-align: left; font-size: 11px; }
.mw-tabela-fabul .mw-liczba { color: #EDE8DC; text-align: right; font-variant-numeric: tabular-nums; }
.mw-patch {
  display: inline-block; padding: 2px 6px; border: 1px solid #29395D;
  color: #A8CBFF; background: rgba(91, 141, 224, .08); font-variant-numeric: tabular-nums;
}
.mw-postep-komorka { display: grid; grid-template-columns: 32px 58px; align-items: center; gap: 7px; }
.mw-postep-komorka b {
  color: #EDE8DC; font-size: 10.5px; font-weight: 600; text-align: right;
  font-variant-numeric: tabular-nums;
}
.mw-postep-komorka > i { display: block; height: 3px; overflow: hidden; background: #111B36; }
.mw-postep-komorka em { display: block; height: 100%; }

.kont { cursor: pointer; }
.kont .plyta { transition: filter .3s; }
.kont:hover .plyta, .kont:focus-visible .plyta { filter: drop-shadow(0 0 14px rgba(240, 210, 132, .55)); }
.kont .kontur { transition: stroke .3s, stroke-width .3s; }
.kont:hover .kontur, .kont:focus-visible .kontur { stroke: #F0D284; stroke-width: 1.9; }
.kont:focus-visible { outline: none; }
.kont .nazwa {
  font-family: Cinzel, Georgia, serif; font-size: 11.5px; font-weight: 700; fill: #EDE8DC;
  letter-spacing: .17em; text-anchor: middle; pointer-events: none;
  paint-order: stroke; stroke: rgba(4, 7, 15, .94); stroke-width: 4.5px; transition: fill .3s;
}
.kont:hover .nazwa { fill: #F0D284; }
.kont.spi { cursor: default; }
.kont.spi .nazwa { fill: #8794AE; }

.med { cursor: pointer; }
.med .tarcza { transition: stroke .25s, stroke-width .25s, filter .25s; stroke-width: 1.2; }
.med:hover .tarcza, .med:focus-visible .tarcza {
  stroke: #F0D284; stroke-width: 2; filter: drop-shadow(0 0 11px rgba(240, 210, 132, .55));
}
.med:focus-visible { outline: none; }
.med .podpis {
  font-family: Cinzel, Georgia, serif; font-size: 9.5px; fill: #EDE8DC; letter-spacing: .05em;
  text-anchor: middle; pointer-events: none;
  paint-order: stroke; stroke: rgba(4, 7, 15, .94); stroke-width: 3.5px; transition: fill .25s;
}
.med:hover .podpis { fill: #F0D284; }

.mw-dymek {
  position: absolute; min-width: 216px; max-width: 270px; padding: 12px 14px;
  left: 0; top: 0; pointer-events: none; opacity: 0;
  transform: translate3d(var(--dymek-x, 0), calc(var(--dymek-y, 0) + 5px), 0);
  transition: opacity .17s;
  background: rgba(4, 7, 15, .97); border: 1px solid #1E2E4F; border-left: 2px solid #C9A227;
  font-size: 11.5px; z-index: 6;
  clip-path: polygon(0 0, calc(100% - 9px) 0, 100% 9px, 100% 100%, 9px 100%, 0 calc(100% - 9px));
}
.mw-dymek.jest {
  opacity: 1;
  transform: translate3d(var(--dymek-x, 0), var(--dymek-y, 0), 0);
}
.mw-dymek h4 {
  margin: 0 0 8px; font-family: Cinzel, Georgia, serif; font-size: 13px;
  letter-spacing: .04em; color: #EDE8DC;
}
.mw-w { display: flex; justify-content: space-between; gap: 10px; padding: 2px 0; color: #8794AE; }
.mw-w b { color: #EDE8DC; font-weight: 500; font-variant-numeric: tabular-nums; }
.mw-w.cicho { color: #5E6980; font-style: italic; }
.mw-hr { height: 1px; background: #1E2E4F; margin: 8px 0; }
.mw-tor { height: 3px; background: #111B36; margin-top: 3px; overflow: hidden; }
.mw-tor i { display: block; height: 100%; }
.mw-dol {
  padding: 9px 18px; border-top: 1px solid #1E2E4F; background: #070C1A;
  font-size: 10px; color: #5E6980; display: flex; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
/* Notka dla gościa: mapa jest publiczna, treść misji nie. */
.mw-gosc {
  padding: 9px 18px; border-top: 1px solid #1E2E4F; background: #070C1A;
  font-size: 11px; color: #8794AE; margin: 0;
}
.mw-zapas { padding: 14px 18px; }
.mw-tabela { width: 100%; border-collapse: collapse; font-size: 12px; }
.mw-tabela caption {
  text-align: left; padding-bottom: 8px; color: #8794AE; font-size: 11px;
}
.mw-tabela th, .mw-tabela td {
  padding: 6px 8px; border-bottom: 1px solid #1E2E4F; text-align: right; color: #EDE8DC;
}
.mw-tabela thead th { color: #8794AE; font-weight: 500; font-size: 10px; text-transform: uppercase; }
.mw-tabela tbody th { text-align: left; font-family: Cinzel, Georgia, serif; font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .plyta, .kontur, .nazwa, .tarcza, .podpis, .mw-dymek,
  .mw-strefa-plyta, .mw-strefa-stan { transition: none; }
  .oddech { display: none; }
  .mw-tunel-animy-rdzen { opacity: .56; }
  .mw-zlowroga-anima-aura { opacity: .58; }
  .mw-zlowroga-anima-zyla,
  .mw-zlowroga-anima-iskra { opacity: .62; }
  .mw-cienie-las-aura { opacity: .48; }
  .mw-cienie-swiatlik { opacity: .64; }
  .mw-draenor-szlak-zar { opacity: .68; }
  .mw-draenor-dym { opacity: .3; }
  .mw-draenor-portal-aura { opacity: .58; }
  .mw-draenor-oshugun-aura,
  .mw-draenor-auchindoun-aura { opacity: .48; }
}

@media (max-width: 700px) {
  .mw-region-kartusz { max-width: 56%; padding: 7px 10px 6px; }
  .mw-strefa-nazwa { font-size: 13px; stroke-width: 5px; }
  .mw-strefa-nazwa.dluga { font-size: 11.5px; }
  .mw-szczegoly { padding: 17px 14px 20px; }
  .mw-szczegoly-gora { display: block; }
  .mw-katalog { margin-top: 13px; }
  .mw-kpi { grid-template-columns: 1fr; }
}


/* ═══════════════ WSKAŹNIK SEKCJI ═══════════════ */
.wezel-etykieta { transition: color .3s, opacity .3s, transform .3s; }
.wezel-kropka   { transition: r .3s, fill .3s, opacity .3s; }

[data-wezel][aria-current="true"] .wezel-etykieta { color:#F0D284; opacity:1; transform:translateX(-4px); }
[data-wezel][aria-current="true"] .wezel-kropka   { fill:#F0D284; opacity:1; }
[data-wezel][aria-current="true"] .wezel-halo     { opacity:.9; }


/* ═══════════════ OKNO MODALNE ═══════════════
   Natywny <dialog> ma domyślną ramkę, tło i padding — zdejmujemy je,
   bo wygląd budujemy w środku. ::backdrop to natywna warstwa przyciemniająca,
   nie trzeba jej tworzyć ręcznie. */
dialog {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: 100vw;
  max-height: 100vh;
}
dialog::backdrop {
  background: rgba(4, 7, 15, .84);
  backdrop-filter: blur(7px);
}

@keyframes okno-wejscie { from { opacity:0; transform:translateY(20px) scale(.965); } to { opacity:1; transform:none; } }
@keyframes tlo-wejscie  { from { opacity:0; } to { opacity:1; } }

dialog[open]           { animation: okno-wejscie .3s cubic-bezier(.2,.85,.25,1); }
dialog[open]::backdrop { animation: tlo-wejscie .3s ease; }


/* ═══════════════ PANEL BOCZNY (filtry) ═══════════════
   Ten sam <dialog>, tylko dosunięty do prawej krawędzi na pełną wysokość.
   Nadal dostajemy Escape i pułapkę fokusu za darmo. */
dialog.panel-boczny {
  margin: 0 0 0 auto;
  height: 100vh;
  max-height: 100vh;
}
@keyframes panel-wjazd { from { transform: translateX(100%); } to { transform: none; } }
dialog.panel-boczny[open] { animation: panel-wjazd .28s cubic-bezier(.2,.85,.25,1); }


/* ═══════════════ GRUPY ZWIJANE ═══════════════
   Zbudowane na natywnym <details>/<summary> — zwijanie działa bez ani jednej
   linii JavaScriptu, a czytnik ekranu sam ogłasza stan rozwinięcia.
   Zdejmujemy tylko domyślny trójkącik, bo rysujemy własną strzałkę. */
summary { list-style: none; cursor: pointer; }
summary::-webkit-details-marker { display: none; }

.strzalka-grupy          { transition: transform .2s ease; }
details[open] .strzalka-grupy { transform: rotate(90deg); }

/* UWAGA NA PRZYSZŁOŚĆ: <summary> musi być BEZPOŚREDNIM dzieckiem <details>.
   Opakowanie go dodatkowym <div> (np. po to, żeby zrobić ramkę) sprawia, że
   przeglądarka uznaje sekcję za pozbawioną nagłówka i przestaje ją zwijać —
   bez żadnego błędu w konsoli. Ramkę robimy na elemencie NADRZĘDNYM wobec
   <details>, nigdy w środku. */


/* ═══════════════ OBSZARY PRZEWIJANE ═══════════════
   Długie listy pokazujemy w okienku o stałej wysokości zamiast rozlewać
   je na całą stronę. Pasek przewijania stylowany, żeby nie wyglądał jak
   systemowy element doklejony do ciemnego interfejsu. */
.przewijane {
  scrollbar-width: thin;                    /* Firefox */
  scrollbar-color: #2A3A5E transparent;     /* Firefox: suwak / tło */
  overscroll-behavior-x: contain;
  overscroll-behavior-y: auto;              /* na krawędzi oddaje scroll stronie */
}
.przewijane::-webkit-scrollbar        { width: 10px; }
.przewijane::-webkit-scrollbar-track  { background: transparent; }
.przewijane::-webkit-scrollbar-thumb  {
  background: #2A3A5E;
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: 99px;
}
.przewijane::-webkit-scrollbar-thumb:hover {
  background: #C9A227;
  background-clip: padding-box;
}


/* ═══════════════ POLA FORMULARZY ═══════════════ */
.pole { transition: border-color .2s, box-shadow .2s, background-color .2s; }
.pole:focus {
  outline: none;
  border-color: rgba(201,162,39,.75);
  box-shadow: 0 0 0 1px rgba(201,162,39,.35), 0 0 22px -8px rgba(240,210,132,.65);
  background-color: rgba(24,39,74,.55);
}


/* ═══════════════ ODSŁANIANIE SEKCJI (strona powitalna) ═══════════════
   Reguły działają wyłącznie pod `.js` na <html> — dodaje ją mały skrypt
   w <head>. Bez JavaScriptu nic nie jest ukryte, strona po prostu stoi. */
.js .odslon {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .8s ease, transform .8s cubic-bezier(.2,.85,.25,1);
  transition-delay: var(--odslon-opoznienie, 0s);
}
.js .odslon.jest { opacity: 1; transform: none; }


/* ═══════════════ MIGOT ZŁOTA ═══════════════
   Powolne przesuwanie gradientu w tekście z bg-clip-text. Element musi mieć
   gradientowe tło; rozmiar rozciągamy tutaj, żeby było czym oddychać. */
@keyframes blask {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}
.a-blask { background-size: 200% auto; animation: blask 7s ease-in-out 1 both; }


/* ═══════════════ DOSTĘPNOŚĆ ═══════════════
   Kto ma włączone ograniczenie ruchu w systemie, dostaje statyczną stronę.
   Nic nie traci — animacje niosą tu wyłącznie nastrój, żadnej informacji. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *    { animation: none !important; transition: none !important; }
}

/* Firefox przy jednoczesnym ruchu filtrowanego SVG, dużych rozmyć i canvasa
   potrafi stale zajmować cały rdzeń. Interfejs nie niesie animacją informacji,
   więc w Gecko zostawiamy wszystkie stany i interakcje, ale bez dekoracyjnego
   ruchu. Canvas oraz SMIL mają odpowiadające zabezpieczenia w swoich skryptach. */
@supports (-moz-appearance: none) {
  * { animation: none !important; }
}


/* Ściąga skrótów w pasku bocznym edytorów. Skrót jest kodem, więc krój
   o stałej szerokości - w proporcjonalnym „{imie}" i „{lmie}" wyglądają
   podobnie, a to jest tekst, który trafia graczowi na ekran. */
.skrot {
  display: inline-block;
  border: 1px solid rgba(201, 162, 39, .3);
  background: rgba(4, 7, 15, .6);
  padding: .1rem .35rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10.5px;
  color: #C9A227;
  transition: border-color .15s ease, color .15s ease;
}
.skrot:hover { border-color: rgba(201, 162, 39, .7); color: #F0D284; }

/* Potwierdzenie skopiowania kolorem, nie podmianą napisu: zmiana treści
   zmieniałaby szerokość przycisku i cała lista drgałaby przy kliknięciu.
   Szmaragd, bo to ten sam sygnał „zrobione", co przy zatwierdzonych tekstach. */
.token-skopiowany {
  border-color: #5FB98B !important;
  color: #8FD6B0 !important;
}

/* ═══════════════ POWIADOMIENIA ═══════════════
   Toast z pierścieniem odliczania. Sam pierścień rysuje SVG, a jego postęp
   ustawia js/powiadomienia.js przez stroke-dashoffset - nie animacją CSS,
   bo blok DOSTĘPNOŚĆ niżej wyłącza animacje przy prefers-reduced-motion,
   a odliczanie niesie informację (ile zostało), nie sam nastrój.

   currentColor: pierścień bierze kolor z opakowującego <span>, a ten dostaje
   w szablonie text-szmaragd. Dzięki temu kolor stanu ustawia się jedną klasą
   Tailwinda obok reszty kolorów kafla, a nie osobną regułą tutaj. */
.powiadomienie {
  cursor: pointer;
  transition: opacity .25s ease, transform .25s ease;
}
.powiadomienie.znika {
  opacity: 0;
  transform: translateX(14px);
}

.odliczanie-tor {
  fill: none;
  stroke: currentColor;
  stroke-opacity: .18;
  stroke-width: 2;
}
.odliczanie-wskaz {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}
.odliczanie-cyfra {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: .7rem;
  line-height: 1;
  opacity: .85;
  font-variant-numeric: tabular-nums;
}


/* ═══════════════ ZNAK (logo w nagłówku) ═══════════════
   Stalówka pióra o obrysie znacznika zadania: jeden kształt mówi
   jednocześnie "tu jest quest" i "tu się pisze". Kropla atramentu stoi
   w miejscu kropki wykrzyknika.

   Nazwy z przedrostkiem "znak-", bo goły "unos" i "obrot" są już zajęte
   przez iskry w tle i portal - i znaczą tam co innego (tamten unos ma
   16 px skoku, tutaj chodzi o 1,6 px oddechu).

   transform-origin w jednostkach viewBoxa, nie w procentach: przy SVG
   procenty liczą się od pudełka elementu, więc pierścień obracałby się
   wokół własnej krawędzi zamiast wokół środka znaku. */
@keyframes znak-unos  { 0%, 100% { transform: translateY(0); }             50% { transform: translateY(-1.6px); } }
@keyframes znak-obrot { to { transform: rotate(360deg); } }
@keyframes znak-tetno { 0%, 100% { opacity: .25; transform: scale(1); }    50% { opacity: .7; transform: scale(1.09); } }

.znak-stalowka,
.znak-pierscien,
.znak-poswiata { transform-origin: 24px 24px; }

/* Pod kursorem znak przyspiesza oddech i rozjaśnia poświatę - drobna
   nagroda za najechanie, bez stałego przemalowywania nagłówka. */
.grupa-znak:hover .znak-stalowka,
.grupa-znak:focus-visible .znak-stalowka { animation: znak-unos 1.9s ease-in-out infinite; }
.grupa-znak:hover .znak-pierscien,
.grupa-znak:focus-visible .znak-pierscien { animation: znak-obrot 12s linear infinite; }
.grupa-znak:hover .znak-poswiata,
.grupa-znak:focus-visible .znak-poswiata { animation: znak-tetno 1.9s ease-in-out infinite; }


/* ═══════════════ PIÓRKO WEJŚCIA DO REDAKCJI ═══════════════
   Jeden znak dla wszystkich katalogów. Piórko leży przy tytule, a nie przy
   prawej krawędzi wiersza, dlatego jego margines jest częścią komponentu.
   Długi bezruch w animacji sprawia, że krótki „telefoniczny” wstrząs wraca
   dokładnie co pięć sekund i nie męczy wzroku podczas czytania listy. */
@keyframes pioro-dzwoni {
  0%, 11%, 100% { transform: translateX(0) rotate(0deg); }
  1.5%          { transform: translateX(-1.4px) rotate(-11deg); }
  3%            { transform: translateX(1.3px) rotate(10deg); }
  4.5%          { transform: translateX(-1.1px) rotate(-9deg); }
  6%            { transform: translateX(1px) rotate(8deg); }
  7.5%          { transform: translateX(-.7px) rotate(-6deg); }
  9%            { transform: translateX(.5px) rotate(4deg); }
}

.pioro-akcji {
  width: 1.45rem;
  height: 1.45rem;
  margin-left: 2.5rem;
  flex: 0 0 auto;
  color: #C9A227;
  opacity: 0;
  pointer-events: none;
  filter: drop-shadow(0 0 5px rgba(201, 162, 39, .28));
  transform-box: fill-box;
  transform-origin: 78% 78%;
  transition: opacity .18s ease, color .18s ease, filter .18s ease;
}
.pioro-poswiata { opacity: .18; }
.uchwyt-piora:hover .pioro-akcji,
.uchwyt-piora:focus-visible .pioro-akcji {
  color: #F8DB72;
  opacity: 1;
  filter: drop-shadow(0 0 6px rgba(248, 219, 114, .48));
  animation: pioro-dzwoni 5s ease-in-out infinite;
}


/* ═══════════════ RAMY ORNAMENTALNE ═══════════════
   Oprawa graficzna zbudowana na `border-image` i pakiecie WebP w
   `wspolne/ornamenty/`. Referencja wizualna: 07_redakcja_ornamenty.html.

   ── DLACZEGO TEN PLIK OBSŁUGUJE OBA ŚWIATY ──────────────────────────────
   `STATICFILES_DIRS` mapuje prefiks `makiety` na `platforma-makiety/wspolne/`,
   więc ten sam arkusz obsługuje makiety otwierane z dysku i produkcję.
   Ścieżki `ornamenty/...` są względne wobec TEGO pliku, dzięki czemu działają
   w obu przypadkach — w makiecie jako sąsiedni katalog, w Django jako
   /static/makiety/ornamenty/. Nie wolno ich zmienić na `../img/...`:
   zadziałałyby tylko po stronie Django i po cichu zepsuły makiety.

   ── DLACZEGO border-image, A NIE TŁO ────────────────────────────────────
   Obraz jest cięty na dziewięć pól. Narożniki NIGDY się nie skalują,
   krawędzie rozciągają się między nimi, środek jest pomijany. Jeden plik
   obsługuje kafel 200 px i kafel 1400 px z identycznie ostrym okuciem.
   Zwykłe `background-size` deformowałoby narożniki razem z resztą.

   ── DWIE LICZBY, KTÓRE TRZEBA ROZUMIEĆ ──────────────────────────────────
   `border-image-slice` — GDZIE tniemy PLIK (piksele obrazu, bez jednostki)
   `border-image-width` — ILE miejsca ornament zajmie NA EKRANIE

   Assety są w skali 2×, więc `width` to zwykle połowa `slice`. Zmiana samego
   `width` skaluje okucie bez ruszania pliku — to jedyna gałka, którą reguluje
   się wielkość ozdoby.

   ── CHWYT, KTÓRY WRACA W CAŁYM UKŁADZIE ─────────────────────────────────
   `border-width` (ile ramka zabiera UKŁADOWI) bywa tu mniejszy niż
   `border-image-width` (ile RYSUJE). Wtedy ornament wychodzi do środka
   i maluje NAD polem treści, nie spychając jej. Bez tego treść musiałaby
   zaczynać się kilkadziesiąt pikseli od krawędzi i kafel wyglądałby jak
   kartka włożona luzem do ramki. Wzdłuż krawędzi ornament to sama cienka
   listwa, więc nic nie zasłania; bryłę ma tylko w narożnikach.

   ── PUŁAPKA, KTÓRA MYLI OBJAW Z PRZYCZYNĄ ───────────────────────────────
   `border-color: transparent` jest OBOWIĄZKOWY. Gdy obraz się nie wczyta
   (zła ścieżka, brak pliku po collectstatic), przeglądarka rysuje zwykły
   border w kolorze `currentColor` — na ciemnym motywie daje to grube kremowe
   pasy, które wyglądają jak zepsuty ornament, a nie jak brak pliku.

   ── HIERARCHIA CIĘŻARU ──────────────────────────────────────────────────
   hero  — raz na ekranie, bogaty (niebieska wstawka, gwiazda, gem)
   panel — kilka razy, średni
   blok  — kilkadziesiąt razy, prawie niewidoczny

   To nie jest kwestia gustu. Gdyby blok dostał ornament hero, ekran zamieniłby
   się w tapetę, a redaktor siedzi przy nim godzinami i ma czytać tekst,
   nie ramki.

   ── RELACJA DO `.klip` ──────────────────────────────────────────────────
   Ramy ZASTĘPUJĄ `.klip` na kontenerach. `.klip-s` zostaje na drobiazgach:
   przyciskach, chipsach, polach. Obu naraz na jednym elemencie użyć się nie
   da — `clip-path` ścina rogi pod 45°, czyli wycina dokładnie te piksele,
   w których miałoby stać okucie. */

/* ═══════════ KTÓRA RAMKA GDZIE ═══════════
   Tabela decyzyjna dla wszystkich obszarów roboczych. Szablon nie wymyśla
   geometrii - wybiera rolę z tej listy. Dzięki temu rozmowy, dymki,
   cinematyki, dokumenty, przedmioty, czary i interfejs dostają ten sam
   wygląd bez kopiowania klas z ekranu do ekranu.

   ┌─────────────────────────┬──────────────────────────────────────────────┐
   │ ROLA NA EKRANIE         │ KLASY                                        │
   ├─────────────────────────┼──────────────────────────────────────────────┤
   │ kafel-bohater           │ rama-hero                                    │
   │  (powitanie, tytuł      │ + rama-nad-hero ZAMIAST, gdy pod ramkę       │
   │   zadania, Twój wkład)  │   wchodzi zdjęcie na całą powierzchnię       │
   ├─────────────────────────┼──────────────────────────────────────────────┤
   │ panel / katalog / mapa  │ rama-panel                                   │
   │  (wybór dodatku, listy  │ + rama-panel-plaska, gdy treść ma dochodzić  │
   │   wątków, obszary,      │   do samej listwy (listy, tabele)            │
   │   baner krainy)         │ + rama-nad ZAMIAST, gdy pod ramką jest       │
   │                         │   zdjęcie na całą powierzchnię               │
   ├─────────────────────────┼──────────────────────────────────────────────┤
   │ kafel powtarzalny       │ rama-blok                                    │
   │  (blok redakcyjny)      │                                              │
   ├─────────────────────────┼──────────────────────────────────────────────┤
   │ drobiazg                │ rama-blok rama-blok-s                        │
   │  (przyciski, chipsy,    │                                              │
   │   pola, awatary)        │                                              │
   ├─────────────────────────┼──────────────────────────────────────────────┤
   │ przycisk WYPEŁNIONY     │ klip-s  (bez zmian)                          │
   │  kolorem                │ ścięty róg jest kształtem przycisku,         │
   │                         │ nie obramowaniem - nie ma czego zastępować   │
   └─────────────────────────┴──────────────────────────────────────────────┘

   KIEDY `rama-nad` ZAMIAST `rama-panel`: zawsze, gdy w kaflu jest grafika
   rozciągnięta na całą powierzchnię. Dzieci malują się NAD tłem i
   obramowaniem rodzica, więc zwykła ramka zostałaby przez zdjęcie przykryta.
   Objaw jest mylący - ramka wygląda, jakby „znikała z jednej strony".

   PUŁAPKA PRZY `rama-nad`: `border-width` nakładki nie rezerwuje miejsca
   w układzie, więc odstęp treści od ramki robi się zwykłym paddingiem
   na kaflu (px-7 py-7 albo px-5 py-5). */

.rama-hero,
.rama-panel,
.rama-blok {
  border-style: solid;
  border-color: transparent;
  background-clip: border-box;
}

/* Kartusz tytułu zadania. Ornament sięga 131 px, tniemy na 96/156 — linia
   cięcia ma paść na PROSTĄ listwę, nigdy w połowę zdobienia. */
.rama-hero {
  border-width: 20px 22px;
  border-image: url("ornamenty/ramka-hero.34dce8d6eec6.webp") 96 156 / 46px 76px stretch;
  position: relative;
}

/* Mikro-wariant kartusza — przycisk użytkownika w pasku górnym.
   UWAGA: `slice` jest niesymetryczny (96 w pionie, 156 w poziomie), więc
   `border-image-width` musi zachować ten sam stosunek. Równe wartości
   ścisnęłyby narożniki w jedną stronę. */
.rama-hero-s {
  border-width: 5px 8px;
  border-image-width: 15px 24px;
}

.rama-panel {
  border-width: 22px;
  border-image: url("ornamenty/ramka-panel.8d55d1f65d57.webp") 44 / 22px stretch;
}

/* Wariant dla kontenerów, w których treść ma dochodzić do samej listwy:
   listy wątków, katalogi, panele z własnymi nagłówkami. Ten sam chwyt co
   w kafelku bloku - układ odsunięty o 9 px, ornament rysowany w pełnych 22.
   Bez tego wiersz listy kończyłby się 22 px przed ramką i cały kontener
   wyglądałby jak kartka włożona luzem. */
.rama-panel-plaska {
  border-width: 9px;
  border-image-width: 22px;
}

/* Blok redakcyjny — najlżejszy, bo powtarza się kilkadziesiąt razy na ekranie.
   `overflow: hidden` jest bezpieczne: przycina TREŚĆ, nie `border-image`. */
.rama-blok {
  border-width: 9px;
  border-image: url("ornamenty/ramka-blok.57feeed4cc4e.webp") 50 / 25px stretch;
  overflow: hidden;
}

/* ═══════════ RAMKA RYSOWANA NAD TREŚCIĄ ═══════════
   Zwykła `border-image` siedzi na samym elemencie, a dzieci malują się NAD
   tłem i obramowaniem rodzica. Dopóki w kaflu jest sam tekst, nie ma to
   znaczenia. Gdy jednak pod ornament ma wejść zdjęcie rozciągnięte na całą
   powierzchnię, grafika zakrywa okucia — i to nie po trochu, tylko dokładnie
   tam, gdzie sięga. Objaw jest mylący: ramka wygląda, jakby „znikała po
   lewej", a w rzeczywistości jest przykryta.

   Tutaj ornament rysuje NAKŁADKA `::after` rozpięta na `inset: 0`. Stoi nad
   wszystkimi dziećmi, więc zdjęcie może spokojnie wypełnić kafel od krawędzi
   do krawędzi, a okucia i tak leżą na wierzchu.

   Dwie konsekwencje, o których trzeba pamiętać:
   - `border-width` nakładki NIE rezerwuje miejsca w układzie, więc odstęp
     treści od ramki robi się zwykłym paddingiem na kaflu,
   - `pointer-events: none` jest konieczny, inaczej nakładka przechwyci
     kliknięcia w cały kafel. */

.rama-nad,
.rama-nad-hero { position: relative; }

.rama-nad::after,
.rama-nad-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-style: solid;
  border-color: transparent;
}

.rama-nad::after {
  border-width: 9px;
  border-image: url("ornamenty/ramka-panel.8d55d1f65d57.webp") 44 / 22px stretch;
}

.rama-nad-hero::after {
  border-width: 12px 14px;
  border-image: url("ornamenty/ramka-hero.34dce8d6eec6.webp") 96 156 / 46px 76px stretch;
}

/* Mniejsza nakładka ornamentalna — kafelki filtra na stronie Szukaj.
   Odpowiada `.rama-blok-s`, ale rysuje ramkę NAD grafiką, nie obok niej. */
.rama-nad-s::after {
  border-width: 6px;
  border-image: url("ornamenty/ramka-blok.57feeed4cc4e.webp") 50 / 17px stretch;
}

/* Mniejsza skala tej samej ramki — pudełka w kartuszu, przyciski w pasku. */
.rama-blok-s {
  border-width: 6px;
  border-image-width: 17px;
}

/* Zakładka obszaru i przycisk zatwierdzania: ozdobne są tylko ścięte końce,
   środek jest płaski. Stąd slice pionowy 0 i `fill`, które zostawia środek
   jako rozciągane wypełnienie.

   Napis MUSI zaczynać się za skosem (stąd `border-width: 0 42px`). Bliżej
   leżałby na ukośnej krawędzi, częściowo poza błękitem. */
.zakladka {
  border-style: solid;
  border-color: transparent;
  border-width: 0 42px;
  border-image: url("ornamenty/zakladka.f6bea3727db1.webp") 0 44 fill / 0 44px stretch;
  min-height: 46px;
}

.przycisk-zatwierdz {
  border-style: solid;
  border-color: transparent;
  border-width: 0 46px;
  border-image: url("ornamenty/przycisk-nieaktywny.56aad067e4c0.webp") 0 47 fill / 0 46px stretch;
  height: 47px;
}
.przycisk-zatwierdz.aktywny {
  border-image-source: url("ornamenty/przycisk-aktywny.342f6119ca36.webp");
}


/* ═══════════════ TŁA ORNAMENTALNE ═══════════════
   Wszystkie mają mocny woal (70–88% krycia). To są tła POD TEKST, który
   redaktor czyta godzinami, a nie ilustracje. Faktura ma dawać wrażenie
   materiału, nie rywalizować z literami.

   `background-origin: border-box` wraca wszędzie tam, gdzie pod spodem jest
   `border-image`. Domyślnie tło wymierza się do padding-boxa, czyli do
   obszaru WEWNĄTRZ ramki — obraz kończyłby się przed ornamentem i zostawiał
   pusty pas. `background-clip` sam tego nie naprawia: on decyduje, DOKĄD
   wolno malować, a nie CZYM się mierzy. */

.kartusz-tlo {
  background-image:
    linear-gradient(180deg, rgba(4, 7, 15, .55), rgba(4, 7, 15, .78)),
    url("ornamenty/hero-tlo.a3066374c0e1.webp");
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-color: #04070F;
  background-origin: border-box;
  background-clip: border-box;
}

/* Sztandar wisi przy lewej krawędzi kartusza. Ujemna góra wciąga go pod
   górną listwę, dodatnia lewa odsuwa od pionowej — inaczej okucie narożnika
   i ornament sztandaru nachodzą na siebie i oba przestają być czytelne. */
.sztandar {
  width: 96px;
  align-self: flex-start;
  aspect-ratio: 212 / 327;
  background: url("ornamenty/sztandar.ed83f8a273b4.webp") top center / 100% auto no-repeat;
  position: relative;
  margin: -20px 0 -14px 6px;
}

/* Klejnot na środku PŁACHTY, nie pliku. Zmierzone z alfy sztandaru: materiał
   ciągnie się od x=11 do x=181 przy szerokości pliku 212 px, więc jego oś
   wypada na 45,3%, a nie na 50%. Te niecałe pięć punktów procentowych widać
   gołym okiem. Gdyby sztandar był kiedyś przerysowany i wycentrowany
   w kadrze, ta wartość wróci do 50%. */
.sztandar img {
  position: absolute;
  left: 45.3%;
  top: 42%;
  translate: -50% -50%;
  width: 46%;
}

/* Górny pasek i nagłówek bloku: ozdoby siedzą przy samych krawędziach pliku,
   więc `100% 100%` zamiast `cover`. Na tak niskim i szerokim elemencie
   `cover` pokazałby sam środek i zgubił oba motywy. Przy tak rozmytej
   fakturze rozciągnięcie jest niewidoczne. */
.pasek-tlo {
  background-image:
    linear-gradient(180deg, rgba(4, 7, 15, .72), rgba(4, 7, 15, .88)),
    url("ornamenty/tlo-gorny-pasek.8562b595ae7b.webp");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: center;
}

.tlo-naglowek {
  background-image:
    linear-gradient(90deg, rgba(7, 12, 26, .8), rgba(7, 12, 26, .62) 50%, rgba(7, 12, 26, .8)),
    url("ornamenty/tlo-naglowek.c03dbe53f96f.webp");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: center;
}

/* Wspólny, zwarty pasek akcji w edytorach wypowiedzi, rozmów, filmów
   i dokumentów. Ciemny woal utrzymuje czytelność nad fakturą nagłówka. */
.pasek-zatwierdzania {
  background-image:
    linear-gradient(90deg, rgba(4, 7, 15, .96), rgba(7, 12, 26, .86) 50%, rgba(4, 7, 15, .96)),
    url("../img/ornamenty/tlo-naglowek.c03dbe53f96f.webp");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: center;
  box-shadow: 0 -12px 30px rgba(1, 3, 10, .42);
}

/* Trzy kolumny bloku. Każda niesie inny stan tekstu, więc dostaje własną
   fakturę: oryginał jest najzimniejszy (tekst źródłowy, którego się nie
   zmienia), redakcja AI ma arkanę, wersja robocza cieplejszą płytę. */
.tlo-kolumny {
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  background-origin: border-box;
  background-clip: border-box;
}
.tlo-oryginal {
  background-image:
    linear-gradient(rgba(7, 12, 26, .74), rgba(7, 12, 26, .8)),
    url("ornamenty/tlo-oryginal.ae8b96893939.webp");
}
.tlo-redakcja {
  background-image:
    linear-gradient(rgba(11, 18, 38, .7), rgba(11, 18, 38, .78)),
    url("ornamenty/tlo-redakcja.395a7bf78e6d.webp");
}
.tlo-zatwierdzone {
  background-image:
    linear-gradient(rgba(7, 12, 26, .72), rgba(7, 12, 26, .8)),
    url("ornamenty/tlo-zatwierdzone.e9205da09d33.webp");
}

/* Panele boczne. Woal jest najmocniejszy po tej stronie, po której stoi
   treść: przy „Metadanych" to lista dt/dd, a tabelka na wzorzystym tle robi
   się nieczytelna dużo szybciej niż akapit. */
.metadane-tlo {
  background-image:
    linear-gradient(90deg, rgba(7, 12, 26, .95) 30%, rgba(7, 12, 26, .6) 70%, rgba(7, 12, 26, .35) 100%),
    url("ornamenty/tlo-metadane.d8d563c38a5b.webp");
  background-position: center, right center;
  background-size: cover;
  background-repeat: no-repeat;
  background-origin: border-box;
  background-clip: border-box;
}
.pergamin-tlo {
  background-image:
    linear-gradient(180deg, rgba(7, 12, 26, .82), rgba(7, 12, 26, .9)),
    url("ornamenty/tlo-pergamin.9f83b969dcda.webp");
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-origin: border-box;
  background-clip: border-box;
}
/* Sceneria wjeżdża od PRAWEJ i gaśnie w lewo. Obraz jest już narysowany
   z ciemną lewą połową, więc gradient tylko dokańcza przejście — tekst
   zawsze leży na spokojnym tle, niezależnie od tego, ile go jest. */
.kontekst-tlo {
  background-image:
    linear-gradient(90deg, rgba(11, 18, 38, .97) 22%, rgba(11, 18, 38, .55) 58%, rgba(11, 18, 38, .3) 100%),
    url("ornamenty/kontekst-swiata.0fc74701b6be.webp");
  background-position: center, right center;
  background-size: cover;
  background-repeat: no-repeat;
  background-origin: border-box;
  background-clip: border-box;
}


/* ═══════════════ ROMBY ═══════════════ */

/* Numer bloku. Ramka z pliku, cyfra jako tekst — inaczej każda zmiana
   numeracji znaczyłaby nową grafikę. */
.romb-numer {
  width: 31px;
  height: 31px;
  background: url("ornamenty/romb-numer.379973e21854.webp") center/contain no-repeat;
  display: grid;
  place-items: center;
  font-family: Cinzel, Georgia, serif;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: #F0D284;
}

/* Znacznik przy nagłówku kolumny. Plik jest biały, więc zamiast wstawiać go
   jako obrazek, używamy go jako MASKI na kolorze tekstu — jeden plik
   obsługuje wszystkie barwy kolumn i nie trzeba wariantów. */
.znacznik {
  display: inline-block;
  width: 9px;
  height: 9px;
  background: currentColor;
  -webkit-mask: url("ornamenty/romb-znacznik.ae23bb6019a2.webp") center/contain no-repeat;
          mask: url("ornamenty/romb-znacznik.ae23bb6019a2.webp") center/contain no-repeat;
}

/* Link-pułapka na automaty (base.html). Odsunięty poza kadr, a nie ukryty
   przez `display:none` — element z `display:none` przeglądarka sterowana
   skryptem rozpoznaje jednym pytaniem o widoczność, a wtedy pułapka sama się
   zdradza. Odsunięcie zostawia go normalnie wyrenderowanym.

   Rozmiar 1x1 i `overflow:hidden` pilnują, żeby nie wpływał na układ strony,
   a `pointer-events:none` — żeby nie dało się w niego trafić kursorem nawet
   przypadkiem. Za odcięcie od czytników ekranu i klawisza Tab odpowiada sam
   znacznik (`aria-hidden`, `tabindex="-1"`). */
.pulapka {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
  pointer-events: none;
}
