/* ==== Zamknięty zestaw komponentów — style wyłącznie dla app/Views/components/*.php ==== */

/* --- button --- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  padding:13px 24px;border-radius:999px;font-weight:600;font-size:var(--fs-sm);
  border:1px solid transparent;cursor:pointer;transition:transform .15s ease,background .15s ease;
}
.btn:hover{transform:translateY(-1px)}
.btn--primary{background:var(--ink);color:var(--white)}
.btn--primary:hover{background:var(--copper)}
.btn--secondary{background:var(--white);color:var(--ink);border-color:var(--line)}
.btn--secondary:hover{border-color:var(--copper);color:var(--copper)}
.btn--ghost{background:transparent;color:var(--ink);padding:8px 4px}
.btn--danger{background:var(--danger);color:var(--white)}
.btn--sm{padding:9px 16px;font-size:var(--fs-xs)}
.btn--block{width:100%}

/* --- card --- */
.card{
  display:block;
  background:var(--white);border:1px solid var(--line);border-radius:var(--radius-md);
  overflow:hidden;box-shadow:var(--shadow-sm);
}
.card--pad{padding:var(--sp-5)}

/* --- badge --- */
.badge{
  display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:999px;
  font-size:var(--fs-xs);font-weight:600;
}
.badge--copper{background:var(--copper-soft);color:var(--copper)}
.badge--sage{background:var(--sage-soft);color:var(--sage)}
.badge--danger{background:var(--danger-soft);color:var(--danger)}
.badge--line{background:var(--stone);color:var(--sub)}

/* --- lead: krótki, wyróżniony opis pod tytułem (kategoria, produkt) — dotąd istniał
   tylko jako .hero p.lead poniżej, więc gdziekolwiek indziej użyty renderował się jak
   zwykły akapit bez żadnego stylu */
.lead{font-size:var(--fs-md);color:var(--ink);line-height:1.65;max-width:60ch;margin:0 0 var(--sp-6)}
@media (min-width:1241px){
  .lead{font-size:16px}
}

/* --- hero --- */
.hero{padding:var(--sp-9) 0 var(--sp-5)}
.hero__eyebrow{color:var(--copper)}
.hero h1{font-size:var(--fs-2xl);line-height:1.08;margin-bottom:var(--sp-4)}
.hero p.lead{font-size:var(--fs-md);color:var(--sub);max-width:60ch;margin-bottom:var(--sp-6)}
.hero__image{border-radius:var(--radius-lg);overflow:hidden;margin-top:var(--sp-6);box-shadow:var(--shadow-md)}
.hero__image img{width:100%;height:380px;object-fit:cover;cursor:zoom-in;transition:transform .5s cubic-bezier(.2,.7,.2,1)}
.hero__image:hover img{transform:scale(1.05)}
.hero__actions{display:flex;gap:var(--sp-3);flex-wrap:wrap}

/* --- film realizacji (YouTube, autoplay bez sterowania — sama animacja w tle) --- */
.realization-video{width:100%}
.realization-video__frame{position:relative;width:100%;aspect-ratio:16/9;background:#000}
.realization-video__frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;pointer-events:none}
@media (min-width:1241px){
  .realization-video{max-width:1440px;margin:0 auto}
}

/* --- breadcrumbs --- */
.breadcrumbs{display:flex;gap:8px;align-items:center;font-size:var(--fs-xs);color:var(--sub);flex-wrap:wrap;margin-bottom:var(--sp-5)}
.breadcrumbs a:hover{color:var(--copper)}
.breadcrumbs span.sep{color:var(--faint)}

/* --- spec grid (2 kolumny, żeby dłuższe listy parametrów nie robiły się bardzo wysokie) --- */
.spec-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 var(--sp-7);border:1px solid var(--line);border-radius:var(--radius-md);padding:4px var(--sp-5)}
/* min-width:0 na wierszu (element siatki) i na obu spanach (elementy flex) — bez tego
   żaden nie chce się kurczyć poniżej naturalnej szerokości treści. Wcześniej wartość miała
   flex-shrink:0, co dobrze zawijało długą ETYKIETĘ — ale gdy to WARTOŚĆ była długa (np.
   pełna lista kolekcji, współczynnik z jednostkami), flex-shrink:0 nie dawał jej się
   skurczyć wcale, więc cały deficyt szerokości spadał na etykietę, która potrafiła się
   spłaszczyć do 0px i zawinąć w wieżowiec jednoliterowych linii. Obie strony muszą móc się
   kurczyć i zawijać, żeby długość TEKSTU (obojętnie po której stronie) nigdy nie psuła
   układu. */
.spec-grid__row{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--sp-4);padding:12px 0;border-top:1px solid var(--line);font-size:var(--fs-sm);min-width:0}
.spec-grid__row:nth-child(1),.spec-grid__row:nth-child(2){border-top:none}
.spec-grid__row span:first-child{color:var(--sub);min-width:0;flex:0 1 auto}
.spec-grid__row span:last-child{font-weight:600;text-align:right;margin-left:var(--sp-4);min-width:0;flex:0 1 auto}
@media (max-width:640px){
  .spec-grid{grid-template-columns:1fr}
  .spec-grid__row:nth-child(2){border-top:1px solid var(--line)}
  /* etykiety długie na całe zdanie (np. parametry szklenia) inaczej nie zawijały się —
     value miał flex-shrink:0 i justify-content:space-between trzymał oba w jednej linii,
     więc tekst wychodził poza kartę zamiast łamać się do kolejnego wiersza */
  .spec-grid__row{flex-direction:column;align-items:flex-start;gap:2px}
  .spec-grid__row span:last-child{text-align:left;margin-left:0;flex-shrink:1}
}

/* --- hero produktu: tytuł/opis obok zdjęcia, zamiast osobnego paska nad siatką --- */
.hero-split{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-7);align-items:center}
.hero-split__image{border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-md);height:400px}
.hero-split__image img{width:100%;height:100%;object-fit:cover;cursor:zoom-in;transition:transform .5s cubic-bezier(.2,.7,.2,1)}
.hero-split__image:hover img{transform:scale(1.05)}
/* zdjęcie bez linku do lightboksa (kategoria) — kursor zoom-in sugerowałby akcję po
   kliknięciu, której tu nie ma; produkt ma zdjęcie owinięte w <a>, więc go nie dotyczy */
.hero-split__image > img{cursor:default}

/* wariant: zdjęcie bez ramki/cienia — sama kategoria, produkt zachowuje oryginalną oprawę */
.hero-split--no-frame .hero-split__image{border-radius:0;box-shadow:none}

/* wariant: bez zdjęcia wcale (kategoria — hero_image zostaje tylko jako obrazek do udostępniania
   w social media, patrz ogImage w CategoryController) — tekst zajmuje całą szerokość zamiast
   zostawiać pustą drugą kolumnę siatki */
.hero-split--full{grid-template-columns:1fr}

