@import url("tokens.css");

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: 'Poppins', system-ui, sans-serif;
  background: var(--void);
  color: var(--text);
  line-height: 1.6;
  overflow-x: hidden;
}

a { color: var(--fel); text-decoration: none; transition: color .2s ease; }
a:hover { color: var(--arcane); }

img { max-width: 100%; display: block; }

/* Linki-miniaturki (<a> jest domyślnie inline) muszą być blokowe, inaczej aspect-ratio
   i wysokość 100% dziecka-<img> są w specyfikacji nieokreślone dla elementów inline -
   stąd niespójny "pasek" pod obrazkiem różniący się między przeglądarkami. */
a.thumb-wrap { display: block; }

.muted { color: var(--muted); }

/* ---------- tło: rozmyte, powoli dryfujące plamy fel-zieleni / fioletu ---------- */
.site-glow {
  position: fixed;
  inset: -10%;
  z-index: -1;
  background:
    radial-gradient(circle at 12% 8%, rgba(139, 92, 246, 0.22), transparent 42%),
    radial-gradient(circle at 88% 15%, rgba(57, 255, 143, 0.14), transparent 40%),
    radial-gradient(circle at 30% 90%, rgba(57, 255, 143, 0.1), transparent 45%),
    radial-gradient(circle at 80% 85%, rgba(139, 92, 246, 0.16), transparent 42%),
    var(--void);
  animation: glow-drift 26s ease-in-out infinite alternate;
  /* Wymusza własną warstwę kompozytora - bez tego przeglądarka potrafi przemalowywać
     tło co klatkę, co przy wielu kartach z backdrop-filter (np. strona sprzętu)
     mocno obciąża FPS. */
  will-change: transform;
}

@keyframes glow-drift {
  0% { transform: translate3d(0, 0, 0) scale(1); }
  100% { transform: translate3d(-2%, 2%, 0) scale(1.05); }
}

/* ---------- nav ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  /* Tło mocniejsze, blur lżejszy niż wcześniej (16px) - pełny rozmyty "szron" na
     position:sticky to jeden z głównych powodów jankowania scrolla na Chrome (Chrome
     przelicza backdrop-filter na bieżąco, klatka po klatce, dopóki nagłówek jest w
     kadrze - czyli przez CAŁY czas scrollowania, nie tylko przy zmianie). Podniesienie
     nieprzezroczystości tła kompensuje wizualnie słabszy blur, więc "szkło" wygląda
     tak samo, kosztując przy tym dużo mniej. */
  background: rgba(10, 7, 18, 0.88);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--panel-border);
}
.site-header__inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: .9rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.brand {
  font-weight: 700;
  letter-spacing: .05em;
  font-size: 1.05rem;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: .5rem;
}
.brand { transition: color .2s ease; }
.brand i { color: var(--fel); display: inline-block; transition: transform .25s cubic-bezier(.2,.8,.2,1), color .2s ease; }
.brand:hover { color: var(--text); }
.brand:hover i { transform: scale(1.15) rotate(-6deg); color: var(--arcane); }

.primary-nav ul {
  list-style: none;
  display: flex;
  gap: .3rem;
  margin: 0;
  padding: 0;
  flex-wrap: wrap;
}
.primary-nav a {
  color: var(--muted);
  padding: .5rem .85rem;
  border-radius: var(--radius-sm);
  font-size: .92rem;
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  border: 1px solid transparent;
  background-clip: padding-box;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.primary-nav a:hover { color: var(--text); border-color: var(--panel-border); }
.primary-nav a.active { color: var(--void); background: linear-gradient(135deg, var(--fel), var(--arcane)); background-clip: padding-box; font-weight: 600; }
.primary-nav a i { transition: transform .2s ease; }
.primary-nav a:hover i { transform: scale(1.15); }

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
  padding: .5rem .6rem;
  cursor: pointer;
  transition: border-color .2s ease;
}
.nav-toggle:hover { border-color: var(--panel-border-strong); }
.nav-toggle span { width: 20px; height: 2px; background: var(--text); display: block; transition: transform .25s cubic-bezier(.2,.8,.2,1), opacity .2s ease; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Pozycja oznaczona jako "tylko ikona" (Wsparcie): na dużym ekranie sama ikonka, żeby nie
   zapychać nagłówka kolejną etykietą, a nazwa pojawia się w dymku po najechaniu.
   Poniżej 900 px reguły te są nadpisywane i pozycja wygląda jak każda inna. */
@media (min-width: 901px) {
  .primary-nav a.nav-icon-only { position: relative; }
  .primary-nav a.nav-icon-only .nav-label {
    /* Etykieta zostaje w DOM dla czytników ekranu, ale znika z układu. */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .primary-nav a.nav-icon-only::after {
    content: attr(data-nav-tip);
    position: absolute;
    top: calc(100% + .5rem);
    left: 50%;
    transform: translateX(-50%) translateY(-4px);
    padding: .3rem .6rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--panel-border);
    background: rgba(10, 7, 18, .96);
    color: var(--text);
    font-size: .78rem;
    font-weight: 500;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity .18s ease, transform .18s ease;
    z-index: 20;
  }
  .primary-nav a.nav-icon-only:hover::after,
  .primary-nav a.nav-icon-only:focus-visible::after {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}

@media (max-width: 900px) {
  .nav-toggle { display: flex; }
  .primary-nav {
    display: grid;
    grid-template-rows: 0fr;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: rgba(10, 7, 18, 0.97);
    border-bottom: 1px solid var(--panel-border);
    opacity: 0;
    transition: grid-template-rows .3s cubic-bezier(.2,.8,.2,1), opacity .25s ease;
  }
  .primary-nav.open { grid-template-rows: 1fr; opacity: 1; }
  .primary-nav > ul {
    overflow: hidden;
    min-height: 0;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: flex-end;
    padding: 0 1.5rem;
    gap: .3rem;
    transition: padding .3s ease;
  }
  .primary-nav.open > ul { padding: 1rem 1.5rem 1.5rem; }
  .site-header { position: sticky; }
  .site-header__inner { position: relative; }
}

@media (prefers-reduced-motion: reduce) {
  .primary-nav, .primary-nav > ul, .nav-toggle span { transition: none; }
}

/* ---------- hero ---------- */
.hero {
  position: relative;
  padding: 6rem 1.5rem 5rem;
  text-align: center;
  overflow: hidden;
}
.hero--with-bg { background-size: cover; background-position: center; }
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10,7,18,.55) 0%, rgba(10,7,18,.85) 75%, var(--void) 100%);
  z-index: 0;
}
.hero__content {
  position: relative;
  z-index: 1;
  max-width: 720px;
  margin: 0 auto;
  padding: 2.2rem 2rem;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-glow);
}
/* Puls glow jako osobna, tylko-opacity animacja pseudo-elementu (tańsze niż animowanie
   samego box-shadow klatka po klatce, które wymusza przemalowanie na głównym wątku). */
