/* ⚠ Kolor marki wchodzi TOKENEM MOTYWU, nie wartoscia wpisana w arkusz.
 * Ten modul jedzie na wszystkie sklepy rodziny, a kazda marka ma wlasny motyw:
 * Garibald oliwke, Koszulkowy cyan. Wartosc wpisana tutaj bylaby prawdziwa na
 * jednym sklepie i falszywa na pozostalych - i to bez zadnego bledu.
 *
 * ⚠ Zapas jest NEUTRALNY MARKOWO (grafit), a nie kolorem ktorejkolwiek marki.
 * Wchodzi tylko wtedy, gdy --bs-primary nie istnieje - czyli poza motywem sklepu.
 * Kolor drugiej marki w tej roli byloby najtrudniej zauwazyc: wygladalby jak
 * poprawnie dzialajacy motyw.
 */
:root {
  --abcu-brand: var(--bs-primary, #3f4347);
}

/*
 * Ababink - Personalizacja, style frontu.
 *
 * ⚠ Kolory ida WYLACZNIE z tokenow motywu (--bs-*). Nazwa koloru wpisana tutaj bylaby
 * nieprawdziwa na kolejnym sklepie rodziny, a modul jedzie na szesc sklepow. Wartosci
 * po przecinku w `var()` to zapas na wypadek motywu bez danego tokenu, nie decyzja.
 *
 * ⚠ Prog 992 px jest ten sam, na ktorym mega menu przechodzi w szuflade, a ikona zgod
 * znika - "telefon" ma w tym sklepie znaczyc jedno.
 */

.abcu { margin-top: .85rem; }

/* --------------------------------------------------- wejscie na karcie -- */

/* ⚠ Przycisk jest WYPELNIONY, czyli wyglada na glowna akcje strony - bo nia jest.
   Wczesniej mial obrys kreskowany, zeby czytal sie jako zaproszenie obok "dodaj do
   koszyka"; odkad natywnego przycisku na tych produktach nie ma, druga akcja nie
   istnieje i zaproszenie zamienialoby sie w slepy zaulek: klient nie mialby czym kupic. */
.abcu-open {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  width: 100%; min-height: 48px; padding: .7rem 1rem;
  border: 1.5px solid var(--abcu-brand);
  border-radius: var(--bs-border-radius, 10px);
  background: var(--abcu-brand);
  color: #fff;
  font-weight: 600; font-size: .95rem; cursor: pointer;
  transition: background-color .18s ease, border-color .18s ease;
}
.abcu-open:hover { background: color-mix(in srgb, var(--abcu-brand) 85%, #000); }
.abcu-open[hidden] { display: none; }

/* ⚠ Natywny "dodaj do koszyka" znika z produktow, ktore WYMAGAJA personalizacji.
   Nie jest to kosmetyka: rdzen i tak odmowilby dodania (pole personalizacji ma
   `required = 1`), wiec zostawiony przycisk prowadzilby wylacznie do komunikatu
   o bledzie. Regula celuje w rodzica, ktory zawiera nasz edytor, wiec produkt bez
   personalizacji zachowuje sie jak dotad. */
.js-product-add-to-cart:has(.abcu) .product__add-to-cart { display: none; }
.abcu-ico { width: 20px; height: 20px; flex: 0 0 auto; fill: none;
  stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

.abcu-hint {
  margin: .45rem 0 0; font-size: .82rem; text-align: center;
  color: var(--bs-secondary-color, #666);
}

.abcu-note { margin: .5rem 0 0; font-size: .85rem; }

/* ---------------------------------------------- stan po zapisaniu -- */

.abcu-done {
  display: flex; align-items: center; gap: .8rem;
  padding: .7rem .85rem;
  border: 1px solid var(--bs-border-color, #e5e5e5);
  border-radius: var(--bs-border-radius, 10px);
  background: var(--bs-tertiary-bg, #f9f9f9);
  font-size: .88rem;
}
/* ⚠ Wlasna regula `display` na elemencie z atrybutem `hidden` POKAZALABY go: reguła
   przegladarki `[hidden]{display:none}` ma najnizsza mozliwa specyficznosc. */
.abcu-done[hidden] { display: none; }

.abcu-done-img {
  width: 56px; height: 56px; flex: 0 0 auto;
  object-fit: cover; border-radius: calc(var(--bs-border-radius, 10px) - 3px);
  background: var(--bs-body-bg, #fff);
}
.abcu-done-img[hidden] { display: none; }
.abcu-done-txt { min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.abcu-done-txt > span {
  color: var(--bs-secondary-color, #666);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* ⚠ Podsumowanie jest WIELOLINIJKOWE (jedna linia na grupę pól), więc nie może
   dziedziczyć `nowrap` z reguły wyżej - przy trzech postaciach klient widział jedno
   przycięte zdanie z wielokropkiem i nie miał jak sprawdzić, co właściwie zamówił.
   Ograniczamy je czterema wierszami, bo to potwierdzenie, a nie karta zamówienia. */
.abcu-done-txt > [data-abcu-done-text] {
  white-space: pre-line;
  text-overflow: clip;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  line-height: 1.35;
}

/* Podgląd projektu w oknie „dodano do koszyka" (hook `displayCartModalFooter`).
   ⚠ `order: -1` stawia blok NAD przyciskami: stopka motywu jest fleksem z
   `justify-content: space-between`, a w DOM nasz blok idzie po przyciskach. Bez tego
   potwierdzenie stoi pod wezwaniem do działania, czyli po decyzji, którą ma poprzedzać.
   Gdyby motyw przestał być fleksem, blok wyląduje pod przyciskami - gorzej, ale nie źle. */
/* ⚠ `order` musi siedziec na POZYCJI FLEKSA, czyli na owijce motywu - nasz div jest
   jej dzieckiem i wlasny `order` nie robi tam nic (zmierzone: blok ladowal 46 px pod
   przyciskami mimo `order: -1`). Zawezamy `:has()`, zeby nie przestawiac stopki
   modulom, ktore kiedys wejda na ten sam hook. */
/* ⚠ `-2`, a nie `-1`: ponizej 576 px motyw sam podnosi przycisk kasy (`nth-child(2)`)
   na `order: -1`, wiec przy rownej wartosci decyduje kolejnosc w DOM i nasz blok ladowal
   MIEDZY przyciskami. Zmierzone na 390 px. */
.modal-footer .blockcart-modal__cart-footer-extra:has(.abcu-modal) { order: -2; }

.abcu-modal {
  width: 100%;
  display: flex; align-items: flex-start; gap: .7rem;
  padding: .7rem 0 .1rem;
  border-top: 1px solid var(--bs-border-color, #e4e4e4);
  text-align: left;
}
.abcu-modal-txt { min-width: 0; flex: 1 1 auto; }
/* ⚠ Odnosnik edycji stoi na koncu wiersza i NIE zawija sie razem z lista wyborow:
   przy dlugim podsumowaniu ma zostac widoczny, bo to jedyna droga do poprawki
   przed zamknieciem okna. */
.abcu-modal-edit {
  flex: 0 0 auto; align-self: center;
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .85rem; white-space: nowrap; min-height: 24px;
}
.abcu-modal-edit .material-icons { font-size: 16px; }
.abcu-modal-title { display: block; font-weight: 600; font-size: .92rem; margin-bottom: .15rem; }
.abcu-modal-list { list-style: none; margin: 0; padding: 0; font-size: .82rem;
  color: var(--bs-secondary-color, #666); }
.abcu-modal-list > li { margin: 0; }
.abcu-modal-list strong { font-weight: 600; color: var(--bs-body-color, #222); }

/* 1.113.0: plakietka „z imieniem” na kafelku (flaga rdzenia `abcu-imie`, product-flags.tpl motywu) - biala, z ciemna
   ramka, zeby nie wygladala jak „Nowosc”.
   ⚠⚠ 1.113.2: TLA NIE DA SIE nadpisac `background-color` - motyw daje kazdej plakietce poza `discount` tlo marki
   z `!important` W WARSTWIE KASKADY (`@layer`), a dla `!important` warstwa bije kod bez warstwy niezaleznie od
   selektora (1.113.0 i 1.113.1 na gNL: tlo rgb(0, 186, 255); docs/stan/front.md). Biel kladziemy wiec jako
   `background-image` (motyw go nie ustawia), a ramke zwykla deklaracja - dla zwyklych kod bez warstwy wygrywa.
   Kolor napisu zostaje z motywu (czarny `!important`). */
.product-flags .badge.abcu-imie {
  background-image: linear-gradient(var(--bs-body-bg, #fff), var(--bs-body-bg, #fff));
  border-color: var(--bs-body-color, #222);
  font-weight: 600;
}

/* 1.112.0: ten sam projekt na innym nosniku - wiersz kafelkow w stopce okna „Dodano”. ⚠ Stopka okna jest widoczna
   zawsze (okno przewija tresc, nie stopke), wiec blok ma byc NISKI: jeden wiersz, przewijany w poziomie. */
.abcu-xs { width: 100%; padding: .55rem 0 .15rem; text-align: left; }
.abcu-xs-t { margin: 0 0 .4rem; font-weight: 600; font-size: .9rem; }
.abcu-xs-l {
  list-style: none; margin: 0; padding: 0 0 .25rem;
  display: flex; gap: .5rem; overflow-x: auto; scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch; scrollbar-width: thin;
}
.abcu-xs-i { flex: 0 0 auto; scroll-snap-align: start; }
.abcu-xs-a {
  display: flex; flex-direction: column; align-items: center; gap: .15rem;
  width: 92px; padding: .35rem .25rem .4rem;
  border: 1px solid var(--bs-border-color, #e4e4e4); border-radius: var(--bs-border-radius, 10px);
  color: var(--bs-body-color, #222); text-decoration: none; background: var(--bs-body-bg, #fff);
  transition: border-color .15s ease;
}
@media (hover: hover) { .abcu-xs-a:hover { border-color: var(--abcu-brand); } }
.abcu-xs-a:focus-visible { outline: 2px solid var(--abcu-brand); outline-offset: 1px; }
.abcu-xs-img { width: 64px; height: 64px; object-fit: contain; border-radius: 6px; }
.abcu-xs-n {
  font-size: .74rem; line-height: 1.2; text-align: center;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.abcu-xs-p { font-size: .74rem; font-weight: 600; white-space: nowrap; }

/* Personalizacja w koszyku i na zamówieniu - renderuje ją hook `displayCustomization`. */
.abcu-custom-list { list-style: none; margin: 0; padding: 0; }
.abcu-custom-list > li { margin: 0 0 .25rem; }
.abcu-custom-list > li:last-child { margin-bottom: 0; }
.abcu-custom-group { display: block; font-size: .85rem; }
.abcu-custom-item { white-space: nowrap; }
.abcu-custom-label { color: var(--bs-secondary-color, #666); }

.abcu-link {
  align-self: flex-start;
  background: none; border: 0; padding: 0;
  color: var(--abcu-brand);
  text-decoration: underline; cursor: pointer; font-size: .85rem;
}

/* ------------------------------------------------------------ szuflada -- */

.abcu-drawer { position: fixed; inset: 0; z-index: 1080; }
.abcu-drawer[hidden] { display: none; }

.abcu-backdrop {
  position: absolute; inset: 0;
  background: rgba(17, 17, 17, .5);
  animation: abcu-fade .2s ease both;
}

.abcu-panel {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: var(--bs-body-bg, #fff);
  animation: abcu-slide-up .26s cubic-bezier(.22, 1, .36, 1) both;
}

@keyframes abcu-fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes abcu-slide-up { from { transform: translateY(3%); opacity: .6 } to { transform: none; opacity: 1 } }
@keyframes abcu-slide-in { from { transform: translateX(3%); opacity: .6 } to { transform: none; opacity: 1 } }

/* --- naglowek --- */

.abcu-head {
  flex: 0 0 auto;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  padding: .85rem 1rem;
  border-bottom: 1px solid var(--bs-border-color, #e5e5e5);
}
.abcu-head-txt { min-width: 0; }
.abcu-eyebrow {
  margin: 0 0 .1rem; font-size: .68rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--abcu-brand);
}
.abcu-title {
  margin: 0; font-size: 1rem; line-height: 1.3; font-weight: 600;
  /* Nazwy w tym katalogu to hasla z nadruku, wiec bywaja dlugie na dwie linijki. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

.abcu-x {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  /* Cel dotykowy 44 px - WCAG 2.5.5. */
  width: 44px; height: 44px; margin: -.35rem -.4rem 0 0;
  background: none; border: 0; cursor: pointer;
  border-radius: 50%;
  color: var(--bs-body-color, #222);
  transition: background-color .15s ease;
}
.abcu-x:hover { background: var(--bs-tertiary-bg, #f2f2f2); }
.abcu-x svg { width: 22px; height: 22px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; }

/* --- tresc --- */

.abcu-body {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
  overflow-y: auto; overscroll-behavior: contain;
}

/* --- podglad --- */

.abcu-preview {
  flex: 0 0 auto;
  padding: .9rem 1rem .5rem;
  background: var(--bs-body-bg, #fff);
  /* Na telefonie podglad zostaje na wierzchu, a pola przewijaja sie pod nim: inaczej
     klient pisze w polu, ktorego skutku nie widzi - a to jest cala wartosc edytora. */
  position: sticky; top: 0; z-index: 2;
  border-bottom: 1px solid var(--bs-border-color, #e5e5e5);
}

/*
 * ⚠ Ramka MUSI miec wysokosc definitywna (`height`, nie `max-height`) - i to nie jest
 * kosmetyka. Przy `max-height` na ramce o wysokosci `auto` procentowa wysokosc obrazka
 * nie ma sie do czego odniesc, wiec obrazek przyjmuje wysokosc z proporcji szerokosci,
 * przerasta ramke i zostaje PRZYCIETY u dolu - a obrys pola zadruku, liczony z proporcji,
 * ląduje wtedy obok nadruku. Widac to bylo wylacznie na telefonie i wylacznie na obrazku.
 */
.abcu-stage {
  position: relative; margin: 0 auto;
  display: flex; align-items: center; justify-content: center;
  /* ⚠ Szerokosc MUSI byc jawna. Scena brala ja dotad od obrazka, a obie warstwy sa
     pozycjonowane bezwzglednie, wiec bez tego zostaje z zerem i podglad znika - przy
     poprawnie wczytanym pliku i bez ani jednego bledu w konsoli. Przy okazji ramka
     przestaje zmieniac szerokosc przy przejsciu miedzy kompozytem a kadrem, bo te dwa
     pliki maja rozne proporcje - czyli znika skok calego ukladu szuflady. */
  width: 100%; max-width: 460px; height: 34vh;
  border-radius: var(--bs-border-radius, 10px);
  /* Biel, bo kompozyt jest splaszczany na bialym - kazdy inny kolor ramki dawalby
     widoczny szew wokol makiety. */
  background: #fff;
  overflow: hidden;
  /* 1.98.0: palec po podgladzie przewija szuflade (pionowo), dopoki nie ma czego przesuwac - wtedy
     `[data-abcu-pan]` nizej zabiera przegladarce caly gest. Szczypanie i tak dochodzi do skryptu. */
  touch-action: pan-y;
}

/* 1.98.0: SCENA - podklad, ostry wycinek i warstwa kadru pod jednym przeksztalceniem (translate + scale,
   od lewego gornego rogu). `z-index: 0` robi z niej osobny kontekst ukladania przy kazdym przyblizeniu,
   takze bez przeksztalcenia - obrys, wskaznik pracy i przyciski stoja zawsze nad nia. */
.abcu-world { position: absolute; inset: 0; z-index: 0; transform-origin: 0 0; }
/* Ostry wycinek z serwera: polozenie i wymiar w px sceny z JS (kadr z odpowiedzi), nad podkladem. */
.abcu-detail { position: absolute; z-index: 1; display: block; max-width: none; max-height: none; pointer-events: none; }
.abcu-detail[hidden] { display: none; }

/* Obrazek dobiera rozmiar sam, w granicach ramki. Dzieki temu jego pudelko jest
   DOKLADNIE tym, co widac - bez pasow po bokach - wiec obrys pola zadruku da sie
   ustawic wzgledem niego, zamiast odtwarzac rachunek `object-fit: contain` w skrypcie. */
/* ⚠ Obie warstwy sa pozycjonowane BEZWZGLEDNIE i srodkowane przez `inset` + `margin`,
   a nie ukladem fleksowym sceny. Powod jest ten sam, dla ktorego obrazek dobiera rozmiar
   sam: `offsetLeft`/`clientWidth` maja opisywac to, co widac. Dwa obrazki w ukladzie
   fleksowym stanelyby obok siebie, a przy `object-fit` pudelko elementu przestaloby byc
   obrysem obrazka i obrys pola zadruku wyladowalby obok nadruku. */
.abcu-img {
  position: absolute; inset: 0; margin: auto;
  display: block; max-width: 100%; max-height: 100%; width: auto; height: auto;
  transition: opacity .18s ease;
}
.abcu-img[hidden] { display: none; }
/* 1.98.0: po wczytaniu podklad uklada skrypt (boxImage) - co do ulamka piksela, bo na nim lezy ostry wycinek
   i warstwa kadru przy przyblizeniu do 12x. Srodkowanie marginesem zostaje na czas pierwszego wczytania. */
.abcu-img[data-abcu-box] { inset: auto; margin: 0; max-width: none; max-height: none; }
/* ⚠ 1.90.0: podgladu NIE przygaszamy na czas skladania nowego. Krycie .45 na bialej scenie
   (tlo musi byc biale - patrz .abcu-stage) zamienialo czarna koszulke w szara, a bialy kubek
   w biala plame, i trwalo cale przeladowanie: 0,5-1,3 s po kazdej przerwie w pisaniu
   (test 2026-10-06, Chromium/Firefox/WebKit). Wlasciciel: "bialo sie robi". Stary podglad
   stoi w pelnych kolorach, o pracy mowi wskaznik .abcu-busy (po 350 ms). `data-abcu-loading`
   zostaje jako znacznik stanu dla skryptu i testow. */

/* Obrys pola zadruku. Polozenie idzie z serwera W PROCENTACH obrazka - liczba
   w pikselach opisywalaby kompozyt sprzed przeskalowania i ramka ladowalaby obok. */
.abcu-area {
  position: absolute; pointer-events: none;
  left: var(--abcu-ax); top: var(--abcu-ay);
  width: var(--abcu-aw); height: var(--abcu-ah);
  border: 1px dashed rgba(255, 255, 255, .55);
  border-radius: 2px;
  transition: opacity .2s ease;
}
.abcu-area[hidden] { display: none; }

/* Wariant dla nosnika sfotografowanego pod katem: ksztalt robi <polygon>,
   wiec sam element nie moze miec wlasnej ramki ani zaokraglenia. */
.abcu-area--poly { border: 0; border-radius: 0; }
.abcu-area--poly svg { width: 100%; height: 100%; display: block; }
.abcu-area--poly polygon {
  fill: none;
  stroke: rgba(255, 255, 255, .55);
  stroke-width: 1;
  stroke-dasharray: 4 3;
}
/* 1.98.0: obrys zostaje przy przyblizeniu - stoi poza scena i ma 1 px przy kazdej skali. */

/* ⚠ Kursor jest JEDYNA zapowiedzia, ze podglad da sie przeciagnac - podpowiedz tekstowa
   stoi przy suwaku, a nie na obrazku. `touch-action: none` jest konieczne: bez niego
   przegladarka na telefonie potraktuje ruch palcem jako przewijanie strony i zdarzenia
   wskaznika przestana dochodzic w polowie gestu. */
.abcu-stage--drag,
.abcu-stage:has([data-abcu-crop-x]) { cursor: default; }
.abcu-stage[data-abcu-pan] { cursor: grab; touch-action: none; }
.abcu-stage[data-abcu-pan].abcu-stage--drag { cursor: grabbing; }

/*
 * Warstwa kadru rysowana w przegladarce - stoi nad kompozytem tylko na czas gestu.
 *
 * ⚠ `pointer-events: none` jest warunkiem, nie ozdobnikiem: warstwa przykrywa pole
 * zadruku, a to po nim klient wlasnie ciagnie - bez tego pierwszy ruch zabieralby
 * wskaznik samemu sobie.
 */
.abcu-live { position: absolute; z-index: 2; pointer-events: none; overflow: hidden; transform-origin: 0 0; }
.abcu-live-area { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
.abcu-live-area[hidden] { display: none; }
.abcu-live-frame { position: absolute; overflow: hidden; }
/* 1.97.0: zdjecie ma wymiar swojego POKRYCIA (jak na serwerze), a przycina je dopiero ramka - stad
   `max-width: none` i wymiary z JS zamiast `object-fit`, ktory przycinal je do ramki przed przesunieciem. */
.abcu-live-frame img { position: absolute; max-width: none; display: block; }
/* Duch: cale zdjecie przygaszone - klient widzi, co przesuwa i co zostaje poza ramka. Tylko w trakcie gestu. */
.abcu-live-ghost { position: absolute; opacity: 0; transition: opacity .12s ease; }
.abcu-live-ghost img { display: block; width: 100%; height: 100%; max-width: none; }
.abcu-live.is-ghost .abcu-live-ghost { opacity: .4; }
.abcu-live.is-ghost .abcu-live-frame {
  box-shadow: 0 0 0 var(--abcu-obw, 1.5px) rgba(255, 255, 255, .95), 0 0 0 calc(var(--abcu-obw, 1.5px) * 2) rgba(0, 0, 0, .3);
}
.abcu-live-canvas { position: absolute; left: 0; top: 0; width: 100%; height: 100%; display: block; }
.abcu-live-canvas[hidden] { display: none; }
/* 1.104.0: czysta makieta pod zdjeciem, przycieta (`clip-path` z JS) do ramki pola - pod przezroczystym zdjeciem
   nie ma prawa prowitywac stary nadruk z kompozytu. Wymiar podkladu, od lewego gornego rogu warstwy. */
.abcu-live-tlo { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: none; display: block; }
.abcu-live-tlo[hidden] { display: none; }
.abcu-live-front { position: absolute; max-width: none; display: block; }
.abcu-live-front[hidden] { display: none; }

/* --- szybki podglad --------------------------------------------------------- */

/*
 * ⚠ Przycisk stoi DOKLADNIE TAM, gdzie stalo natywne „do koszyka" - miedzy iloscia
 * a reszta bloku akcji. Renderujemy sie hookiem `displayProductActions`, czyli ZA
 * przyciskiem i za wszystkim, co inne moduly na tym hooku postawily wczesniej
 * (u nas zegar wysylki), wiec bez tego przycisk ladowal na samym dole karty podgladu -
 * kilkaset pikselow pod miejscem, w ktorym klient go szuka.
 *
 * ⚠ Kolejnosc robimy `order`-em, a nie przenoszeniem wezla w JS: rodzic jest fleksem
 * (`product__actions-qty-add`), wiec to jedna wlasciwosc zamiast kodu, ktory musialby
 * znac budowe motywu i odpalac sie po kazdym otwarciu podgladu.
 */
.abcu-preview-cta { margin: 0; flex: 1 1 100%; }
.quickview:has(.abcu-preview-cta) .product__quantity { order: -2; }
.quickview:has(.abcu-preview-cta) .abcu-preview-cta { order: -1; }
.abcu-open--link { text-decoration: none; width: 100%; justify-content: center; }

/*
 * ⚠ W szybkim podgladzie chowamy natywne „do koszyka". Produkt bez personalizacji
 * i tak nie wejdzie do koszyka (rdzen odmawia przy `customization_field.required`),
 * wiec przycisk, ktory zawsze konczy sie bledem, jest gorszy niz jego brak.
 *
 * ⚠ Zasieg jest ZAWEZONY DO OKNA PODGLADU i to jest warunek, nie ostroznosc na wyrost.
 * Regula `body:has(...)` schowalaby przyciski na calym listingu, a tresc podgladu
 * ZOSTAJE W DRZEWIE po jego zamknieciu - czyli po jednym zajrzeniu w produkt
 * personalizowany zniklyby przyciski wszystkich pozostalych. Ten blad juz tu byl,
 * w `ababinkpacks`.
 */
.quickview:has(.abcu-preview-cta) [data-button-action="add-to-cart"] { display: none; }
/* ⚠ Chowamy takze OWIJKE przycisku: sama pusta zajmuje w kolumnie caly wiersz, wiec
   po ukryciu guzika zostawala po nim dziura. */
.quickview:has(.abcu-preview-cta) .product__add-to-cart.add { display: none; }

/* Migotanie tylko przy PIERWSZYM ladowaniu - przy kazdej literze bylo by nerwowe. */
.abcu-skeleton {
  position: absolute; inset: 0;
  background: linear-gradient(90deg,
    var(--bs-tertiary-bg, #f2f2f2) 25%,
    var(--bs-secondary-bg, #e9e9e9) 37%,
    var(--bs-tertiary-bg, #f2f2f2) 63%);
  background-size: 400% 100%;
  animation: abcu-shimmer 1.3s ease-in-out infinite;
}
.abcu-skeleton[hidden] { display: none; }
@keyframes abcu-shimmer { from { background-position: 100% 0 } to { background-position: 0 0 } }

.abcu-tools {
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
  max-width: 460px; margin: .5rem auto 0;
}

.abcu-tool {
  display: inline-flex; align-items: center; gap: .35rem;
  min-height: 36px; padding: .3rem .6rem;
  background: none; border: 1px solid var(--bs-border-color, #e5e5e5);
  border-radius: 999px; cursor: pointer;
  font-size: .78rem; color: var(--bs-body-color, #222);
  transition: border-color .15s ease, background-color .15s ease;
}
.abcu-tool:hover { border-color: var(--abcu-brand); }
.abcu-tool[aria-pressed="true"] {
  border-color: var(--abcu-brand);
  background: color-mix(in srgb, var(--abcu-brand) 10%, transparent);
}
.abcu-tool svg { width: 15px; height: 15px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; }

.abcu-dims { font-size: .75rem; color: var(--bs-secondary-color, #666); text-align: right; }

/* 1.98.0: przyblizenie wybierane przez klienta - w rogu sceny, nad nia. */
.abcu-zctl {
  position: absolute; right: 8px; bottom: 8px; z-index: 4;
  display: flex; align-items: center; gap: 2px; padding: 3px;
  border-radius: 999px; background: rgba(255, 255, 255, .94);
  box-shadow: 0 1px 6px rgba(0, 0, 0, .18);
  touch-action: manipulation;
}
.abcu-zbtn, .abcu-zlevel {
  appearance: none; -webkit-appearance: none; margin: 0; border: 0; background: none;
  min-width: 34px; height: 34px; padding: 0 6px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  color: #222; font-size: .78rem; line-height: 1; cursor: pointer;
  transition: background-color .15s ease, opacity .15s ease;
}
.abcu-zlevel { min-width: 44px; font-variant-numeric: tabular-nums; }
.abcu-zbtn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
/* `:hover` tylko tam, gdzie jest kursor - na dotyku zostaje po stuknieciu i przycisk wyglada na wcisniety. */
@media (hover: hover) {
  .abcu-zbtn:hover:not(:disabled), .abcu-zlevel:hover:not(:disabled) { background: rgba(0, 0, 0, .07); }
}
.abcu-zbtn:active:not(:disabled), .abcu-zlevel:active:not(:disabled) { background: rgba(0, 0, 0, .12); }
.abcu-zbtn:focus-visible, .abcu-zlevel:focus-visible { outline: 2px solid var(--abcu-brand); outline-offset: 1px; }
.abcu-zbtn:disabled, .abcu-zlevel:disabled { opacity: .35; cursor: default; }
/* Palec potrzebuje wiekszego celu niz kursor. */
@media (pointer: coarse) {
  .abcu-zbtn, .abcu-zlevel { min-width: 40px; height: 40px; }
  .abcu-zlevel { min-width: 46px; }
}

/* 1.102.0: podpowiedz „przybliz” - dymek nad przyciskami przyblizenia, strzalka wskazuje „+”. Klik przybliza pole. */
.abcu-zhint {
  position: absolute; right: 8px; bottom: 54px; z-index: 5;
  max-width: min(240px, calc(100% - 16px)); margin: 0; padding: 8px 12px;
  border: 0; border-radius: 10px; background: #222; color: #fff;
  font: inherit; font-size: .8rem; line-height: 1.3; text-align: left;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .25); cursor: pointer;
  animation: abcu-zhint-in .25s ease;
}
.abcu-zhint::after {
  content: ''; position: absolute; right: 22px; bottom: -6px;
  border: 6px solid transparent; border-bottom: 0; border-top-color: #222;
}
.abcu-zhint[hidden] { display: none; }
@media (pointer: coarse) {
  .abcu-zhint { bottom: 60px; }
  .abcu-zhint::after { right: 25px; }
}
@keyframes abcu-zhint-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* --- formularz --- */

.abcu-form { flex: 1 1 auto; padding: 1rem; }

.abcu-fields { list-style: none; margin: 0; padding: 0; }
.abcu-item, .abcu-group { margin-bottom: 1.15rem; }
.abcu-item:last-child, .abcu-group:last-child { margin-bottom: 0; }
.abcu-field { margin-bottom: 1.15rem; }
.abcu-field:last-child { margin-bottom: 0; }

/* ⚠ Regula warunkowa chowa pole atrybutem `hidden`, wiec KAZDA nasza deklaracja
   `display` na tych elementach musi miec blizniaczke dla `[hidden]`: regula
   przegladarki `[hidden]{display:none}` ma najnizsza mozliwa specyficznosc i przegrywa
   z czymkolwiek wlasnym. Dzis zadne z nich `display` nie ustawia - te trzy linie
   pilnuja, zeby dolozenie go w przyszlosci nie odslonilo pol, ktorych klient widziec
   nie ma. */
.abcu-field[hidden], .abcu-item[hidden], .abcu-group[hidden] { display: none; }

/* Pole, ktore pojawia sie po wyborze w innym polu, WCHODZI - ten sam ruch, co karta
   nowej postaci. Zniknieciu nie animujemy: jest odpowiedzia na wybor klienta. */
.abcu-field.is-new { animation: abcu-rep-in .26s cubic-bezier(.22, 1, .36, 1) both; }

/* Grupa pol: jedna rzecz na nadruku zlozona z kilku wyborow (postac = sylwetka +
   fryzura + ubranie). Ramka jest tu informacja, a nie ozdoba - bez niej szesc
   wybierakow pod rzad wyglada jak szesc niezaleznych pytan. */
.abcu-group {
  padding: .85rem .9rem .9rem;
  border: 1px solid var(--bs-border-color, #dcdcdc);
  border-radius: var(--bs-border-radius, 10px);
  background: var(--bs-tertiary-bg, #f9f9f9);
}
.abcu-group-h {
  display: flex; align-items: center; gap: .45rem;
  margin: 0 0 .75rem; font-size: .9rem; font-weight: 700;
}
.abcu-group-b .abcu-label { font-weight: 500; }
.abcu-group-b .abcu-field:last-child { margin-bottom: 0; }

/* Powtarzalna grupa: karta na wystapienie, pasek z przyciskami pod spodem. */
.abcu-rep-list { display: grid; gap: .7rem; }
.abcu-rep-item {
  padding: .7rem .75rem .75rem;
  border: 1px solid var(--bs-border-color, #dcdcdc);
  border-radius: var(--bs-border-radius, 10px);
  background: var(--bs-body-bg, #fff);
}
/* Wejscie nowej postaci - patrz addInstance(). Skala zostaje na 1: karta jest szeroka
   na cala kolumne, wiec skalowanie jej rusza wszystkim obok. */
.abcu-rep-item.is-new { animation: abcu-rep-in .26s cubic-bezier(.22, 1, .36, 1) both; }
@keyframes abcu-rep-in { from { opacity: 0; transform: translateY(-6px) } to { opacity: 1; transform: none } }

.abcu-rep-head {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin-bottom: .6rem;
}
.abcu-rep-title { font-weight: 700; font-size: .88rem; margin-right: auto; }

.abcu-rep-kinds { display: inline-flex; gap: .25rem; }
.abcu-rep-kind {
  padding: .2rem .55rem; min-height: 30px;
  border: 1px solid var(--bs-border-color, #dcdcdc);
  border-radius: 999px;
  background: transparent; color: inherit;
  font-size: .76rem; line-height: 1.2; cursor: pointer;
}
.abcu-rep-kind.is-on {
  border-color: var(--abcu-brand);
  background: color-mix(in srgb, var(--abcu-brand) 12%, transparent);
  font-weight: 600;
}

.abcu-rep-del {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0;
  border: 0; border-radius: 50%;
  background: transparent; color: var(--bs-secondary-color, #666); cursor: pointer;
}
.abcu-rep-del svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; }
.abcu-rep-del:hover:not(:disabled) { background: var(--bs-tertiary-bg, #f0f0f0); }
/* ⚠ Ostatniej postaci nie da sie skasowac - nadruk bez ani jednej nie ma sensu.
   Przycisk zostaje widoczny, zeby nie znikal i nie wracal przy kazdej zmianie skladu. */
.abcu-rep-del:disabled { opacity: .35; cursor: not-allowed; }

.abcu-rep-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-top: .7rem; }
.abcu-rep-add {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .4rem .75rem; min-height: 38px;
  border: 1px dashed var(--abcu-brand);
  border-radius: var(--bs-border-radius, 10px);
  background: transparent; color: var(--abcu-brand);
  font-size: .82rem; font-weight: 600; cursor: pointer;
}
.abcu-rep-add:hover:not(:disabled) {
  background: color-mix(in srgb, var(--abcu-brand) 8%, transparent);
}
.abcu-rep-add:disabled { opacity: .4; cursor: not-allowed; }
.abcu-rep-info { font-size: .76rem; color: var(--bs-secondary-color, #666); margin-left: auto; }

/* Staly element wzoru: klient ma go widziec, ale nie ma czym zmienic. */
.abcu-locked {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .1rem .5rem;
  margin: 0; padding: .6rem .8rem;
  border-left: 3px solid var(--abcu-brand);
  border-radius: var(--bs-border-radius-sm, 4px);
  background: color-mix(in srgb, var(--abcu-brand) 7%, transparent);
  font-size: .86rem;
}
.abcu-locked-l { color: var(--bs-secondary-color, #666); }
.abcu-locked-v { font-weight: 700; }

.abcu-label {
  display: flex; align-items: center; gap: .45rem;
  margin-bottom: .4rem; font-size: .9rem; font-weight: 600;
}

/* Numer kroku zamiast gwiazdki przy wymaganych: kolejnosc mowi klientowi, ile jeszcze
   przed nim, a gwiazdka mowi tylko tyle, ze cos jest obowiazkowe. */
.abcu-step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: 0 0 auto;
  border-radius: 50%; font-size: .7rem; font-weight: 700;
  background: color-mix(in srgb, var(--abcu-brand) 14%, transparent);
  color: var(--abcu-brand);
}
.abcu-opt {
  font-style: normal; font-weight: 400; font-size: .74rem;
  color: var(--bs-secondary-color, #666);
}

.abcu-control { position: relative; }

.abcu-input {
  width: 100%; min-height: 48px;
  padding: .7rem 3.4rem .7rem .85rem;
  border: 1px solid var(--bs-border-color, #dcdcdc);
  border-radius: var(--bs-border-radius, 10px);
  background: var(--bs-body-bg, #fff);
  color: var(--bs-body-color, #222);
  font-size: 1rem;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.abcu-input:focus {
  outline: none;
  border-color: var(--abcu-brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--abcu-brand) 18%, transparent);
}
.abcu-input::placeholder { color: var(--bs-secondary-color, #999); }
/* 1.70.0: pole wielowierszowe (prani) - licznik w prawym dolnym rogu, nie w srodku wysokosci,
   i tekst wysrodkowany jak na nadruku, zeby klient widzial podzial wierszy tak, jak pojdzie. */
.abcu-textarea { min-height: 7.5rem; resize: vertical; text-align: center; line-height: 1.4; padding-right: .85rem; padding-bottom: 1.6rem; }
.abcu-control:has(.abcu-textarea) .abcu-count { top: auto; bottom: .5rem; transform: none; }

/* Licznik odlicza W DOL, czyli mowi "ile jeszcze zostalo", a nie "ile juz wpisales" -
   ta druga liczba nie jest klientowi do niczego potrzebna. */
.abcu-count {
  position: absolute; right: .8rem; top: 50%; transform: translateY(-50%);
  font-size: .75rem; font-variant-numeric: tabular-nums;
  color: var(--bs-secondary-color, #999);
  pointer-events: none;
}
.abcu-count[data-abcu-close] { color: var(--bs-warning, #9A6516); font-weight: 600; }

/* Jednostka doklejana przez serwer (1.57.0) - klient widzi, ze nie musi jej wpisywac. */
.abcu-suffix {
  position: absolute; right: 2.6rem; top: 50%; transform: translateY(-50%);
  font-size: .95rem; color: var(--bs-secondary-color, #777);
  pointer-events: none;
}

/* Przedrostek (1.69.0, rok "-1965-") - po lewej, a pole odsuwa tekst, zeby na siebie nie wchodzily.
   1.99.0: 1,8rem to zapas tylko dla jednego znaku; dluzszy przedrostek dostaje odstep z dlugosci w szablonie pola. */
.abcu-prefix { right: auto; left: .8rem; }
.abcu-field:has(.abcu-prefix) .abcu-input { padding-left: 1.8rem; }

.abcu-help { margin: .35rem 0 0; font-size: .78rem; color: var(--bs-secondary-color, #666); }

/* ⚠ Znacznik pola z bledem MUSI nazywac sie inaczej niz ramka komunikatu
   (`data-abcu-error`): ten sam atrybut na dwoch roznych rzeczach sprawia, ze
   `querySelector` trafia raz w jedna, raz w druga - zaleznie od kolejnosci w DOM. */
.abcu-field[data-abcu-invalid] .abcu-input {
  border-color: var(--bs-danger, #C9383E);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bs-danger, #C9383E) 15%, transparent);
}
.abcu-field[data-abcu-invalid] .abcu-step {
  background: color-mix(in srgb, var(--bs-danger, #C9383E) 16%, transparent);
  color: var(--bs-danger, #C9383E);
}

.abcu-warnings, .abcu-error {
  margin-top: 1rem; padding: .65rem .85rem;
  border-radius: var(--bs-border-radius, 10px);
  font-size: .84rem;
}
.abcu-warnings { background: var(--bs-warning-bg-subtle, #f7efdd); color: var(--bs-warning-text-emphasis, #6b460f); }
.abcu-error { background: var(--bs-danger-bg-subtle, #f8dcdd); color: var(--bs-danger-text-emphasis, #7a2226); }
.abcu-warnings[hidden], .abcu-error[hidden] { display: none; }

/* --- stopka --- */

.abcu-foot {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 1rem;
  padding: .75rem 1rem;
  padding-bottom: calc(.75rem + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--bs-border-color, #e5e5e5);
  background: var(--bs-body-bg, #fff);
}

.abcu-recap { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.abcu-recap-label {
  font-size: .68rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--bs-secondary-color, #888);
}
.abcu-summary {
  font-size: .85rem; color: var(--bs-body-color, #222);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.abcu-add {
  flex: 0 0 auto; min-height: 48px; padding: .7rem 1.4rem;
  border: 0; border-radius: var(--bs-border-radius, 10px);
  background: var(--abcu-brand); color: #fff;
  font-weight: 600; font-size: .95rem; cursor: pointer;
  transition: opacity .15s ease, transform .12s ease;
}
.abcu-add:hover { opacity: .92; }
.abcu-add:active { transform: translateY(1px); }
.abcu-add:disabled { opacity: .6; cursor: default; }

/* --------------------------------------------------------------- komputer -- */

@media (min-width: 992px) {
  .abcu-panel {
    inset: 0 0 0 auto;
    width: min(1000px, 100%);
    box-shadow: -10px 0 40px rgba(0, 0, 0, .16);
    animation-name: abcu-slide-in;
  }

  .abcu-head { padding: 1.1rem 1.5rem; }
  .abcu-title { font-size: 1.15rem; -webkit-line-clamp: 1; }

  .abcu-body { flex-direction: row; overflow: hidden; }

  .abcu-preview {
    /* Podglad zajmuje stala polowe panelu i NIE przewija sie razem z polami. */
    flex: 0 0 46%; position: static;
    display: flex; flex-direction: column; justify-content: center;
    padding: 1.5rem; border-bottom: 0;
    border-right: 1px solid var(--bs-border-color, #e5e5e5);
    background: var(--bs-tertiary-bg, #fafafa);
  }
  /* Na komputerze wysokosc daje kolumna, wiec `flex` zamiast `vh`. */
  .abcu-stage { flex: 1 1 auto; min-height: 0; height: auto; max-width: 100%; }
  .abcu-tools { flex: 0 0 auto; max-width: 100%; }

  /* Pola sa waskie z natury; rozciagniete na pol tysiaca pikseli wygladaja na zgubione
     w pustym panelu. Ograniczenie szerokosci jest tu decyzja, nie zapasem. */
  .abcu-form { overflow-y: auto; padding: 1.75rem 1.5rem; }
  .abcu-fields, .abcu-warnings, .abcu-error { max-width: 420px; }
  .abcu-foot { padding: 1rem 1.5rem; }
}

/* ------------------------------------------------------------- ruch -- */

@media (prefers-reduced-motion: reduce) {
  .abcu-live-ghost { transition: none; }
  .abcu-panel, .abcu-backdrop { animation: none; }
  .abcu-skeleton { animation: none; }
  .abcu-rep-item.is-new, .abcu-field.is-new { animation: none; }
  .abcu-img { transition: none; }
  .abcu-zhint { animation: none; }
  .abcu-add:active { transform: none; }
}

/* ------------------------------------------------------- grafika klienta -- */

.abcu-upload { margin-top: .1rem; }

.abcu-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .2rem; padding: 1.2rem 1rem; min-height: 116px;
  border: 1.5px dashed var(--bs-border-color, #d5d5d5);
  border-radius: var(--bs-border-radius, 10px);
  background: var(--bs-tertiary-bg, #fafafa);
  cursor: pointer; text-align: center;
  transition: border-color .15s ease, background-color .15s ease;
}
.abcu-drop:hover, .abcu-drop:focus-visible, .abcu-drop[data-abcu-nad] {
  border-color: var(--abcu-brand);
  background: color-mix(in srgb, var(--abcu-brand) 6%, transparent);
  outline: none;
}
.abcu-drop[hidden] { display: none; }
/* 1.111.0: wymagane zdjecie, ktorego brakuje przy „Dodaj do koszyka” - ta sama czerwien, co przy polu tekstowym */
.abcu-field[data-abcu-invalid] .abcu-drop {
  border-color: var(--bs-danger, #C9383E);
  background: color-mix(in srgb, var(--bs-danger, #C9383E) 6%, transparent);
}
.abcu-drop svg { width: 26px; height: 26px; fill: none; stroke: var(--abcu-brand);
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.abcu-drop-t { font-weight: 600; font-size: .9rem; }
.abcu-drop-s { font-size: .76rem; color: var(--bs-secondary-color, #777); }

.abcu-up {
  display: flex; gap: .8rem; align-items: flex-start;
  padding: .7rem; border: 1px solid var(--bs-border-color, #e5e5e5);
  border-radius: var(--bs-border-radius, 10px);
}
.abcu-up[hidden] { display: none; }

/* Szachownica pod miniatura: bez niej wynik usuwania tla wyglada tak samo jak
   oryginal na bialym tle - czyli klient nie widzi, ktory wariant wybiera. */
.abcu-up-img {
  width: 76px; height: 76px; flex: 0 0 auto; object-fit: contain;
  border-radius: calc(var(--bs-border-radius, 10px) - 4px);
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #e9e9e9 25%, transparent 25%, transparent 75%, #e9e9e9 75%),
    linear-gradient(45deg, #e9e9e9 25%, transparent 25%, transparent 75%, #e9e9e9 75%);
  background-size: 14px 14px;
  background-position: 0 0, 7px 7px;
}

.abcu-up-info { min-width: 0; display: flex; flex-direction: column; gap: .3rem; }
.abcu-up-name {
  font-size: .82rem; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.abcu-quality { font-size: .76rem; display: inline-flex; align-items: center; gap: .35rem; }
.abcu-quality::before {
  content: ''; width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
  background: var(--bs-secondary-color, #999);
}
.abcu-quality[data-abcu-grade="good"] { color: var(--bs-success, #206942); }
.abcu-quality[data-abcu-grade="good"]::before { background: var(--bs-success, #206942); }
.abcu-quality[data-abcu-grade="average"] { color: var(--bs-warning, #9A6516); }
.abcu-quality[data-abcu-grade="average"]::before { background: var(--bs-warning, #9A6516); }
.abcu-quality[data-abcu-grade="weak"] { color: var(--bs-danger, #C9383E); }
.abcu-quality[data-abcu-grade="weak"]::before { background: var(--bs-danger, #C9383E); }

@media (prefers-reduced-motion: reduce) {
  .abcu-upst-bar[data-abcu-indet] .abcu-upst-fill { animation: none; width: 100%; opacity: .5; }
  .abcu-upst-fill { transition: none; }
}

/* ============================================ wskazniki pracy i skalowanie == */

/*
 * ⚠ Wskaznik pracy niesie NAPIS, nie sama animacje. Kreciolek bez slowa nie odroznia
 * "trwa skladanie" od "cos sie zacielo" - a to jest dokladnie ta chwila, w ktorej
 * klient zastanawia sie, czy klikac dalej.
 */
.abcu-busy {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-radius: 999px;
  background: rgba(255, 255, 255, .94); color: #333;
  font-size: .82rem; box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
  z-index: 3; pointer-events: none;
}
.abcu-busy[hidden] { display: none; }

.abcu-spinner {
  width: 14px; height: 14px; flex: none; border-radius: 50%;
  border: 2px solid rgba(0, 0, 0, .16); border-top-color: currentColor;
  animation: abcu-rotation .7s linear infinite;
}
.abcu-spinner[hidden] { display: none; }
@keyframes abcu-rotation { to { transform: rotate(360deg); } }

.abcu-drop-icon { position: relative; display: inline-flex; }

/*
 * 1.96.0: stan wgrywania W MIEJSCU strefy (ta sama wysokosc, wiec nic pod spodem nie skacze):
 * etap z procentem, pasek, ile MB doszlo. Etap `serwer` i nieznany rozmiar - pasek bez konca.
 */
.abcu-upst {
  display: flex; flex-direction: column; justify-content: center; gap: .45rem;
  min-height: 116px; padding: 1rem;
  border: 1.5px solid color-mix(in srgb, var(--abcu-brand) 45%, transparent);
  border-radius: var(--bs-border-radius, 10px);
  background: color-mix(in srgb, var(--abcu-brand) 5%, var(--bs-body-bg, #fff));
}
.abcu-upst[hidden] { display: none; }
.abcu-upst-row { display: flex; align-items: center; gap: .5rem; font-size: .9rem; font-weight: 600; }
.abcu-upst-row .abcu-spinner { border-top-color: var(--abcu-brand); }
.abcu-upst-t { min-width: 0; }
.abcu-upst-p { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--abcu-brand); }
.abcu-upst-bar {
  position: relative; height: 8px; overflow: hidden; border-radius: 999px;
  background: color-mix(in srgb, var(--abcu-brand) 15%, transparent);
}
.abcu-upst-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  border-radius: inherit; background: var(--abcu-brand);
  transition: width .2s ease;
}
.abcu-upst-bar[data-abcu-indet] .abcu-upst-fill {
  width: 35%; transition: none;
  animation: abcu-upst-indet 1.1s ease-in-out infinite;
}
@keyframes abcu-upst-indet { from { left: -35% } to { left: 100% } }
.abcu-upst-s { font-size: .76rem; color: var(--bs-secondary-color, #666); font-variant-numeric: tabular-nums; }
.abcu-upst-s:empty { display: none; }

/* Blad wgrania stoi PRZY polu - ramka bledow szuflady jest na koncu formularza, czesto poza oknem. */
.abcu-uperr {
  margin: .5rem 0 0; padding: .55rem .75rem;
  border-radius: var(--bs-border-radius, 10px);
  background: var(--bs-danger-bg-subtle, #f8dcdd); color: var(--bs-danger-text-emphasis, #7a2226);
  font-size: .82rem;
}
.abcu-uperr[hidden] { display: none; }

.abcu-up-ok {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: .76rem; font-weight: 600; color: var(--bs-success, #206942);
}
.abcu-up-ok::before {
  content: ''; width: .5rem; height: .85rem; flex: 0 0 auto; margin: -.2rem .1rem 0 .1rem;
  border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.abcu-up-ok[hidden] { display: none; }

.abcu-spinner-btn { border-color: rgba(255, 255, 255, .35); border-top-color: #fff; }
.abcu-add.is-praca { cursor: progress; }
/* Przycisk z napisem wgrywania i procentem: cyfry o stalej szerokosci, zeby nie drgal; spinner obok tekstu. */
.abcu-add.is-wgrywanie { display: inline-flex; align-items: center; gap: .5rem; font-variant-numeric: tabular-nums; }
.abcu-add.is-wgrywanie:disabled { opacity: .85; }

.abcu-mockup-desc {
  text-align: center; font-size: .72rem; color: #999; margin: 4px 0 0;
  letter-spacing: .03em;
}

/* -------------------------------------------------------------- skalowanie -- */

.abcu-scale { margin-top: 10px; }
.abcu-scale[hidden] { display: none; }
.abcu-scale-label { display: block; font-size: .76rem; color: #666; margin-bottom: 4px; }
.abcu-scale-row { display: flex; align-items: center; gap: 8px; }

/* ⚠ Cel dotykowy 44 px, nie mniej: przyciski skali stoja obok siebie, a klient
   poprawia wielkosc kciukiem. */
.abcu-scale-btn {
  width: 34px; height: 34px; flex: none; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--bs-border-color, #ddd); background: #fff;
  font-size: 1.1rem; line-height: 1; color: #333;
}
.abcu-scale-btn:disabled { opacity: .4; cursor: default; }

/* ⚠ Suwak zostaje NATYWNY. Obsluga klawiatura, rola i wartosc dla czytnika ekranu
   oraz zachowanie na dotyku sa w nim za darmo; wlasny element wymagalby odtworzenia
   tego wszystkiego. Kolor uchwytu bierzemy jedna wlasciwoscia. */
.abcu-scale-row input[type="range"] {
  flex: 1; min-width: 0; accent-color: var(--abcu-brand);
}
.abcu-scale-result {
  min-width: 3.2em; text-align: right; font-size: .8rem;
  font-variant-numeric: tabular-nums; color: #444;
}

@media (max-width: 991px) {
  .abcu-scale-btn { width: 44px; height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  /* Bez ruchu zostaje sam ksztalt, wiec wskaznik musi pulsowac przezroczystoscia -
     inaczej znika jako informacja. */
  .abcu-spinner { animation: abcu-pulse-spinner 1.2s ease-in-out infinite; }
}
@keyframes abcu-pulse-spinner { 50% { opacity: .3; } }

/* --------------------------------------------------------- wybor kroju pisma -- */

.abcu-fonts { margin-top: 8px; }
.abcu-fonts-label { display: block; font-size: .76rem; color: #666; margin-bottom: 4px; }
.abcu-fonts-list { display: flex; flex-wrap: wrap; gap: 6px; }

/* ⚠ Kafelek jest napisany SWOIM krojem, bo nazwa kroju sama z siebie nic nie mowi -
   "Coiny" i "Lobster" roznia sie dla klienta wylacznie tym, jak wygladaja. */
.abcu-font {
  border: 1px solid var(--bs-border-color, #ddd); background: #fff; color: #222;
  border-radius: 8px; padding: 6px 12px; cursor: pointer; font-size: 1.05rem; line-height: 1.2;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.abcu-font:hover { border-color: #aaa; }
.abcu-font.is-on {
  border-color: var(--abcu-brand);
  box-shadow: 0 0 0 1px var(--abcu-brand);
}

/* ⚠ Cel dotykowy 44 px na telefonie: kafelki stoja ciasno obok siebie, a klient
   przebiera miedzy nimi kciukiem. */
@media (max-width: 991px) {
  .abcu-font { min-height: 44px; }
}

/* ------------------------------------------------- wybor grafiki z biblioteki -- */

.abcu-assets { margin-top: 6px; }
/* ⚠ Siatka, a nie rzad z przewijaniem w bok. Zagniezdzone przewijanie w szufladzie
   znaczy, ze kolko myszy raz przewija liste, raz strone - ta sama pulapka, ktora
   wylecial a z panelu filtrow wyszukiwarki. */
.abcu-assets-list {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
}
.abcu-asset {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  border: 1px solid var(--bs-border-color, #ddd); background: #fff; color: #222;
  border-radius: 8px; padding: 6px 4px; cursor: pointer; line-height: 1.15;
}
/* ⚠ Kafelek ma OBRAZEK I PODPIS, nigdy sam obrazek. Warianty roznia sie czasem
   szczegolem (kolor wlosow, dlugosc grzywki), ktorego na miniaturze 72 px nie widac -
   a sam obrazek jako jedyny nosnik informacji lamie WCAG 1.4.1. To ta sama zasada,
   co przy probnikach kolorow w filtrach wyszukiwarki. */
.abcu-asset img {
  width: 100%; height: 72px; object-fit: contain; display: block;
  /* Grafiki bywaja jasne i przezroczyste - na bieli znikaja bez tego tla. */
  background: var(--bs-tertiary-bg, #f5f5f5); border-radius: 5px;
}
.abcu-asset-n {
  font-size: .72rem; color: #444; text-align: center;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.abcu-asset:hover { border-color: #aaa; }
.abcu-asset.is-on {
  border-color: var(--abcu-brand);
  box-shadow: 0 0 0 1px var(--abcu-brand);
}
.abcu-assets-empty { font-size: .82rem; color: #777; margin: 4px 0 0; }

@media (max-width: 991px) {
  /* Wiekszy kafelek na dotyku: cel ma 44 px z zapasem, a przy okazji widac grafike. */
  .abcu-assets-list { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
  .abcu-asset img { height: 84px; }
}

/* --- brak prawa zwrotu przy personalizacji ------------------------------- */
/* ⚠ To informacja, a nie ostrzezenie o bledzie: ma byc widoczna i spokojna.
   Czerwien czytalaby sie jak usterka zamowienia. */
.abcu-returns-line {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin: .35rem 0 0;
  font-size: .8125rem;
  color: var(--bs-secondary-color, #666);
}
.abcu-returns-line .material-icons { font-size: 1rem; }

.abcu-returns { margin-bottom: 1rem; }
.abcu-returns p { margin: 0 0 .35rem; }
.abcu-returns p:last-child { margin-bottom: 0; }
.abcu-returns__heading { font-weight: 600; }
.abcu-returns__complaint { font-size: .875rem; }

/* --- pozycja koszyka z personalizacja ------------------------------------- */
/* ⚠ Miniatury projektu tu nie ma: zdjeciem pozycji jest sama wizualizacja
   (hook `actionPresentCart`). */
.abcu-cart-line { margin-top: .4rem; }
.abcu-cart-line .abcu-returns-line { margin: 0; }
.abcu-cart-actions { margin: 0 0 .25rem; }
/* ⚠ `min-height` to wymog dotykowy (WCAG 2.5.8: 24 x 24 px), a nie estetyka: sam
   wiersz tekstu 0,85 rem daje 20 px wysokosci, czyli cel mniejszy od minimum. */
.abcu-cart-edit {
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .85rem; min-height: 24px;
}
.abcu-cart-edit .material-icons { font-size: 16px; }

/* ⚠ Cel dotykowy na telefonie: 44 px (WCAG 2.5.5). Na myszy 30 px jest w porzadku
   i mieści karte postaci w mniejszej wysokosci - to ta sama zasada, co przy wierszu
   filtra w wyszukiwarce. */
@media (max-width: 991px) {
  .abcu-rep-del { width: 44px; height: 44px; }
  .abcu-rep-kind { min-height: 44px; padding-left: .8rem; padding-right: .8rem; }
  .abcu-rep-add { min-height: 44px; }
}