@media (max-width:820px){
  .hero-split{grid-template-columns:1fr;gap:var(--sp-5)}
  .hero-split__image{height:280px}
}

/* --- gallery (zdjęcia realizacji: rozmyte, przyciemnione tło z samego zdjęcia) --- */
.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-3)}
.gallery a{position:relative;display:block;border-radius:var(--radius-sm);overflow:hidden;aspect-ratio:4/3;background-size:cover;background-position:center;cursor:zoom-in}
.gallery a::before{content:"";position:absolute;inset:0;background:inherit;background-size:cover;background-position:center;filter:blur(16px) brightness(.7);transform:scale(1.15)}
.gallery img{position:relative;z-index:1;width:100%;height:100%;object-fit:contain;transition:transform .2s ease}
.gallery a:hover img{transform:scale(1.04)}
@media (max-width:640px){.gallery{grid-template-columns:repeat(2,1fr)}}

/* --- gallery--flat: miniaturki dodatkowych zdjęć produktu (pod wyposażeniem), na
   jednolitym (często ciemnym) tle studyjnym, gdzie rozmyte tło z samego zdjęcia zlewałoby
   się z ciemnym obiektem (np. czarne drzwi) — białe tło + margines gwarantują widoczny
   kontrast dookoła. auto-fill zamiast sztywnej liczby kolumn, bo liczba dodatkowych zdjęć
   różni się mocno między produktami */