.hero__content::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 55px rgba(139, 92, 246, 0.25), 0 0 95px rgba(57, 255, 143, 0.14);
  opacity: 0;
  animation: hero-glow-pulse 4s ease-in-out infinite;
  pointer-events: none;
  z-index: -1;
}

@keyframes hero-glow-pulse {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}
.hero__eyebrow { color: var(--muted); font-size: .95rem; letter-spacing: .08em; text-transform: uppercase; margin: 0; }
.hero h1 {
  font-size: clamp(2rem, 5vw, 3.2rem);
  margin: .2rem 0 .6rem;
  background: linear-gradient(135deg, var(--fel), var(--arcane));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero p { color: var(--text); margin: 0; }

/* ---------- sekcje / karty ---------- */
.section { max-width: 1100px; margin: 0 auto; padding: 3.2rem 1.5rem; }
.section-heading { text-align: center; margin-bottom: 2rem; }
.section-heading h2 { font-size: 1.6rem; margin: 0 0 .4rem; }
.section-heading h2 i, .category-block h2 i, .equipment-group h2 i {
  color: var(--fel);
  display: inline-block;
  transition: transform .25s cubic-bezier(.2,.8,.2,1), color .2s ease;
}
.section-heading h2:hover i, .category-block h2:hover i, .equipment-group h2:hover i {
  transform: scale(1.2) rotate(-6deg);
  color: var(--arcane);
}

.card {
  /* Bez backdrop-filter: mieszany z animowanym tłem w tle i przewijaniem w Chrome
     backdrop-filter na wielu kartach naraz (np. 31 na stronie sprzętu) potrafi mocno
     zjadać FPS - każda karta co klatkę na nowo próbkuje i rozmywa to, co jest pod nią.
     Nieprzezroczyste tło + obwódka + poświata dają podobny efekt "szkła" dużo taniej. */
  background: rgba(20, 15, 30, 0.72);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 1.6rem;
  /* Ta sama liczba warstw cienia co w :hover, tylko przezroczysta - dzięki temu
     przejście naprawdę się rozjaśnia (fade), zamiast skakać z "none" na cień. */
  box-shadow: 0 0 40px rgba(139, 92, 246, 0), 0 0 80px rgba(57, 255, 143, 0);
  transition: transform .3s cubic-bezier(.2,.8,.2,1), border-color .3s ease, box-shadow .4s ease;
}
.card:hover { border-color: var(--panel-border-strong); box-shadow: var(--shadow-glow); transform: translateY(-4px); }
a.card:hover { color: var(--fel); }

/* ---------- animacje przy scrollu (własna, minimalna zamiana biblioteki AOS) ----------
   Wcześniej biblioteka AOS z CDN - zdjęta na rzecz IntersectionObserver w main.js
   (initScrollReveal()), bo: (1) sama w sobie nasłuchiwała scrolla na całej stronie,
   co dokłada się do jankowania Chrome przy przewijaniu obok blur/backdrop-filter;
   (2) CSS z CDN nie dawał się debugować z tej domeny (blokada CORS na introspekcję
   reguł przez JS). Wartości przesunięcia/skali poniżej to dokładnie te, których
   używała AOS 2.3.1 dla "fade-up"/"zoom-in" - wygląd się nie zmienił, zniknął tylko
   koszt biblioteki. */
[data-aos] {
  opacity: 0;
  transition-property: opacity, transform;
  transition-duration: .6s;
  transition-timing-function: ease;
}
[data-aos="fade-up"] { transform: translate3d(0, 100px, 0); }
[data-aos="zoom-in"] { transform: scale(.6); }
[data-aos].aos-animate { opacity: 1; transform: translateZ(0); }
[data-aos="zoom-in"].aos-animate { transform: translateZ(0) scale(1); }

@media (prefers-reduced-motion: reduce) {
  [data-aos] { opacity: 1; transform: none; transition: none; }
}

/* Karta ma też własne przejścia hover (border-color/box-shadow) - trzeba je dopisać
   obok opacity/transform z reguły bazowej powyżej, inaczej podmiana transition-property
   dla [data-aos] nadpisałaby je i hover migałby zero-jedynkowo zamiast się rozjaśniać. */
.card[data-aos] {
  transition-property: transform, border-color, box-shadow, opacity;
}

.grid { display: grid; gap: 1.2rem; }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

/* ---------- karuzela materiałów (przewijanie z kropkami, gdy pozycji jest więcej niż mieści się w jednym widoku) ---------- */
.material-carousel { position: relative; }
.material-carousel__track {
  display: flex;
  gap: 1.2rem;
  overflow-x: auto;
  /*
   * BEZ TEGO: gdy overflow-x jest ustawione na "auto" bez jawnego overflow-y, przeglądarka
   * z automatu przelicza też overflow-y na "auto" (tak działa specyfikacja CSS - "visible"
   * w jednej osi zawsze zamienia się w "auto", gdy druga oś nie jest "visible"). Tor stawał
   * się więc PRZYPADKOWO przewijalny w pionie, mimo że nigdzie tego nie chcieliśmy.
   *
   * Zmierzone na produkcji: karty mają dokładnie wysokość toru (355px), ale scrollHeight
   * toru i tak wynosił 450px - różnicę dawały karty jeszcze nieodsłonięte animacją scrolla
   * (transform: translateY(100px) z [data-aos], zanim IntersectionObserver je odsłoni) -
   * transformowana pozycja elementu liczy się do obszaru przewijalnego, mimo że wizualnie
   * jest niewidoczna. Efekt końcowy: kółko myszy nad taką sekcją przewijało ją SAMĄ W SOBIE
   * w pionie zamiast przewinąć całą stronę - "sekcje miały własne scrollery w górę/dół".
   *
   * overflow-y: hidden usuwa to ryzyko u źródła, niezależnie od tego, co akurat nadmuchuje
   * wysokość przewijalną - tor ma przewijać się WYŁĄCZNIE w poziomie, nigdy w pionie.
   */
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* padding-top: miejsce na uniesienie karty przy hoverze (.card:hover ma
     transform: translateY(-4px)) - bez tego zapasu overflow-y: hidden nad tą linią
     ucinało kilka górnych pikseli uniesionej karty, bo box toru był dopasowany
     dokładnie do wysokości karty w spoczynku. */
  padding-top: 6px;
  padding-bottom: .3rem;
  scrollbar-width: none;
}
/* Gdy wszystko mieści się w jednym widoku (nic do przewijania), wyśrodkuj zamiast
   przyklejać do lewej krawędzi - inaczej jedna karta wygląda jak przypadkowo wsunięta w róg. */
.material-carousel__track--fits { justify-content: center; }
.material-carousel__track::-webkit-scrollbar { display: none; }
/* Na ekranach dotykowych łagodniejsze przyciąganie. Przy "x mandatory" poziomy przewijak
   potrafi na Androidzie przechwycić szybki gest w dół i zablokować przewijanie strony -
   "proximity" przyciąga tak samo po zakończeniu gestu, ale nie trzyma go tak kurczowo.
   scroll-snap-stop poniżej zostaje, bo to on pilnuje, żeby jeden gest nie przeskoczył
   od razu kilku kart. Na myszy zostaje "mandatory" - tam problemu nie ma. */
@media (pointer: coarse) {
  .material-carousel__track { scroll-snap-type: x proximity; }
}

.material-carousel__track > .card,
.material-carousel__track > .thumb-wrap {
  scroll-snap-align: start;
  /* "always" wymusza zatrzymanie na KAŻDEJ karcie nawet przy szybkim geście (flick) -
     bez tego szybkie przewinięcie dotykiem potrafiło przeskoczyć kilka kart naraz
     i wylądować dalej niż oczekiwano. */
  scroll-snap-stop: always;
  flex: 0 0 calc((100% - 2 * 1.2rem) / 3);
  min-width: 0;
}
@media (max-width: 860px) {
  .material-carousel__track > .card,
  .material-carousel__track > .thumb-wrap { flex-basis: calc((100% - 1.2rem) / 2); }
}
@media (max-width: 560px) {
  .material-carousel__track > .card,
  .material-carousel__track > .thumb-wrap { flex-basis: 100%; }
}

/* Galeria zrzutów programu w karcie pobieralni - mniejsza, stały aspect-ratio (poza .material-card). */
.download-gallery { margin-top: .8rem; }
.download-gallery .thumb-wrap { aspect-ratio: 4/3; border-radius: var(--radius-sm); overflow: hidden; background: rgba(255,255,255,.04); }
.download-gallery .thumb-wrap img { width: 100%; height: 100%; object-fit: cover; transition: transform .3s ease; }
.download-gallery .thumb-wrap:hover img { transform: scale(1.06); }
.download-gallery .material-carousel__track > .thumb-wrap { flex-basis: calc((100% - 1.2rem) / 2); }
/* Zrzuty ze smartfona (portret) - węższy, wyższy box zamiast wycinania przez 4:3, ustawiane
   przez JS po sprawdzeniu naturalnych wymiarów obrazka. */
.thumb-wrap--portrait { aspect-ratio: 9/16 !important; }
.download-gallery .thumb-wrap--portrait { flex-basis: calc((100% - 1.2rem) / 3) !important; }
.material-carousel__dots {
  display: flex;
  justify-content: center;
  gap: .5rem;
  margin-top: 1rem;
}
.material-carousel__dots button {
  width: .55rem;
  height: .55rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--panel-border-strong);
  cursor: pointer;
  transition: background .25s ease, transform .25s ease;
}
.material-carousel__dots button:hover { background: var(--muted); }
.material-carousel__dots button.active {
  background: linear-gradient(135deg, var(--fel), var(--arcane));
  transform: scale(1.3);
}
@media (prefers-reduced-motion: reduce) {
  .material-carousel__track { scroll-behavior: auto; }
}