.gallery--flat{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
.gallery--flat a{background:var(--white);border:1px solid var(--line)}
.gallery--flat a::before{display:none}
.gallery--flat img{object-fit:cover}

/* --- gallery--natural: miniatury galerii realizacji BEZ przycinania i BEZ rozmytego tła
   pod spodem (to ono dokładało pozorne "poszerzenie" góra/dół pod zdjęciami w pionie —
   aspect-ratio:4/3 + object-fit:contain zostawiało blurred padding tam, gdzie zdjęcie nie
   wypełniało całego pudełka). Zdjęcie zawsze na pełną szerokość komórki, wysokość wynika z
   naturalnych proporcji — ta sama zasada co .realizacje-grid/.grid-3--realizacje. align-self
   na kotwicy, żeby przy nierównych wysokościach w tym samym rzędzie CSS Grid jej nie
   rozciągało (patrz identyczna poprawka przy .realizacje-more-item). */
.gallery--natural a{align-self:start;aspect-ratio:auto;background:none !important}
.gallery--natural a::before{display:none}
.gallery--natural img{position:static;width:100%;height:auto;object-fit:unset;display:block}

/* --- lightbox galerii zdjęć --- */
.image-lightbox{
  position:fixed;inset:0;z-index:1100;background:rgba(10,9,8,.92);display:none;
  align-items:center;justify-content:center;padding:24px 24px 90px;overflow:hidden;
  touch-action:none;
}
.image-lightbox.open{display:flex}
.image-lightbox.zoomed{align-items:flex-start;justify-content:flex-start;overflow:auto;-webkit-overflow-scrolling:touch;cursor:grab}
.image-lightbox.panning{cursor:grabbing}
body.lightbox-open{overflow:hidden}
.image-lightbox__figure{position:relative;max-width:calc(100vw - 48px);max-height:calc(100vh - 110px);display:flex;flex-direction:column;align-items:center;gap:var(--sp-3)}
.image-lightbox.zoomed .image-lightbox__figure{max-width:none;max-height:none;margin:auto}
.image-lightbox__img{
  max-width:100%;max-height:calc(100vh - 110px);width:auto;height:auto;border-radius:var(--radius-md);
  box-shadow:var(--shadow-md);object-fit:contain;-webkit-user-drag:none;user-select:none;
  background-size:cover;background-position:center;transition:filter .35s ease;
}
/* placeholder na czas ładowania pełnego zdjęcia (patrz showIndex w site.js) — tło ustawione
   inline jako już wczytana miniaturka z siatki galerii, tu tylko rozmyta */
.image-lightbox__img.is-loading{filter:blur(18px)}
/* przyciski zmiany zdjęcia i opis trzymamy razem, jako stały pasek u dołu ekranu —
   niezależnie od wysokości zdjęcia, więc nawigacja jest zawsze w tym samym, przewidywalnym miejscu */
.image-lightbox__caption{position:fixed;z-index:10;bottom:46px;left:50%;transform:translateX(-50%);max-width:calc(100vw - 160px);color:var(--white);font-size:var(--fs-sm);font-weight:600;text-align:center}
.image-lightbox__caption:empty{display:none}
.image-lightbox__counter{position:fixed;z-index:10;bottom:24px;left:50%;transform:translateX(-50%);color:rgba(255,255,255,.75);font-size:var(--fs-xs);letter-spacing:.04em}
.image-lightbox__close,.image-lightbox__prev,.image-lightbox__next,.image-lightbox__zoom button{
  position:fixed;z-index:10;display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:50%;background:rgba(255,255,255,.12);border:none;
  color:var(--white);cursor:pointer;font-size:20px;transition:background .15s ease;
}
.image-lightbox__close:hover,.image-lightbox__prev:hover,.image-lightbox__next:hover,.image-lightbox__zoom button:hover{background:rgba(255,255,255,.24)}
.image-lightbox__zoom button:disabled{opacity:.35;cursor:default}
.image-lightbox__zoom button:disabled:hover{background:rgba(255,255,255,.12)}
/* przy powiększeniu zdjęcie może przewinąć się pod przyciski — wtedy tło musi być kryjące, żeby ikony były czytelne */
.image-lightbox.zoomed .image-lightbox__close,
.image-lightbox.zoomed .image-lightbox__prev,
.image-lightbox.zoomed .image-lightbox__next,
.image-lightbox.zoomed .image-lightbox__zoom button{background:var(--ink)}
.image-lightbox.zoomed .image-lightbox__close:hover,
.image-lightbox.zoomed .image-lightbox__prev:hover,
.image-lightbox.zoomed .image-lightbox__next:hover,
.image-lightbox.zoomed .image-lightbox__zoom button:hover{background:var(--copper)}
.image-lightbox.zoomed .image-lightbox__zoom button:disabled,
.image-lightbox.zoomed .image-lightbox__zoom button:disabled:hover{background:var(--ink);opacity:.35}
.image-lightbox__close{top:20px;right:20px}
.image-lightbox__prev,.image-lightbox__next{top:auto;bottom:20px}
.image-lightbox__prev{left:20px}
.image-lightbox__next{right:20px}
.image-lightbox__zoom{position:fixed;top:20px;left:20px;z-index:10;display:flex;gap:8px}
.image-lightbox__zoom button{position:static;font-size:22px;font-weight:600}
@media (max-width:640px){
  .image-lightbox{padding:56px 12px 76px}
  .image-lightbox__figure{max-width:100%;max-height:calc(100vh - 132px)}
  .image-lightbox__img{max-height:calc(100vh - 132px)}
  .image-lightbox__close{top:10px;right:10px}
  .image-lightbox__zoom{top:10px;left:10px;gap:6px}
  .image-lightbox__zoom button{width:36px;height:36px;font-size:18px}
  .image-lightbox__prev,.image-lightbox__next{bottom:16px;width:40px;height:40px}
  .image-lightbox__prev{left:12px}
  .image-lightbox__next{right:12px}
  .image-lightbox__counter{bottom:26px}
  .image-lightbox__caption{bottom:46px;max-width:calc(100vw - 24px)}
}

/* --- modal artykułu (linki "czytaj cały artykuł" w krokach kreatora wycen — otwierają
   pełną treść bez opuszczania kreatora, żeby nie tracić postępu wypełniania) ---
   Szerokość okna celowo podąża za szerokością treści strony (--container, ta sama zmienna,
   której używa .container) zamiast sztywnej wartości: na ekranie mniej więcej tej szerokości
   okno jest odrobinę węższe niż viewport (margines z paddingu .article-modal), a na bardzo
   szerokich ekranach nie rośnie ponad szerokość, jaką i tak ma treść strony. */
.article-modal{
  position:fixed;inset:0;z-index:1000;display:none;align-items:flex-start;justify-content:center;
  padding:40px 24px;background:rgba(20,18,16,.72);overflow-y:auto;
}
.article-modal.open{display:flex}
.article-modal__dialog{
  position:relative;background:var(--white);border-radius:var(--radius-lg);
  max-width:var(--container);width:100%;box-shadow:var(--shadow-md);overflow:hidden;
}
.article-modal__close{
  position:absolute;top:14px;right:14px;z-index:2;width:36px;height:36px;border-radius:999px;
  border:none;background:rgba(0,0,0,.55);color:var(--white);font-size:18px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
.article-modal__close:hover{background:rgba(0,0,0,.75)}
.article-modal__body{max-height:calc(100vh - 80px);overflow-y:auto}
.article-modal__loading{padding:60px 24px;text-align:center;color:var(--sub)}
@media (max-width:640px){
  /* nawet na mobile okno musi czytelnie odcinać się od tła (widoczny margines, zaokrąglenie,
     cień) — inaczej wygląda dokładnie jak zwykła strona i nie wiadomo, że to nakładka */
  .article-modal{padding:14px}
  .article-modal__dialog{max-width:100%;max-height:calc(100vh - 28px);border-radius:var(--radius-md)}
  .article-modal__body{max-height:calc(100vh - 28px)}
}

/* --- kreator wycen: upload plików (krok typu file_upload) — przyciski aparatu/wyboru pliku
   i miniaturki załączników z podglądem, nazwą, rozmiarem i usuwaniem przed przejściem dalej --- */
.file-upload-field__buttons{display:flex;gap:10px;flex-wrap:wrap}
.file-upload-field__btn{position:relative;cursor:pointer}
/* domyślnie ukryty — site.js pokazuje go tylko po potwierdzeniu (navigator.mediaDevices),
   że urządzenie faktycznie ma aparat; bez tego przycisk "Zrób zdjęcie" mylił na komputerach
   bez kamery (i tak działał jak zwykły wybór pliku, ale sama etykieta wprowadzała w błąd) */
.file-upload-field__btn--camera{display:none}
.file-upload-field__btn--camera.has-camera{display:inline-flex}
.file-upload-field__input{position:absolute;width:1px;height:1px;padding:0;margin:0;border:0;opacity:0;overflow:hidden;pointer-events:none}
.file-upload-field__list{display:flex;flex-wrap:wrap;gap:12px;margin-top:16px}
.file-thumb{
  position:relative;width:104px;display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:8px;border:1px solid var(--line);border-radius:var(--radius-md);background:var(--white);transition:opacity .2s;
}
.file-thumb.is-uploading{opacity:.55}
.file-thumb__preview{
  width:72px;height:72px;border-radius:var(--radius-sm);overflow:hidden;display:flex;
  align-items:center;justify-content:center;background:var(--stone);color:var(--sub);
}
.file-thumb__preview img{width:100%;height:100%;object-fit:cover}
.file-thumb__meta{margin-top:6px;width:100%}
.file-thumb__name{
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  font-size:11px;font-weight:600;color:var(--ink);word-break:break-word;
}
.file-thumb__size{display:block;font-size:10px;color:var(--sub);margin-top:2px}
.file-thumb__remove{
  position:absolute;top:-8px;right:-8px;width:22px;height:22px;border-radius:999px;border:none;
  background:var(--ink);color:var(--white);font-size:12px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;box-shadow:var(--shadow-sm);
}
.file-thumb__remove:hover{background:var(--copper)}

/* --- feature list ("Dlaczego my") --- */
.feature-list{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--sp-5)}
.feature-item{display:flex;gap:var(--sp-3);align-items:flex-start}
.feature-item__icon{
  flex:none;width:38px;height:38px;border-radius:50%;background:var(--sage-soft);color:var(--sage);
  display:flex;align-items:center;justify-content:center;font-weight:700;
}
.feature-item h4{font-size:var(--fs-base);margin-bottom:4px}
.feature-item p{color:var(--sub);font-size:var(--fs-base);line-height:1.55;white-space:pre-line}
@media (max-width:640px){.feature-list{grid-template-columns:1fr}}
@media (min-width:641px) and (max-width:900px){.feature-list > :last-child:nth-child(odd){grid-column:1 / -1}}

/* --- steps ("Jak przebiega montaż") --- */
.steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--sp-5)}
.step{position:relative;padding-top:var(--sp-3)}
.step__num{
  display:inline-flex;width:34px;height:34px;border-radius:50%;background:var(--ink);color:var(--white);
  align-items:center;justify-content:center;font-weight:700;font-size:var(--fs-sm);margin-bottom:var(--sp-3);
}
.step h4{font-size:var(--fs-base);margin-bottom:6px}
.step p{color:var(--sub);font-size:var(--fs-sm)}

/* --- cta banner --- */
.cta-banner{
  background:var(--ink);color:var(--white);border-radius:var(--radius-lg);
  padding:var(--sp-7);display:flex;justify-content:space-between;align-items:center;gap:var(--sp-5);flex-wrap:wrap;
}
.cta-banner h3{font-size:var(--fs-xl);margin-bottom:var(--sp-2)}
.cta-banner p{color:rgba(255,255,255,.72)}
.cta-banner .btn--primary{background:var(--copper)}
.cta-banner .btn--primary:hover{background:var(--white);color:var(--ink)}