/* Strzałki lewo/prawo - tylko na urządzeniach z myszą (dotyk ma swój gest przesunięcia). */
.material-carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  border: 1px solid var(--panel-border);
  background: rgba(10, 7, 18, .75);
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  opacity: 0;
  transition: opacity .2s ease, background .2s ease, border-color .2s ease;
}
.material-carousel:hover .material-carousel__arrow { opacity: 1; }
.material-carousel__arrow:hover { background: var(--arcane); border-color: var(--panel-border-strong); }
.material-carousel__arrow--prev { left: -.6rem; }
.material-carousel__arrow--next { right: -.6rem; }
@media (pointer: coarse) {
  .material-carousel__arrow { display: none; }
}

/* Przycisk "Pokaż wszystko" - przełącza karuzelę na pełną, pionową listę. */
.material-carousel__showall {
  display: block;
  margin: .8rem auto 0;
  padding: .4rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--panel-border);
  background: rgba(255, 255, 255, .04);
  color: var(--text);
  font-size: .85rem;
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
.material-carousel__showall:hover { background: rgba(255, 255, 255, .08); border-color: var(--panel-border-strong); }
/* "Pokaż wszystko": na PC ładna siatka (auto-fill, tyle kolumn ile się mieści),
   na wąskim ekranie naturalnie spada do jednej kolumny - bez osobnych media queries. */