/* --- testimonial card --- */
.testimonial{padding:var(--sp-6);border:1px solid var(--line)}
.testimonial__stars{color:var(--copper);letter-spacing:3px;margin-bottom:var(--sp-4);font-size:15px}
.testimonial__content{color:var(--ink);margin-bottom:var(--sp-5);font-size:16.5px;line-height:1.6;font-weight:500}
.testimonial__author-row{display:flex;align-items:center;gap:12px}
.testimonial__avatar{
  flex:none;width:38px;height:38px;border-radius:50%;background:var(--sage-soft);color:var(--sage);
  display:flex;align-items:center;justify-content:center;font-weight:800;font-size:13px;
}
.testimonial__author{font-weight:700;font-size:13.5px;display:block}
.testimonial__source{color:var(--sub);font-size:11.5px}
.testimonial__more{
  display:inline-block;background:none;border:none;padding:0;margin:-8px 0 var(--sp-5);
  font-size:13px;font-weight:600;color:var(--copper);cursor:pointer;
  text-decoration:underline;text-underline-offset:2px;font-family:inherit;
}
.testimonial__more:hover{color:var(--ink)}
.testimonial-wrap.is-hidden{display:none}
/* karta opinii "Zaufali nam" (kategoria/produkt) — bez osobnego wrappera: karta sama jest
   elementem siatki, więc rozciąga się razem z tłem/ramką (patrz .card{display:block}),
   bez ryzyka pustego miejsca pod spodem znanego z .testimonial-wrap powyżej */
.card.testimonial.is-hidden{display:none}
/* "Zaufali nam" na stronie głównej: karty mają różną wysokość (różna długość treści
   opinii) — wrapper dodany dla "Zobacz więcej" domyślnie rozciągnąłby się na wysokość
   wyższego sąsiada w wierszu (patrz identyczna poprawka i wyjaśnienie przy .realizacje-
   more-item), stąd align-self:start od razu tutaj. */
.grid-2--testimonials-home .testimonials-more-item{align-self:start}
.grid-2--testimonials-home .testimonials-more-item.is-hidden{display:none}
/* powiększenie tylko na prawdziwym tablecie w pionie (szerokość+proporcje), telefon
   zostaje przy dawnym, stałym rozmiarze zdefiniowanym wyżej */
@media (min-width:680px) and (max-width:900px){
  .testimonial__content{font-size:clamp(16.5px, 14.3px + 0.58vw, 19.5px)}
  .testimonial__author{font-size:clamp(13.5px, 12.4px + 0.29vw, 15px)}
  .testimonial__source{font-size:clamp(11.5px, 10.4px + 0.29vw, 13px)}
}
.article-teaser.is-hidden{display:none}

/* --- rating summary (strona /opinie) --- */
.rating-summary{display:flex;align-items:center;gap:var(--sp-6);flex-wrap:wrap;padding:var(--sp-6);border:1px solid var(--line);border-radius:var(--radius-md);background:var(--copper-soft)}
.rating-summary__score{font-size:44px;font-weight:800;color:var(--ink);line-height:1;letter-spacing:-.02em}
.rating-summary__stars{color:var(--copper);letter-spacing:3px;font-size:20px;margin-bottom:4px}
.rating-summary__meta{color:var(--sub);font-size:var(--fs-sm)}
.rating-summary .btn{margin-left:auto}
@media (max-width:640px){.rating-summary .btn{margin-left:0}}

/* --- realization card --- */
.realization-card__image{position:relative;aspect-ratio:4/3;overflow:hidden;background-size:cover;background-position:center}
.realization-card__image::before{content:"";position:absolute;inset:0;background:inherit;background-size:cover;background-position:center;filter:blur(16px) brightness(.7);transform:scale(1.15)}
.realization-card__image img{position:relative;z-index:1;width:100%;height:100%;object-fit:contain;transition:transform .3s ease}
.realization-card:hover .realization-card__image img{transform:scale(1.03)}
.realization-card__body{padding:var(--sp-4)}
.realization-card__tag{color:var(--copper);font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;letter-spacing:.06em}
.realization-card h4{font-size:var(--fs-base);margin:6px 0}
.realization-card p{color:var(--sub);font-size:var(--fs-sm)}
.realization-card__link{display:inline-flex;align-items:center;gap:6px;font-weight:600;font-size:13.5px;color:var(--sub)}
.realization-card__link .arrow{transition:transform .2s ease}
.realization-card:hover .realization-card__link .arrow{transform:translateX(3px)}
.realization-card__location{display:flex;align-items:center;gap:4px;color:var(--sub);font-size:var(--fs-xs);margin:8px 0 0}
.realization-card__location svg{width:12px;height:12px;flex:none}
/* lokalizacja na stronie szczegółów realizacji, pod "Zastosowane produkty" — większa wersja
   tej samej ikonki pinezki co na kartach realizacji (patrz .realization-card__location) */
.realization-detail__location{display:flex;align-items:center;gap:6px;color:var(--sub);font-size:var(--fs-sm);margin:0 0 24px}
.realization-detail__location svg{width:16px;height:16px;flex:none;color:var(--copper)}
/* "Zobacz więcej" na portfolio kategorii/produktu — karta jest sama elementem siatki
   (bez wrappera), więc chowanie jej bezpośrednio nie zostawia pustego miejsca w rzędzie */
.realization-card.is-hidden{display:none}

/* --- /nasze-realizacje: mniej regularny układ tylko na desktopie — baza to 2 symetryczne
   kolumny, każda karta zajmuje 1 (pół szerokości) albo 2 (pełna szerokość) kolumny.
   Zdjęcia mają na sobie logo z boku, więc nie mogą być kadrowane ani dopasowywane do
   sztywnych proporcji pudełka — zamiast tego zdjęcie zawsze wypełnia pełną szerokość
   swojej komórki, a wysokość komórki wynika z naturalnych proporcji tego konkretnego
   zdjęcia (prawdziwy masonry: brak pustych pasków, pudełka celowo nie są tej samej
   wysokości). Na węższych ekranach zostaje prosty, jednokolumnowy stos. */
.realizacje-grid{display:grid;grid-template-columns:1fr;gap:var(--sp-5)}
/* niewidoczny znacznik doładowywania przy przewijaniu (patrz initLoadMore w site.js) —
   sam w sobie nie zajmuje miejsca w layoucie */
.load-more-sentinel{height:1px}
/* Zdjęcia bez przycinania na KAŻDEJ szerokości (nie tylko od tabletu w górę) — poniżej te
   reguły były tylko w media queries od 680px, więc na telefonie zdjęcia w pionie (wyższe
   niż szersze) wracały do bazowego aspect-ratio:4/3 + object-fit:contain, co dokładało
   białe pole nad i pod zdjęciem zamiast wypełnić całą szerokość karty. */
.realizacje-grid .realization-card__image{aspect-ratio:auto;background:none !important}
.realizacje-grid .realization-card__image::before{display:none}
.realizacje-grid .realization-card__image img{position:static;width:100%;height:auto;object-fit:unset;display:block}
/* Tablet w pionie (680–899px, np. iPad, Galaxy Tab) — ta sama "czasem 2, czasem 1 w rzędzie"
   mozaika co na desktopie, żeby duży tablet nie spadał do wymuszonego, jednokolumnowego
   stosu jak telefon. Osobna definicja od progu desktopowego (900px+) niżej — na węższym
   tablecie karty w parze są ciaśniej, więc dostają mniejszy odstęp. */