.material-carousel--list .material-carousel__track {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  overflow: visible;
  scroll-snap-type: none;
}
.material-carousel--list .material-carousel__track > .card,
.material-carousel--list .material-carousel__track > .thumb-wrap {
  flex-basis: auto;
  width: 100%;
  min-width: 0;
}
.material-carousel--list .material-carousel__dots,
.material-carousel--list .material-carousel__arrow { display: none; }

.quick-link {
  display: flex;
  flex-direction: column;
  gap: .6rem;
  text-align: center;
  color: var(--text);
}
.quick-link i { font-size: 1.6rem; color: var(--fel); display: inline-block; align-self: center; transition: transform .25s cubic-bezier(.2,.8,.2,1), color .2s ease; }
.quick-link:hover i { transform: scale(1.2) rotate(-4deg); color: var(--arcane); }
.quick-link h3 { margin: 0; font-size: 1.05rem; }
.quick-link p { margin: 0; color: var(--muted); font-size: .9rem; }

/* ---------- O mnie ---------- */
.bio-card { max-width: 780px; margin: 0 auto; text-align: center; }
.bio-card p { margin: 0 0 1rem; }
.bio-card p:last-child { margin-bottom: 0; }

/* ---------- listy w tekstach site_content (znacznik "/@ " w panelu Teksty strony) ---------- */
.rich-list { list-style: none; margin: 1rem 0; padding: 0; text-align: left; }
/* .bio-card ma text-align:center na całym bloku - lista ma się wyśrodkować jako całość,
   nie rozciągać na 100% szerokości (inaczej tekst byłby wyśrodkowany, ale kropki od
   lewej krawędzi karty). */
.bio-card .rich-list { width: fit-content; margin-left: auto; margin-right: auto; }
.rich-list li { display: flex; align-items: flex-start; gap: .55rem; margin-bottom: .5rem; color: var(--text); }
.rich-list li i { color: var(--fel); margin-top: .2rem; }

/* ---------- sprzęt ---------- */
.equipment-group { margin-bottom: 2.4rem; }
.equipment-group h2 { font-size: 1.3rem; margin-bottom: 1rem; }
.equipment-card { display: flex; gap: 1rem; align-items: flex-start; }
.equipment-card .card-img { flex-shrink: 0; width: 90px; height: 90px; border-radius: var(--radius-sm); overflow: hidden; background: rgba(255,255,255,.04); }
.equipment-card .card-img img { width: 100%; height: 100%; object-fit: cover; }
.equipment-card .card-img--icon { display: flex; align-items: center; justify-content: center; font-size: 1.8rem; color: var(--fel); }
.equipment-card h3 { margin: 0 0 .5rem; font-size: 1rem; }
.equipment-card ul:not(.rich-list) { margin: 0; padding-left: 1.1rem; color: var(--muted); font-size: .92rem; }
.equipment-card .rich-list { font-size: .92rem; }

/* ---------- pobieralnia ---------- */
.download-toolbar { margin-bottom: 1.6rem; }
.download-toolbar input {
  width: 100%;
  padding: .7rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--panel-border);
  background: rgba(0,0,0,.25);
  color: var(--text);
  font-family: inherit;
}
.download-item { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }
.download-item .meta { flex: 1; min-width: 200px; }
.download-item h3 { margin: 0 0 .3rem; font-size: 1rem; }
.download-item h3 i { color: var(--fel); margin-right: .3rem; }
.download-item p { margin: 0; color: var(--muted); font-size: .88rem; }
.download-badges { display: flex; flex-wrap: wrap; gap: .4rem; margin: .5rem 0; }