@media (min-width:680px) and (max-width:899px){
  .realizacje-grid{grid-template-columns:repeat(2,1fr);grid-auto-flow:dense;gap:var(--sp-4)}
  .realizacje-grid > :nth-child(5n+1):not(:first-child){grid-column:1 / -1}
}
@media (min-width:900px){
  .realizacje-grid{grid-template-columns:repeat(2,1fr);grid-auto-flow:dense}
  /* siatka zaczyna się od zwykłej pary zdjęć (nie od jednego dużego) — pełna szerokość
     pojawia się dopiero dalej, żeby urozmaicić układ bez dominowania na starcie */
  .realizacje-grid > :nth-child(5n+1):not(:first-child){grid-column:1 / -1}
}

/* --- sekcja "Nasze realizacje" na stronie głównej: to samo co wyżej (bez pasków tła,
   zdjęcie na pełną szerokość we własnych proporcjach, karty celowo różnej wysokości) —
   inaczej niż /nasze-realizacje karty zostają w prostej siatce 3 kolumn, bez masonry.
   Ramka/zaokrąglenie/tło/cień — ten sam, jeden spójny zestaw co w kartach "Wybierz swój
   wariant" (.compare2-card), cały na zewnętrznym kontenerze karty, bez osobnych
   zaokrągleń/teł na zdjęciu czy treści (jedno źródło prawdy, bez ryzyka że wewnętrzne
   i zewnętrzne zaokrąglenie się rozjadą).

   PRAWDZIWA przyczyna "znikającej ramki": .realizacje-more-item (dodany dla „Zobacz
   więcej") jest bezpośrednim dzieckiem siatki CSS Grid, więc domyślnie ROZCIĄGA SIĘ
   na wysokość najwyższej karty w danym wierszu (align-items:stretch to domyślne
   zachowanie elementów siatki) — a sama karta w środku (zwykły <a>) nie rozciąga się
   razem z nim. Przy kartach o celowo różnej wysokości w tym samym wierszu zostawiało
   to pod krótszą kartą puste, niestylowane miejsce (kolor tła sekcji, bez ramki/cienia)
   wewnątrz rozciągniętego wrappera — dokładnie to wyglądało jak "zniknięta ramka".
   align-self:start na wrapperze to wyłącza — każda karta zajmuje tylko tyle miejsca,
   ile faktycznie potrzebuje. */
.grid-3--realizacje .realizacje-more-item{align-self:start}
.grid-3--realizacje .realization-card{
  border-radius:var(--radius-md);overflow:hidden;box-shadow:0 6px 20px rgba(29,29,31,.12);
  background:var(--white);border:1px solid var(--line);
}
.grid-3--realizacje .realization-card__image{position:relative;aspect-ratio:auto;background:none !important}
.grid-3--realizacje .realization-card__image::before{display:none}
.grid-3--realizacje .realization-card__image img{position:static;width:100%;height:auto;object-fit:unset;display:block}
.grid-3--realizacje .realization-card__body{padding:28px}
.grid-3--realizacje .realizacje-more-item.is-hidden{display:none}

/* --- listy wyposażenia (standard / opcje dodatkowe) --- */
.check-list,.option-list{
  display:grid;grid-template-columns:1fr 1fr;gap:11px 24px;list-style:none;padding:0;margin:0;
}
.check-list li,.option-list li{position:relative;padding-left:26px;font-size:var(--fs-sm);color:var(--sub);line-height:1.45}
.check-list li::before{content:"✓";position:absolute;left:0;top:0;color:var(--sage);font-weight:700}
.option-list li::before{content:"+";position:absolute;left:0;top:0;color:var(--copper);font-weight:700}
@media (max-width:640px){.check-list,.option-list{grid-template-columns:1fr}}
@media (min-width:641px) and (max-width:900px){
  .check-list > li:last-child:nth-child(odd),.option-list > li:last-child:nth-child(odd){grid-column:1 / -1}
}

/* --- karta zapowiedzi artykułu (miniatura + tekst) --- */
.article-teaser-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-5)}
@media (max-width:900px){.article-teaser-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:640px){.article-teaser-grid{grid-template-columns:1fr}}
/* :not(:first-child) — inaczej przy DOKŁADNIE jednym artykule (pierwszy = ostatni = nieparzysty)
   ta reguła chowałaby jedyną kartę, zostawiając samą pustą siatkę pod nagłówkiem sekcji */
@media (min-width:641px) and (max-width:900px){.article-teaser-grid > :last-child:nth-child(odd):not(:first-child){display:none}}
/* Gdy artykułów jest mniej niż kolumn siatki (np. tylko 1-2 powiązane z danym produktem),
   repeat(N,1fr) rozciągał pojedynczą kartę na całą szerokość albo zostawiał puste, wyglądające
   na "niedziałające" komórki obok — karta/y dostają naturalną, ograniczoną szerokość zamiast. */
.article-teaser-grid:has(> :nth-child(1):last-child){grid-template-columns:minmax(0,380px)}
@media (min-width:901px){
  .article-teaser-grid:has(> :nth-child(2):last-child){grid-template-columns:repeat(2,minmax(0,380px))}
}
.article-teaser{background:var(--stone);border-radius:var(--radius-md);overflow:hidden}
.article-teaser__image{aspect-ratio:16/9;overflow:hidden;display:block}
.article-teaser__image img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .4s ease}
.article-teaser__image:hover img{transform:scale(1.04)}
.article-teaser__body h4 a:hover{color:var(--copper)}
.article-teaser__body{padding:var(--sp-5)}
/* h4/p tej karty mają rozmiar czcionki jako inline style var(--fs-base)/var(--fs-sm) —
   te tokeny są teraz same w sobie płynne (clamp, patrz tokens.css), więc skalują się tu
   automatycznie bez osobnej reguły ani !important. */

/* --- media + text (artykuł ze zdjęciem) --- */
.media-text{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-7);align-items:center}
.media-text--reverse{direction:rtl}
.media-text--reverse > *{direction:ltr}
.media-text__image{border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-md);aspect-ratio:4/3}
.media-text__image img{width:100%;height:100%;object-fit:cover;display:block;cursor:zoom-in;transition:transform .5s cubic-bezier(.2,.7,.2,1)}
.media-text__image:hover img{transform:scale(1.05)}
@media (max-width:820px){.media-text{grid-template-columns:1fr;gap:var(--sp-5)}}

/* --- tabela wartości (bloki artykułu typu "table", np. klasy wg normy) --- */
.data-table-public__scroll{border:1px solid var(--line);border-radius:var(--radius-md);overflow:hidden}
.data-table-public{width:100%;border-collapse:collapse}
.data-table-public th,.data-table-public td{padding:14px 18px;text-align:left;border-bottom:1px solid var(--line)}
.data-table-public thead th{background:var(--stone);font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.05em;color:var(--sub);font-weight:600}
.data-table-public tbody tr:last-child td{border-bottom:none}
.data-table-public tbody tr:nth-child(even){background:rgba(0,0,0,.015)}
/* poniżej 640px każdy wiersz staje się osobną kartą z etykietami nad wartością —
   przy dłuższym tekście w kolumnie "co oznacza" czytelniejsze niż przewijanie w bok */
@media (max-width:640px){
  .data-table-public__scroll{border:none}
  .data-table-public thead{display:none}
  .data-table-public,.data-table-public tbody,.data-table-public tr,.data-table-public td{display:block;width:100%}
  .data-table-public tr{margin-bottom:var(--sp-4);border:1px solid var(--line);border-radius:var(--radius-md);overflow:hidden}
  .data-table-public tbody tr:nth-child(even){background:none}
  .data-table-public td{border-bottom:1px solid var(--line);color:#000}
  .data-table-public td:last-child{border-bottom:none}
  .data-table-public td::before{content:attr(data-label);display:block;font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.05em;color:var(--sub);margin-bottom:4px}
}

/* --- formatowanie treści bloku "tekst" (patrz rich_text() w app/Core/helpers.php) --- */
.text-body blockquote{
  margin:var(--sp-4) 0;padding:2px 0 2px var(--sp-4);border-left:3px solid var(--copper);
  color:var(--sub);font-style:italic;
}
.text-body .text-box{
  display:inline-block;max-width:100%;
  background:var(--stone);border-radius:var(--radius-md);padding:var(--sp-4) var(--sp-5);
  margin:var(--sp-4) 0;
}
.text-body .text-copper{color:var(--copper);font-weight:600}
.text-body .text-sage{color:var(--sage);font-weight:600}
.text-body .text-danger{color:var(--danger);font-weight:600}
.text-body .text-align-left{display:block;text-align:left}
.text-body .text-align-center{display:block;text-align:center}
.text-body .text-align-right{display:block;text-align:right}
.text-body .text-align-justify{display:block;text-align:justify}

/* wzory: [formula]...[/formula] jako ramka na całe równanie, [frac]licznik|mianownik[/frac]
   jako prawdziwy dwuwierszowy ułamek w środku (zamiast rysowania kreski myślnikami) */
.text-body .text-formula{
  display:flex;flex-wrap:nowrap;white-space:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;
  justify-content:center;align-items:center;gap:6px;
  background:var(--stone);border-radius:var(--radius-md);padding:var(--sp-4) var(--sp-5);
  margin:var(--sp-4) 0;font-weight:600;font-size:var(--fs-md);text-align:center;
}
/* na wąskim ekranie wzór szerszy niż ekran ma przewijać się od początku, nie od środka
   (justify-content:center przy przepełnieniu przycina po równo z obu stron) */
@media (max-width:640px){
  .text-body .text-formula{justify-content:flex-start}
}
.text-body .frac{display:inline-flex;flex-direction:column;flex-shrink:0;vertical-align:middle;text-align:center;margin:0 4px;line-height:1.3}
.text-body .frac-num{border-bottom:1.5px solid currentColor;padding:0 6px 3px}
.text-body .frac-den{padding:3px 6px 0}

/* --- alert --- */
.alert{border-radius:var(--radius-sm);padding:var(--sp-4);font-size:var(--fs-sm);margin-bottom:var(--sp-5)}
.alert--success{background:var(--sage-soft);color:var(--sage)}
.alert--error{background:var(--danger-soft);color:var(--danger)}
.alert--info{background:var(--copper-soft);color:var(--copper)}

/* --- komunikat na stronie kontakt (ograniczony do zakresu dat, patrz ContactNotice) — musi
   wyróżniać się na tle zwykłych .card kafelków obok, stąd wyraźny cieplejszy akcent zamiast
   neutralnego tła */
.contact-notice{
  display:flex;gap:18px;align-items:flex-start;
  background:var(--copper-soft);border:1px solid var(--copper);border-left:5px solid var(--copper);
  border-radius:var(--radius-md);padding:24px 28px;margin-bottom:var(--sp-6);
}
.contact-notice__icon{
  flex:none;width:42px;height:42px;border-radius:50%;background:var(--copper);color:var(--white);
  display:flex;align-items:center;justify-content:center;
}
.contact-notice__icon svg{width:24px;height:24px}
.contact-notice__body h3{font-size:var(--fs-lg);margin-bottom:6px;color:var(--ink)}
.contact-notice__body p{color:var(--ink);font-size:var(--fs-sm);line-height:1.65;margin:0}
.contact-notice__phone{display:inline-block;margin-top:12px;font-weight:700;font-size:var(--fs-sm);color:var(--copper);text-decoration:underline;text-underline-offset:2px}
.contact-notice__phone:hover{color:var(--ink)}
.contact-notice + .contact-notice{margin-top:-4px}
@media (max-width:560px){
  .contact-notice{padding:18px 20px;gap:14px}
  .contact-notice__icon{width:36px;height:36px}
  .contact-notice__icon svg{width:20px;height:20px}
}

/* --- form field --- */
.field{margin-bottom:var(--sp-4)}
.field label{display:block;font-size:var(--fs-sm);font-weight:600;margin-bottom:6px}
.field .hint{font-size:var(--fs-xs);color:var(--sub);margin-top:4px}
.field .error{font-size:var(--fs-xs);color:var(--danger);margin-top:4px}
.field input[type=text],
.field input[type=email],
.field input[type=tel],
.field input[type=password],
.field input[type=number],
.field input[type=date],
.field input[type=file],
.field select,
.field textarea{
  width:100%;padding:12px 14px;border:1px solid var(--line);border-radius:var(--radius-sm);
  font-family:inherit;font-size:var(--fs-sm);color:var(--ink);background:var(--white);
}
.field textarea{min-height:120px;resize:vertical}
.field textarea[data-autogrow]{min-height:44px;overflow-y:hidden;resize:none}
.field input:focus,.field select:focus,.field textarea:focus{outline:2px solid var(--copper);outline-offset:1px}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-4)}

/* --- pole wyróżnione (np. logowanie panelu klienta) ---
   selektory muszą być specyficzniejsze niż ".field input[type=text]" powyżej (0,2,1),
   inaczej reguła bazowa wygrywa mimo że jest wcześniej w pliku */
.login-field.field label,.login-field .field label{font-size:var(--fs-md);color:var(--ink)}
.login-field.field input[type=text],.login-field .field input[type=text]{
  width:100%;padding:16px 18px;font-size:var(--fs-md);border:2px solid var(--copper);
  border-radius:var(--radius-md);background:var(--copper-soft);
  transition:box-shadow .15s ease,border-color .15s ease;
}
.login-field.field input[type=text]:focus,.login-field .field input[type=text]:focus{
  outline:none;border-color:var(--copper);background:var(--white);
  box-shadow:0 0 0 4px var(--copper-soft);
}
@media (max-width:640px){.field-row{grid-template-columns:1fr}}
.checkbox-field{display:flex;gap:8px;align-items:flex-start;font-size:var(--fs-xs);color:var(--sub)}