.download-description { max-height: 4.6em; overflow: hidden; position: relative; transition: max-height .3s ease; }
.download-description.expanded { max-height: 60vh; overflow-y: auto; }
.download-description__inner p { margin: 0 0 .5rem; color: var(--muted); font-size: .88rem; }
.download-description__inner p:last-child { margin-bottom: 0; }
.download-description:not(.expanded)::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 1.6em;
  background: linear-gradient(0deg, var(--panel-bg), transparent);
}

.changelog-toggle {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin-top: .6rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--fel);
  font-size: .85rem;
  cursor: pointer;
  transition: color .2s ease;
}
.changelog-toggle:hover { color: var(--arcane); }
.changelog-toggle__chevron { transition: transform .25s ease; font-size: .75em; }
.changelog-toggle[aria-expanded="true"] .changelog-toggle__chevron { transform: rotate(180deg); }

.changelog {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .3s cubic-bezier(.2,.8,.2,1);
}
.changelog.open { grid-template-rows: 1fr; }
.changelog__inner { overflow: hidden; min-height: 0; }
.changelog ul { margin: .6rem 0 0; padding-left: 1.1rem; }
.changelog li { color: var(--muted); font-size: .85rem; margin-bottom: .3rem; }
.changelog li strong { color: var(--text); }
@media (prefers-reduced-motion: reduce) {
  .changelog { transition: none; }
}
.btn {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  border: 0;
  border-radius: var(--radius-sm);
  padding: .6rem 1.1rem;
  font-weight: 600;
  cursor: pointer;
  background: linear-gradient(135deg, var(--fel), var(--arcane));
  color: #06110b;
  font-size: .9rem;
  white-space: nowrap;
  box-shadow: 0 6px 20px rgba(57,255,143,0);
  transition: transform .2s cubic-bezier(.2,.8,.2,1), filter .25s ease, box-shadow .3s ease;
}
.btn:hover { color: #06110b; filter: brightness(1.08); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(57,255,143,.25); }
.btn:active { transform: translateY(0); }
/* Przycisk z długą etykietą w wąskiej karcie: "nowrap" nie pozwalał mu się zwęzić, więc
   w widoku siatki wychodził poza kartę. Tu wolno mu złamać tekst na dwie linie. */
.btn--wrap { white-space: normal; text-align: center; justify-content: center; }
/* Drugorzędna akcja obok głównego CTA (np. "Kopiuj link" obok "Pobierz") - bez pełnego
   gradientu, żeby nie konkurowała wizualnie z akcją główną. */
.btn--ghost {
  background: rgba(255, 255, 255, .04);
  color: var(--text);
  border: 1px solid var(--panel-border);
  box-shadow: none;
}
.btn--ghost:hover { color: var(--fel); filter: none; background: rgba(255, 255, 255, .08); box-shadow: none; border-color: var(--panel-border-strong); }
.card .btn { max-width: 100%; }
.pill { display: inline-block; padding: .15rem .55rem; border-radius: 999px; font-size: .74rem; background: rgba(139,92,246,.22); color: var(--text); }

/* ---------- projekty / materiały ---------- */
.category-block { margin-bottom: 2.6rem; }
.category-block h2 { font-size: 1.3rem; margin-bottom: 1rem; }
.project-card p { color: var(--muted); font-size: .9rem; margin: .4rem 0 .8rem; }
.project-card__icon { font-size: 1.5rem; color: var(--fel); margin-bottom: .6rem; display: inline-block; transition: transform .25s cubic-bezier(.2,.8,.2,1), color .2s ease; }
.project-card:hover .project-card__icon { transform: scale(1.1) rotate(-3deg); color: var(--arcane); }

.material-card .thumb-wrap { border-radius: var(--radius-sm); overflow: hidden; margin-bottom: .8rem; aspect-ratio: 16/9; background: rgba(255,255,255,.04); position: relative; }
.material-card .thumb-wrap img { width: 100%; height: 100%; object-fit: cover; transition: transform .35s ease; }
.material-card .glightbox { cursor: zoom-in; }
.material-card .thumb-wrap:hover img { transform: scale(1.06); }
/* Karta ma stałą wysokość (żeby karty w jednym rzędzie karuzeli się równały) - bez
   przycięcia długi tytuł (np. film z długim opisem + kilkoma wklejonymi bez spacji
   nazwami użytkowników) rósł w nieskończoność i nachodził na przycisk pod spodem.
   Ten sam wzorzec co .music-title (2 linie, pełny tekst w title="" na hover). */
.material-card h3 {
  margin: 0 0 .3rem;
  font-size: 1rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.material-card p { color: var(--muted); font-size: .88rem; margin: 0 0 .6rem; }

/* Nagłówek utworu: ikona po lewej, tytuł po prawej (do 2 linii, pełny tekst na hover). */
.music-header { display: flex; align-items: flex-start; gap: .7rem; margin-bottom: .3rem; }
.music-title {
  font-size: 1rem;
  font-weight: 600;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color .2s ease;
  /* Tytuł jest teraz przyciskiem (otwiera okienko z odnośnikiem) - zdejmujemy z niego
     domyślny wygląd przycisku, żeby dalej wyglądał jak zwykły nagłówek. */
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.music-header:hover .music-title { color: var(--fel); }
.audio-meta { font-size: .78rem; letter-spacing: .03em; }

/* ---------- osadzony odtwarzacz Soundcloud (ładowany po kliknięciu) ---------- */
.sc-embed { margin: .2rem 0 .8rem; }
.sc-embed__play {
  width: 100%;
  padding: .75rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--panel-border-strong);
  background: rgba(255, 255, 255, .04);
  color: var(--text);
  font-family: inherit;
  font-size: .88rem;
  font-weight: 600;
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease, color .2s ease;
}
.sc-embed__play:hover { background: rgba(57, 255, 143, .1); border-color: var(--fel); color: var(--fel); }
.sc-embed__play i { margin-right: .4rem; }
.sc-embed iframe { width: 100%; height: 120px; border: 0; border-radius: var(--radius-sm); display: block; }
.sc-embed--loading { min-height: 120px; }
.sc-embed__note {
  margin: 0;
  padding: .7rem .8rem;
  border-radius: var(--radius-sm);
  background: rgba(255, 170, 0, .08);
  border: 1px solid rgba(255, 170, 0, .3);
  color: var(--muted);
  font-size: .8rem;
}
.sc-embed__note i { color: #ffaa00; margin-right: .35rem; }

/* ---------- tytuł jako link do dedykowanej strony pozycji (plik.php/utwor.php) ----------
   Wcześniej <button> otwierający okienko "udostępnij" z linkiem-kotwicą (#plik-N) - teraz
   zwykły <a> na WŁASNY, kanoniczny adres tej pozycji (patrz plik.php/utwor.php), więc reset
   stylów przycisku (background/border/padding/margin/font) już niepotrzebny, ale zostaje -
   działa identycznie na <a>, a <a> i tak domyślnie nie ma tła/obwódki/wcięcia. */
.share-title {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: color .2s ease;
}
.share-title:hover { color: var(--fel); text-decoration: underline dotted; text-underline-offset: .25em; }

/* ---------- dedykowana strona pojedynczej pozycji (plik.php/utwor.php) ---------- */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  color: var(--muted);
  font-size: .9rem;
  transition: color .2s ease, transform .2s ease;
}
.back-link:hover { color: var(--fel); transform: translateX(-3px); }
.detail-card h1 { margin: 0 0 .8rem; font-size: 1.5rem; }
.detail-card h1 i { color: var(--fel); margin-right: .1em; }
.detail-card__body { margin-top: 1.2rem; color: var(--muted); }
.detail-card__body p { margin: 0 0 .8rem; }
.detail-card__body p:last-child { margin-bottom: 0; }
.detail-card__actions { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 1.6rem; }

/* Krótkie podświetlenie pozycji, do której prowadził przysłany odnośnik. */
.is-shared-target {
  animation: sharedPulse 2.4s ease;
}
@keyframes sharedPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(57, 255, 143, 0); }
  15%, 60% { box-shadow: 0 0 0 3px rgba(57, 255, 143, .55); }
}