/* --- field hint link (kreator wyceny) --- */
.field-hint-link{display:inline-block;font-size:var(--fs-xs);color:var(--copper);font-weight:600;margin-bottom:10px;text-decoration:none}
.field-hint-link:hover{text-decoration:underline}

/* --- choice cards (kreator wyceny: single/multi-select) --- */
.choice-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px}
.choice-card{
  position:relative;display:flex;flex-direction:column;justify-content:center;gap:4px;min-height:52px;
  padding:14px 16px 14px 42px;border:1px solid var(--line);border-radius:var(--radius-sm);cursor:pointer;
  background:var(--white);transition:border-color .15s ease,background .15s ease;
}
.choice-card:hover{border-color:var(--copper)}
.choice-card input{position:absolute;left:16px;top:50%;transform:translateY(-50%) scale(1.15)}
.choice-card:has(input:checked){border-color:var(--copper);background:var(--copper-soft)}
.choice-card__label{font-size:var(--fs-sm);font-weight:600;color:var(--ink)}
.choice-card__hint{font-size:var(--fs-xs);color:var(--copper);text-decoration:none;font-weight:600}
.choice-card__hint:hover{text-decoration:underline}
@media (max-width:640px){
  .choice-cards{grid-template-columns:1fr;gap:10px}
  .choice-card{min-height:56px;padding:16px 16px 16px 46px}
}

/* --- wizard progress (kreator wyceny) --- */
.wizard-progress{display:flex;gap:var(--sp-4);margin-bottom:var(--sp-6)}
.wizard-progress__item{display:flex;align-items:center;gap:8px;color:var(--sub);font-size:var(--fs-xs);font-weight:600}
.wizard-progress__dot{
  width:26px;height:26px;border-radius:50%;border:1px solid var(--line);display:flex;
  align-items:center;justify-content:center;font-size:12px;color:var(--sub);flex-shrink:0;
}
.wizard-progress__item.is-active{color:var(--ink)}
.wizard-progress__item.is-active .wizard-progress__dot{border-color:var(--copper);background:var(--copper);color:var(--white)}
.wizard-progress__item.is-done .wizard-progress__dot{border-color:var(--sage);background:var(--sage);color:var(--white)}

.wizard-progress--bar{display:block}
.wizard-progress__track{height:6px;border-radius:99px;background:var(--stone);overflow:hidden;margin-bottom:8px}
.wizard-progress__fill{height:100%;background:var(--copper);border-radius:99px;transition:width .25s ease}
.wizard-progress__count{font-size:var(--fs-xs);color:var(--sub);font-weight:600}

/* --- honeypot (antyspam, ukryte pole formularza wyceny) --- */
.hp-field{position:absolute;left:-9999px;width:1px;height:1px;opacity:0;overflow:hidden}

/* --- pagination --- */
.pagination{display:flex;gap:8px;justify-content:center;margin-top:var(--sp-6)}
.pagination a,.pagination span{
  min-width:36px;height:36px;display:flex;align-items:center;justify-content:center;
  border-radius:var(--radius-sm);border:1px solid var(--line);font-size:var(--fs-sm);
}
.pagination .active{background:var(--ink);color:var(--white);border-color:var(--ink)}

/* --- data table (admin) --- */
.data-table{width:100%;border-collapse:collapse;background:var(--white)}
.data-table th,.data-table td{padding:12px 14px;text-align:left;font-size:var(--fs-sm);border-bottom:1px solid var(--line)}
.data-table th{color:var(--sub);font-weight:600;background:var(--stone)}
.data-table tr:hover td{background:var(--stone)}

/* --- grid helpers --- */
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-5)}
.grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--sp-5)}
@media (max-width:900px){.grid-3{grid-template-columns:repeat(2,1fr)}}
@media (max-width:640px){.grid-3,.grid-2{grid-template-columns:1fr}}
/* siatki-zapowiedzi (np. realizacje/opinie w sekcjach kategorii) — na tablecie, jeśli
   liczba elementów jest nieparzysta, ostatni znika, żeby zostały pełne pary (pełna lista
   zawsze jest dostępna pod osobnym linkiem, więc nic się nie gubi) */
@media (min-width:641px) and (max-width:900px){.grid-3:not(.grid-3--realizacje) > :last-child:nth-child(odd){display:none}}
/* sekcja "Nasze realizacje" na stronie głównej: widoczność i dokładanie kolejnych kart
   przyciskiem "Zobacz więcej" steruje w całości JS (patrz site.js), stąd brak tu reguł
   ukrywających na podstawie samej szerokości ekranu — JS i tak dobiera odpowiednią
   liczbę początkowo widocznych kart do bieżącej rozdzielczości. */

/* --- color swatches --- */
.color-palette{display:flex;flex-direction:column;gap:var(--sp-5)}
.color-group__title{
  font-size:12.5px;font-weight:700;color:var(--copper);margin-bottom:var(--sp-4);
  text-transform:uppercase;letter-spacing:.05em;padding-bottom:9px;border-bottom:2px solid var(--copper-soft);
}
.color-swatches{display:flex;flex-wrap:wrap;gap:var(--sp-4)}
.color-swatch{
  display:flex;flex-direction:column;align-items:center;gap:7px;width:78px;
  background:none;border:none;padding:0;font-family:inherit;cursor:pointer;
}
/* na telefonie flex-wrap dawał 2-4 na rząd zależnie od szerokości ekranu — grid o 3
   kolumnach robi to samo przewidywalnie na każdym telefonie */
@media (max-width:640px){
  .color-swatches{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4) var(--sp-2);justify-items:center}
  .color-swatch{width:auto}
}
.color-swatch--static{cursor:default}
.color-swatch__img{
  width:60px;height:60px;border-radius:50%;background-size:cover;background-position:center;
  border:1px solid var(--line);display:block;box-shadow:var(--shadow-sm);transition:transform .15s ease,box-shadow .15s ease;
}
.color-swatch:hover .color-swatch__img{transform:scale(1.08);box-shadow:var(--shadow-md)}
.color-swatch__img--empty{background:var(--stone)}
.color-swatch__name{font-size:11.5px;color:var(--sub);text-align:center;line-height:1.3;text-transform:uppercase;letter-spacing:.02em}
/* na desktopie flex-wrap mieścił 10+ kolorów w rzędzie na szerokość kontenera — max 8.
   Koło zdjęcia dostaje stały, większy niż domyślne 60px rozmiar (wcześniej rozciągnięte na
   100% szerokości kolumny wyszło zbyt duże) i jest wyśrodkowane w swojej kolumnie — gap
   między kolumnami daje czytelny odstęp, więc nie trzeba dodatkowo naciągać samego koła.
   Reguła musi być PO bazowej .color-swatch__img{width:60px} wyżej — przy równej
   specyficzności wygrywa kolejność w pliku, a media query samo w sobie tego nie zapewnia. */