/* ---------- okienko udostępniania ---------- */
.share-modal {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 1.2rem;
}
.share-modal.is-open { display: flex; }
.share-modal__backdrop { position: absolute; inset: 0; background: rgba(4, 2, 10, .72); backdrop-filter: blur(4px); }
.share-modal__box {
  position: relative;
  width: min(520px, 100%);
  padding: 1.6rem;
  border-radius: var(--radius);
  /* Bardziej kryjące niż zwykły --panel-bg: okienko leży nad treścią strony i tekst
     w nim musi być w pełni czytelny, a nie prześwitywać. */
  background: #17102a;
  border: 1px solid var(--panel-border-strong);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .55);
  animation: shareIn .25s cubic-bezier(.2, .8, .2, 1);
}
@keyframes shareIn { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: none; } }
.share-modal__x {
  position: absolute;
  top: .7rem;
  right: .7rem;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--panel-border);
  background: rgba(255, 255, 255, .04);
  color: var(--text);
  cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.share-modal__x:hover { background: var(--arcane); }
.share-modal__eyebrow { margin: 0 0 .2rem; font-size: .75rem; letter-spacing: .12em; text-transform: uppercase; color: var(--fel); }
.share-modal__title { margin: 0 0 1rem; font-size: 1.1rem; padding-right: 2.2rem; }
.share-modal__row { display: flex; gap: .6rem; flex-wrap: wrap; }
.share-modal__input {
  flex: 1 1 240px;
  min-width: 0;
  padding: .6rem .8rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--panel-border);
  background: rgba(0, 0, 0, .3);
  color: var(--text);
  font-family: inherit;
  font-size: .85rem;
}
.share-modal__hint { margin: .8rem 0 0; font-size: .78rem; color: var(--muted); }
.music-icon {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  color: var(--fel);
  background: linear-gradient(135deg, rgba(57,255,143,.16), rgba(139,92,246,.16));
  border: 1px solid var(--panel-border);
  transition: transform .25s cubic-bezier(.2,.8,.2,1);
}
.music-header:hover .music-icon { transform: scale(1.08); }

/* Podpis z nazwą materiału, płynnie wjeżdżający po najechaniu na miniaturkę. */
.thumb-caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 1.6rem .8rem .6rem;
  background: linear-gradient(0deg, rgba(10,7,18,.9) 10%, transparent 100%);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .25s ease, transform .25s ease;
  pointer-events: none;
}
.thumb-caption span { color: var(--text); font-weight: 600; font-size: .85rem; }
.thumb-wrap:hover .thumb-caption { opacity: 1; transform: translateY(0); }