@media (min-width:901px){
  .color-swatches{display:grid;grid-template-columns:repeat(8,1fr);gap:var(--sp-4);justify-items:center}
  .color-swatch{width:auto}
  .color-swatch__img{width:88px;height:88px}
}

/* --- generyczne maski zdjęcia (patrz product_definitions.php) — nakładane NA klasę
   .color-swatch__img (mask="circle" to jej domyślny, niezmieniony kształt — reszta to
   modyfikatory) i analogicznie na .door-models__item img niżej. Sześciokąt świadomie
   płaski (nie "spiczasty") — mniej ryzykowny wizualnie przy różnych proporcjach zdjęć. */
.color-swatch__img.mask-none{border-radius:8px}
.color-swatch__img.mask-rounded-square{border-radius:14px}
.color-swatch__img.mask-square{border-radius:0}
.color-swatch__img.mask-hexagon{border-radius:0;clip-path:polygon(25% 0%,75% 0%,100% 50%,75% 100%,25% 100%,0% 50%)}

/* --- door model collections (katalog modeli, np. Martom) --- */
/* overflow-anchor:none — zwijanie/rozwijanie kolekcji gwałtownie zmienia wysokość treści
   nad/pod bieżącą pozycją przewijania; domyślne "scroll anchoring" przeglądarki próbowało
   samo skompensować tę zmianę i w efekcie doskakiwało do zupełnie innej kolekcji (często
   pierwszej) zamiast zostawić przewijanie tam, gdzie je ręcznie ustawiliśmy w JS niżej. */
.door-models{display:flex;flex-direction:column;gap:var(--sp-3);overflow-anchor:none}
/* scroll-margin-top współdzieli tę samą zmienną co próg przyklejenia nagłówka (patrz
   --collection-sticky-top w regule summary niżej i w site.js) — dzięki temu zwinięcie
   przypiętej kolekcji może po prostu użyć natywnego scrollIntoView({block:'start'}), a
   przeglądarka sama zostawi dokładnie tyle miejsca nad nią, ile aktualnie zajmuje topbar
   (albo zero, kiedy topbar jest akurat schowany) — bez ręcznego liczenia offsetu w JS. */
.door-models__collection{scroll-margin-top:var(--collection-sticky-top,var(--header-h))}
.door-models__collection{background:var(--white);border:1px solid var(--line);border-radius:var(--radius-md)}
/* nazwa kolekcji zostaje przyklejona pod topbarem, kiedy przewijasz długą, rozwiniętą
   listę modeli (np. 46 pozycji) — zawsze widać, w której kolekcji się jest, i można ją
   zwinąć bez przewijania z powrotem na górę. overflow:hidden na kontenerze wyżej musiał
   zniknąć, bo blokowałoby position:sticky wewnątrz. */
.door-models__collection summary{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);cursor:pointer;list-style:none;user-select:none;
  position:sticky;top:var(--collection-sticky-top,var(--header-h));z-index:5;background:var(--white);
  border-radius:var(--radius-md);box-shadow:var(--shadow-sm);
  transition:top .15s ease;
}
/* --collection-sticky-top jest ustawiana z JS (site.js) na 0 albo z powrotem na var(--header-h)
   w tym samym momencie, w którym topbar się chowa/pokazuje przy przewijaniu (klasa
   .header-hidden) — bez tego nagłówek kolekcji zatrzymywał się tam, gdzie topbar BYŁBY,
   czyli wizualnie za nisko, ze sporym pustym pasem nad nim, kiedy topbar jest schowany.
   Zmiana przez customproperty na :root zamiast przez selektor z .header-hidden na dalekim
   rodzeństwie — silniki CSS bywają zawodne przy odświeżaniu position:sticky, gdy zmienia
   się tylko klasa na niepowiązanym elemencie w drzewie, a nie realna wartość zmiennej. */
.door-models__collection summary::-webkit-details-marker{display:none}
.door-models__collection summary h3{font-size:var(--fs-base);margin:0}
.door-models__collection summary::before{
  content:"+";flex:none;width:22px;height:22px;border-radius:50%;background:var(--stone);
  color:var(--ink);font-size:15px;font-weight:700;display:flex;align-items:center;justify-content:center;
  order:-1;transition:transform .15s ease;
}
.door-models__collection[open] summary::before{content:"−"}
.door-models__collection summary{flex-wrap:wrap}
.door-models__count{color:var(--sub);font-size:var(--fs-xs);font-weight:600;margin-left:auto}
.door-models__grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(145px,1fr));gap:var(--sp-3);
  padding:0 var(--sp-5) var(--sp-5);
}
/* auto-fill dawał 7 na szerokość kontenera 1180px — na desktopie wymuszamy dokładnie 6 */
@media (min-width:901px){
  .door-models__grid{grid-template-columns:repeat(6,1fr)}
}
.door-models__item{display:flex;flex-direction:column;align-items:center;gap:6px;text-decoration:none}
.door-models__item img{
  width:100%;aspect-ratio:1/1.35;object-fit:contain;background:var(--white);border-radius:8px;
  border:1px solid var(--line);transition:transform .15s ease,box-shadow .15s ease;
  padding:10px;box-sizing:border-box;
}
.door-models__item:hover img{transform:scale(1.04);box-shadow:var(--shadow-sm)}
.door-models__item img.mask-circle{border-radius:50%}
.door-models__item img.mask-rounded-square{border-radius:14px}
.door-models__item img.mask-square{border-radius:0}
.door-models__item img.mask-hexagon{border-radius:0;clip-path:polygon(25% 0%,75% 0%,100% 50%,75% 100%,25% 100%,0% 50%)}

/* --- autouzupełnianie miejscowości + mapka trasy (krok kontaktowy kreatora wycen) --- */
.city-autocomplete{position:relative}
.city-autocomplete__list{
  position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:20;
  background:var(--white);border:1px solid var(--line);border-radius:var(--radius-sm);
  box-shadow:var(--shadow-md);max-height:260px;overflow-y:auto;
}
.city-autocomplete__list[hidden]{display:none}
.city-autocomplete__option{
  display:block;width:100%;text-align:left;padding:10px 14px;border:0;background:none;
  font-size:var(--fs-sm);color:var(--ink);cursor:pointer;border-bottom:1px solid var(--line);
}
.city-autocomplete__option:last-child{border-bottom:0}
.city-autocomplete__option:hover,.city-autocomplete__option:focus,.city-autocomplete__option.is-active{background:var(--stone)}
.city-autocomplete__route{margin-top:var(--sp-3)}
.city-autocomplete__route[hidden]{display:none}
.city-autocomplete__distance-label{
  font-size:var(--fs-sm);font-weight:600;color:var(--ink);margin-bottom:8px;
}
.city-autocomplete__distance-label strong{color:var(--copper)}
.city-autocomplete__map{
  height:240px;border-radius:var(--radius-md);overflow:hidden;border:1px solid var(--line);background:var(--stone);
}
.door-models__symbol{font-size:11px;color:var(--sub);text-align:center;line-height:1.3;font-weight:600}