/* generyczna plansza, gdy nie mamy miniaturki (np. CDA.pl bez publicznych metadanych) */
.thumb-placeholder {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, rgba(139,92,246,.18), rgba(57,255,143,.1));
  color: var(--muted);
  font-size: 2.2rem;
}

/* facade-embed filmów (YouTube/CDA.pl): miniaturka + przycisk play, prawdziwy iframe
   dopiero po kliknięciu. */
.video-embed { cursor: pointer; }
.video-embed iframe { width: 100%; height: 100%; }
.video-embed__play {
  position: absolute; inset: 0; margin: auto; width: 56px; height: 56px;
  border-radius: 50%; border: 0; cursor: pointer;
  background: rgba(10, 7, 18, .55);
  color: var(--fel);
  font-size: 1.3rem;
  display: flex; align-items: center; justify-content: center;
  transition: transform .2s ease, background .2s ease;
}
.video-embed:hover .video-embed__play { transform: scale(1.1); background: rgba(10, 7, 18, .75); color: var(--text); }

/* ---------- podpis w powiększeniu zdjęcia (GLightbox) ----------
   Biblioteka domyślnie rysuje pod obrazkiem biały prostokąt z czarnym tekstem, który
   w ciemnym motywie wygląda jak wyrwana kartka. Zamieniamy go na podpis NAŁOŻONY na dół
   zdjęcia, z gradientem wtapiającym się w obraz - tak samo jak podpisy na miniaturkach.

   Kontener slajdu dostaje "width: fit-content", żeby zwęzić się dokładnie do szerokości
   zdjęcia. Bez tego podpis (pozycjonowany absolutnie względem kontenera) rozciągał się na
   całą szerokość okna i wystawał daleko poza obrazek. */
.glightbox-container .ginner-container.desc-bottom {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
}
.glightbox-container .gslide-description.description-bottom {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: auto;
  /* Biblioteka wpisuje max-width prosto w atrybut style slajdu - stąd !important. */
  max-width: none !important;
  border: 0;
  border-radius: 0 0 var(--radius) var(--radius);
  background: linear-gradient(to top, rgba(10, 7, 18, .94), rgba(10, 7, 18, .6) 55%, rgba(10, 7, 18, 0));
}
.glightbox-container .gdesc-inner { padding: 2.2rem 1.2rem .9rem; }
.glightbox-container .gslide-title {
  margin: 0;
  font-family: 'Poppins', system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  text-shadow: 0 2px 10px rgba(0, 0, 0, .7);
}
.glightbox-container .gslide-desc {
  margin: .25rem 0 0;
  font-family: 'Poppins', system-ui, sans-serif;
  font-size: .85rem;
  color: var(--muted);
}
.glightbox-container .gslide-image img {
  border-radius: var(--radius);
  display: block;
}

/* Odtwarzacz Plyr dla lokalnych plików audio - dopasowanie kolorów do motywu */
.material-card .plyr { border-radius: var(--radius-sm); }
:root {
  --plyr-color-main: var(--fel);
  --plyr-audio-control-color: var(--text);
  --plyr-audio-control-background-hover: var(--arcane);
  --plyr-audio-controls-background: rgba(0,0,0,.25);
}
/* Pasek postępu utworu w wąskiej karcie nie mieści się obok play/czasu/głośności/pobierania -
   zjeżdża na własną linię pod nimi zamiast się ściskać w jednym rzędzie. */
.material-card .plyr__controls { flex-wrap: wrap; row-gap: .5rem; }
.material-card .plyr__progress__container { order: 10; flex: 1 0 100%; }

/* Plyr sam chowa czas trwania na wąskich ekranach własną regułą
   "@media (max-width:767px){.plyr__time+.plyr__time{display:none}}", przez co na telefonie
   widać tylko bieżącą pozycję ("02:17 /" bez końca), mimo że miejsce w karcie jest -
   pasek postępu i tak siedzi w osobnym rzędzie. Przywracamy pełny zapis czasu. */
@media (max-width: 767px) {
  .material-card .plyr__time + .plyr__time { display: block; }
}

/* Galeria screenów używa biblioteki GLightbox (własny CSS z CDN) zamiast ręcznego lightboxa. */
.glightbox { cursor: zoom-in; }

/* ---------- kontakt ---------- */
.contact-card { text-align: center; }
.contact-card i { font-size: 1.6rem; color: var(--fel); margin-bottom: .6rem; display: inline-block; transition: transform .25s cubic-bezier(.2,.8,.2,1), color .2s ease; }
.contact-card:hover i { transform: scale(1.2) rotate(-4deg); color: var(--arcane); }

/* ---------- footer ---------- */
.site-footer { border-top: 1px solid var(--panel-border); padding: 2.6rem 1.5rem 1.6rem; margin-top: 3rem; }
.site-footer__inner { max-width: 1100px; margin: 0 auto; }
.site-footer__inner p { margin: .2rem 0; font-size: .88rem; }
.site-footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 2rem;
  text-align: left;
  margin-bottom: 1.8rem;
}
.site-footer__col h4 {
  margin: 0 0 .7rem;
  font-size: .95rem;
  color: var(--text);
}
.brand--footer { margin-bottom: .5rem; display: inline-block; }
.site-footer__links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .4rem; }
.site-footer__links a {
  color: var(--muted);
  font-size: .88rem;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  transition: color .2s ease, transform .2s ease;
}
.site-footer__links a i { color: var(--fel); width: 1rem; text-align: center; transition: color .2s ease; }
.site-footer__links a:hover { color: var(--text); transform: translateX(3px); }
.site-footer__links a:hover i { color: var(--arcane); }
.site-footer__bottom {
  border-top: 1px solid var(--panel-border);
  padding-top: 1.2rem;
  text-align: center;
}

/* ---------- wsparcie ---------- */
.support-card { text-align: center; display: flex; flex-direction: column; align-items: center; gap: .4rem; }
.support-card > i { font-size: 1.8rem; color: var(--fel); transition: transform .25s cubic-bezier(.2,.8,.2,1), color .2s ease; }
.support-card:hover > i { transform: scale(1.2) rotate(-4deg); color: var(--arcane); }
.support-card h3 { margin: .2rem 0 0; font-size: 1.05rem; }
.support-card p { margin: 0; font-size: .88rem; }
.support-card__cta {
  margin-top: .7rem;
  font-size: .85rem;
  font-weight: 600;
  color: var(--fel);
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  transition: gap .2s ease;
}
.support-card:hover .support-card__cta { gap: .65rem; }
/* Pozycja oznaczona w panelu jako wyróżniona - mocniejsza obwódka i poświata. */
.support-card--highlight {
  border-color: var(--panel-border-strong);
  background: linear-gradient(135deg, rgba(57, 255, 143, .08), rgba(139, 92, 246, .12));
  box-shadow: 0 0 26px rgba(139, 92, 246, .18);
}

/* ---------- utility ---------- */
.empty-state { text-align: center; color: var(--muted); padding: 2rem; }

/* ---------- pływający przycisk "powrót do góry" ---------- */
.back-to-top {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 70;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--panel-border);
  background: rgba(10, 7, 18, .85);
  backdrop-filter: blur(10px);
  color: var(--text);
  font-size: 1.05rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity .25s ease, transform .25s ease, border-color .2s ease, color .2s ease;
}
.back-to-top.is-visible { opacity: 1; transform: translateY(0); pointer-events: auto; }
.back-to-top:hover { border-color: var(--panel-border-strong); color: var(--fel); }

/* ---------- pływający mini-player radia ---------- */
.radio-widget {
  position: fixed;
  right: 5.5rem;
  bottom: 1.5rem;
  z-index: 65;
  display: flex;
  align-items: center;
}
.radio-widget__toggle {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--panel-border);
  background: rgba(10, 7, 18, .85);
  backdrop-filter: blur(10px);
  color: var(--fel);
  font-size: 1.1rem;
  cursor: pointer;
  display: none;
  align-items: center;
  justify-content: center;
  transition: transform .2s ease, border-color .2s ease;
}
.radio-widget__toggle:hover { transform: scale(1.08); border-color: var(--panel-border-strong); }
.radio-widget__toggle.is-playing i { animation: radio-pulse 1.4s ease-in-out infinite; }
@keyframes radio-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

.radio-widget__panel {
  display: flex;
  align-items: center;
  gap: .8rem;
  background: rgba(15, 10, 24, .92);
  backdrop-filter: blur(12px);
  border: 1px solid var(--panel-border);
  border-radius: 999px;
  padding: .5rem 1.1rem;
}
.radio-widget__info { display: flex; align-items: center; gap: .5rem; font-size: .82rem; color: var(--text); white-space: nowrap; }
.radio-widget__info i { color: var(--fel); }
.radio-widget__controls { display: flex; align-items: center; gap: .6rem; }
.radio-widget__play {
  position: relative;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  background: linear-gradient(135deg, var(--fel), var(--arcane));
  color: #06110b;
  font-size: .95rem;
  transition: transform .2s ease;
}
.radio-widget__play:hover { transform: scale(1.08); }
.radio-widget__play:active { transform: scale(.92); }
/* Płynne przejście play <-> pauza (cross-fade + skala) zamiast twardej podmiany ikony. */
.radio-widget__play-icon {
  position: absolute;
  inset: 0;
  margin: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity .25s ease, transform .25s ease;
}
.radio-widget__play-icon--play { opacity: 1; transform: scale(1); }
.radio-widget__play-icon--pause { opacity: 0; transform: scale(.5); }
.radio-widget__play.is-playing .radio-widget__play-icon--play { opacity: 0; transform: scale(.5); }
.radio-widget__play.is-playing .radio-widget__play-icon--pause { opacity: 1; transform: scale(1); }
.radio-widget__volume { width: 90px; accent-color: var(--fel); cursor: pointer; }

@media (max-width: 900px) {
  .radio-widget { right: 1.5rem; bottom: 5.5rem; }
  .radio-widget__toggle { display: flex; }
  .radio-widget__panel {
    position: absolute;
    right: 0;
    bottom: 60px;
    opacity: 0;
    transform: translateY(10px) scale(.95);
    pointer-events: none;
    transition: opacity .25s ease, transform .25s ease;
  }
  .radio-widget.is-open .radio-widget__panel {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
  }
  .radio-widget__volume { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .card { transition: none; }
  .card:hover { transform: none; }
  .site-glow, .hero__content::after { animation: none; }
  .radio-widget__toggle.is-playing i { animation: none; }
}
