diff --git a/src/islands/Internet/InternetAddonsModal.jsx b/src/islands/Internet/InternetAddonsModal.jsx index e8f11c3..28bcdcc 100644 --- a/src/islands/Internet/InternetAddonsModal.jsx +++ b/src/islands/Internet/InternetAddonsModal.jsx @@ -1,4 +1,5 @@ import { useEffect, useMemo, useState } from "preact/hooks"; +import useDraggableFloating from "../hooks/useDraggableFloating.js"; import "../../styles/modal.css"; import "../../styles/addons.css"; @@ -73,6 +74,7 @@ export default function InternetAddonsModal({ addons = [], cenaOpis = "zł / mies.", }) { + const floating = useDraggableFloating("fuz_floating_total_pos_internet_v1"); const phonePlans = useMemo(() => mapPhoneYamlToPlans(phoneCards), [phoneCards]); const addonsList = useMemo(() => normalizeAddons(addons), [addons]); @@ -484,7 +486,13 @@ export default function InternetAddonsModal({ -
e.stopPropagation()}> +
e.stopPropagation()} + >
Razem {money(totalMonthly)} diff --git a/src/islands/Internet/InternetCards.jsx b/src/islands/Internet/InternetCards.jsx index 51e753d..1477a73 100644 --- a/src/islands/Internet/InternetCards.jsx +++ b/src/islands/Internet/InternetCards.jsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "preact/hooks"; import Markdown from "../Markdown.jsx"; import OffersSwitches from "../Switches.jsx"; import InternetAddonsModal from "./InternetAddonsModal.jsx"; -import "../../styles/addons.css"; +import "../../styles/cards.css"; function formatMoney(amount, currency = "PLN") { if (typeof amount !== "number" || Number.isNaN(amount)) return ""; diff --git a/src/islands/hooks/useDraggableFloating.js b/src/islands/hooks/useDraggableFloating.js new file mode 100644 index 0000000..26bb00d --- /dev/null +++ b/src/islands/hooks/useDraggableFloating.js @@ -0,0 +1,140 @@ +import { useEffect, useRef, useState } from "preact/hooks"; + +function clamp(v, min, max) { + return Math.max(min, Math.min(max, v)); +} + +export default function useDraggableFloating(storageKey, opts = {}) { + const margin = Number.isFinite(opts.margin) ? opts.margin : 12; + + const ref = useRef(null); + + const [pos, setPos] = useState(() => { + if (typeof window === "undefined") return { x: null, y: null }; + try { + const raw = localStorage.getItem(storageKey); + return raw ? JSON.parse(raw) : { x: null, y: null }; + } catch { + return { x: null, y: null }; + } + }); + + const stateRef = useRef({ + dragging: false, + pointerId: null, + startX: 0, + startY: 0, + originX: 0, + originY: 0, + }); + + function getBounds(el) { + const rect = el.getBoundingClientRect(); + const maxX = window.innerWidth - rect.width - margin; + const maxY = window.innerHeight - rect.height - margin; + return { maxX, maxY }; + } + + function persist(next) { + try { + localStorage.setItem(storageKey, JSON.stringify(next)); + } catch {} + } + + function onPointerDown(e) { + if (e.pointerType === "mouse" && e.button !== 0) return; + + e.preventDefault(); + e.stopPropagation(); + + const el = ref.current; + if (!el) return; + + el.setPointerCapture?.(e.pointerId); + + const st = stateRef.current; + st.dragging = true; + st.pointerId = e.pointerId; + st.startX = e.clientX; + st.startY = e.clientY; + + const rect = el.getBoundingClientRect(); + st.originX = pos.x == null ? rect.left : pos.x; + st.originY = pos.y == null ? rect.top : pos.y; + } + + function onPointerMove(e) { + const st = stateRef.current; + if (!st.dragging || st.pointerId !== e.pointerId) return; + + const el = ref.current; + if (!el) return; + + const dx = e.clientX - st.startX; + const dy = e.clientY - st.startY; + + const { maxX, maxY } = getBounds(el); + + const next = { + x: clamp(st.originX + dx, margin, maxX), + y: clamp(st.originY + dy, margin, maxY), + }; + + setPos(next); + } + + function onPointerUp(e) { + const st = stateRef.current; + if (!st.dragging || st.pointerId !== e.pointerId) return; + + st.dragging = false; + st.pointerId = null; + + // zapis po puszczeniu + persist(pos); + } + + function reset() { + const next = { x: null, y: null }; + setPos(next); + try { + localStorage.removeItem(storageKey); + } catch {} + } + + // docinanie na resize + useEffect(() => { + function onResize() { + const el = ref.current; + if (!el || pos.x == null || pos.y == null) return; + + const { maxX, maxY } = getBounds(el); + const next = { + x: clamp(pos.x, margin, maxX), + y: clamp(pos.y, margin, maxY), + }; + + if (next.x !== pos.x || next.y !== pos.y) { + setPos(next); + persist(next); + } + } + + window.addEventListener("resize", onResize); + return () => window.removeEventListener("resize", onResize); + }, [pos.x, pos.y]); + + const style = + pos.x == null || pos.y == null + ? undefined + : `left:${pos.x}px; top:${pos.y}px; right:auto; bottom:auto;`; + + const handlers = { + onPointerDown, + onPointerMove, + onPointerUp, + onPointerCancel: onPointerUp, + }; + + return { ref, style, handlers, pos, setPos, reset }; +} diff --git a/src/islands/jambox/JamboxAddonsModal.jsx b/src/islands/jambox/JamboxAddonsModal.jsx index 2581108..7e3d7bb 100644 --- a/src/islands/jambox/JamboxAddonsModal.jsx +++ b/src/islands/jambox/JamboxAddonsModal.jsx @@ -1,4 +1,6 @@ import { useEffect, useMemo, useState } from "preact/hooks"; +import useDraggableFloating from "../hooks/useDraggableFloating.js"; + import "../../styles/modal.css"; import "../../styles/addons.css"; @@ -181,6 +183,7 @@ export default function JamboxAddonsModal({ const addonsList = useMemo(() => normalizeAddons(addons), [addons]); const decodersList = useMemo(() => normalizeDecoders(decoders), [decoders]); + const floating = useDraggableFloating("fuz_floating_total_pos_tv_v1"); const tvAddonsVisible = useMemo(() => { if (!pkg) return []; @@ -415,130 +418,130 @@ export default function JamboxAddonsModal({ // --------- const LS_KEY = "fuz_offer_config_v1"; -function buildOfferPayload() { - const phone = selectedPhoneId - ? phonePlans.find((p) => String(p.id) === String(selectedPhoneId)) - : null; + function buildOfferPayload() { + const phone = selectedPhoneId + ? phonePlans.find((p) => String(p.id) === String(selectedPhoneId)) + : null; - const decoder = selectedDecoderId - ? decodersList.find((d) => String(d.id) === String(selectedDecoderId)) - : null; + const decoder = selectedDecoderId + ? decodersList.find((d) => String(d.id) === String(selectedDecoderId)) + : null; - const tvChosen = tvAddonsVisible - .map((a) => { - const qty = Number(selectedQty[a.id] || 0); - if (qty <= 0) return null; + const tvChosen = tvAddonsVisible + .map((a) => { + const qty = Number(selectedQty[a.id] || 0); + if (qty <= 0) return null; - const termPricing = hasTvTermPricing(a, pkg); - const term = tvTerm[a.id] || "12m"; - const unit = getAddonUnitPrice(a, pkg, termPricing ? term : null); + const termPricing = hasTvTermPricing(a, pkg); + const term = tvTerm[a.id] || "12m"; + const unit = getAddonUnitPrice(a, pkg, termPricing ? term : null); - return { - id: a.id, - nazwa: a.nazwa, - qty, - term: termPricing ? term : null, - unit, - }; - }) - .filter(Boolean); + return { + id: a.id, + nazwa: a.nazwa, + qty, + term: termPricing ? term : null, + unit, + }; + }) + .filter(Boolean); - const addonsChosen = addonsList - .map((a) => { - const qty = Number(selectedQty[a.id] || 0); - if (qty <= 0) return null; + const addonsChosen = addonsList + .map((a) => { + const qty = Number(selectedQty[a.id] || 0); + if (qty <= 0) return null; - const unit = getAddonUnitPrice(a, pkg, null); - return { id: a.id, nazwa: a.nazwa, qty, unit }; - }) - .filter(Boolean); + const unit = getAddonUnitPrice(a, pkg, null); + return { id: a.id, nazwa: a.nazwa, qty, unit }; + }) + .filter(Boolean); - return { - createdAt: new Date().toISOString(), - pkg: { id: pkg?.id ?? null, name: pkg?.name ?? "", price: basePrice }, - phone: phone ? { id: phone.id, name: phone.name, price: phone.price_monthly } : null, - decoder: decoder ? { id: decoder.id, name: decoder.nazwa, price: decoder.cena } : null, - tvAddons: tvChosen, - addons: addonsChosen, - totals: { - base: basePrice, - phone: phonePrice, - decoder: decoderPrice, - tv: tvAddonsPrice, - addons: addonsOnlyPrice, - total: totalMonthly, - currencyLabel: cenaOpis, - }, - }; -} - -function saveOfferToLocalStorage() { - try { - const payload = buildOfferPayload(); - localStorage.setItem(LS_KEY, JSON.stringify(payload)); - } catch {} -} - -//-- dopisane -function moneyWithLabel(v) { - return `${money(v)} ${cenaOpis}`; -} - -function buildOfferMessage(payload) { - const lines = []; - - // nagłówek - lines.push(`Wybrana oferta: ${payload?.pkg?.name || "—"}`); - lines.push(""); - - // ✅ WSZYSTKIE linie jak w podsumowaniu - lines.push(`Pakiet: ${moneyWithLabel(payload?.totals?.base ?? 0)}`); - lines.push(`Telefon: ${payload?.phone ? moneyWithLabel(payload.totals.phone) : "—"}`); - lines.push(`Dekoder: ${payload?.decoder ? moneyWithLabel(payload.totals.decoder) : "—"}`); - lines.push(`Dodatki TV: ${payload?.tvAddons?.length ? moneyWithLabel(payload.totals.tv) : "—"}`); - lines.push(`Dodatkowe usługi: ${payload?.addons?.length ? moneyWithLabel(payload.totals.addons) : "—"}`); - lines.push(`Łącznie: ${moneyWithLabel(payload?.totals?.total ?? 0)}`); - - // szczegóły (pozycje) - if (payload?.phone) { - lines.push(""); - lines.push(`Telefon: ${payload.phone.name} (${moneyWithLabel(payload.phone.price)})`); + return { + createdAt: new Date().toISOString(), + pkg: { id: pkg?.id ?? null, name: pkg?.name ?? "", price: basePrice }, + phone: phone ? { id: phone.id, name: phone.name, price: phone.price_monthly } : null, + decoder: decoder ? { id: decoder.id, name: decoder.nazwa, price: decoder.cena } : null, + tvAddons: tvChosen, + addons: addonsChosen, + totals: { + base: basePrice, + phone: phonePrice, + decoder: decoderPrice, + tv: tvAddonsPrice, + addons: addonsOnlyPrice, + total: totalMonthly, + currencyLabel: cenaOpis, + }, + }; } - if (payload?.decoder) { - lines.push(""); - lines.push(`Dekoder: ${payload.decoder.name} (${moneyWithLabel(payload.decoder.price)})`); + function saveOfferToLocalStorage() { + try { + const payload = buildOfferPayload(); + localStorage.setItem(LS_KEY, JSON.stringify(payload)); + } catch { } } - if (Array.isArray(payload?.tvAddons) && payload.tvAddons.length) { + //-- dopisane + function moneyWithLabel(v) { + return `${money(v)} ${cenaOpis}`; + } + + function buildOfferMessage(payload) { + const lines = []; + + // nagłówek + lines.push(`Wybrana oferta: ${payload?.pkg?.name || "—"}`); lines.push(""); - lines.push("Pakiety dodatkowe TV:"); - for (const it of payload.tvAddons) { - const termTxt = it.term ? `, ${it.term}` : ""; - lines.push( - `- ${it.nazwa} x${it.qty}${termTxt} @ ${moneyWithLabel(it.unit)}` - ); + + // ✅ WSZYSTKIE linie jak w podsumowaniu + lines.push(`Pakiet: ${moneyWithLabel(payload?.totals?.base ?? 0)}`); + lines.push(`Telefon: ${payload?.phone ? moneyWithLabel(payload.totals.phone) : "—"}`); + lines.push(`Dekoder: ${payload?.decoder ? moneyWithLabel(payload.totals.decoder) : "—"}`); + lines.push(`Dodatki TV: ${payload?.tvAddons?.length ? moneyWithLabel(payload.totals.tv) : "—"}`); + lines.push(`Dodatkowe usługi: ${payload?.addons?.length ? moneyWithLabel(payload.totals.addons) : "—"}`); + lines.push(`Łącznie: ${moneyWithLabel(payload?.totals?.total ?? 0)}`); + + // szczegóły (pozycje) + if (payload?.phone) { + lines.push(""); + lines.push(`Telefon: ${payload.phone.name} (${moneyWithLabel(payload.phone.price)})`); } - } - if (Array.isArray(payload?.addons) && payload.addons.length) { - lines.push(""); - lines.push("Dodatkowe usługi:"); - for (const it of payload.addons) { - lines.push(`- ${it.nazwa} x${it.qty} @ ${moneyWithLabel(it.unit)}`); + if (payload?.decoder) { + lines.push(""); + lines.push(`Dekoder: ${payload.decoder.name} (${moneyWithLabel(payload.decoder.price)})`); } + + if (Array.isArray(payload?.tvAddons) && payload.tvAddons.length) { + lines.push(""); + lines.push("Pakiety dodatkowe TV:"); + for (const it of payload.tvAddons) { + const termTxt = it.term ? `, ${it.term}` : ""; + lines.push( + `- ${it.nazwa} x${it.qty}${termTxt} @ ${moneyWithLabel(it.unit)}` + ); + } + } + + if (Array.isArray(payload?.addons) && payload.addons.length) { + lines.push(""); + lines.push("Dodatkowe usługi:"); + for (const it of payload.addons) { + lines.push(`- ${it.nazwa} x${it.qty} @ ${moneyWithLabel(it.unit)}`); + } + } + + return lines.join("\n"); } - return lines.join("\n"); -} - -function saveOfferToLocalStorage() { - try { - const payload = buildOfferPayload(); - payload.message = buildOfferMessage(payload); // ✅ gotowy tekst - localStorage.setItem(LS_KEY, JSON.stringify(payload)); - } catch {} -} + function saveOfferToLocalStorage() { + try { + const payload = buildOfferPayload(); + payload.message = buildOfferMessage(payload); // ✅ gotowy tekst + localStorage.setItem(LS_KEY, JSON.stringify(payload)); + } catch { } + } // --------- @@ -596,38 +599,38 @@ function saveOfferToLocalStorage() { open={openSections.decoder} onToggle={() => toggleSection("decoder")} > - -{decodersList.length === 0 ? ( -

Brak dostępnych dekoderów.

-) : ( -
- {decodersList.map((d) => { - const isSelected = String(selectedDecoderId) === String(d.id); - return ( - + ); + })} +
+ )} @@ -666,77 +669,77 @@ function saveOfferToLocalStorage() { open={openSections.phone} onToggle={() => toggleSection("phone")} > - {phonePlans.length === 0 ? ( -

Brak dostępnych pakietów telefonicznych.

-) : ( -
- {/* brak telefonu */} -
-)} + )}
@@ -799,16 +802,16 @@ function saveOfferToLocalStorage() {
Łącznie {money(totalMonthly)} {cenaOpis} - +
saveOfferToLocalStorage()} -> - Wyślij zapytanie z tym wyborem - + href="/kontakt" + class="btn btn-primary w-full mt-4" + onClick={() => saveOfferToLocalStorage()} + > + Wyślij zapytanie z tym wyborem +
@@ -824,19 +827,25 @@ function saveOfferToLocalStorage() { */} -
e.stopPropagation()}> -
- - Razem - - - {money(totalMonthly)} - - - {cenaOpis} - -
-
+
e.stopPropagation()} + > +
+ + Razem + + + {money(totalMonthly)} + + + {cenaOpis} + +
+
diff --git a/src/islands/jambox/JamboxCards.jsx b/src/islands/jambox/JamboxCards.jsx index 844c1f4..f64f239 100644 --- a/src/islands/jambox/JamboxCards.jsx +++ b/src/islands/jambox/JamboxCards.jsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "preact/hooks"; -import "../../styles/addons.css"; +import "../../styles/cards.css"; import OffersSwitches from "../Switches.jsx"; import JamboxChannelsModal from "./JamboxChannelsModal.jsx"; diff --git a/src/islands/jambox/JamboxChannelsModal.jsx b/src/islands/jambox/JamboxChannelsModal.jsx index b6e91c9..59de332 100644 --- a/src/islands/jambox/JamboxChannelsModal.jsx +++ b/src/islands/jambox/JamboxChannelsModal.jsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from "preact/hooks"; import "../../styles/modal.css"; -import "../../styles/addons.css"; -import "../../styles/channels-search.css"; +import "../../styles/jambox-modal-channel.css"; +import "../../styles/jambox-search.css"; export default function JamboxChannelsModal({ isOpen, onClose, pkg }) { const [channels, setChannels] = useState([]); diff --git a/src/islands/jambox/JamboxChannelsSearch.jsx b/src/islands/jambox/JamboxChannelsSearch.jsx index a3a7df2..d154ffc 100644 --- a/src/islands/jambox/JamboxChannelsSearch.jsx +++ b/src/islands/jambox/JamboxChannelsSearch.jsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState } from "preact/hooks"; -import "../../styles/channels-search.css"; +import "../../styles/jambox-search.css"; export default function JamboxChannelsSearch() { const [q, setQ] = useState(""); diff --git a/src/islands/jambox/JamboxMozliwosciSearch.jsx b/src/islands/jambox/JamboxMozliwosciSearch.jsx index 1f37384..9936577 100644 --- a/src/islands/jambox/JamboxMozliwosciSearch.jsx +++ b/src/islands/jambox/JamboxMozliwosciSearch.jsx @@ -1,6 +1,6 @@ import { useMemo, useState } from "preact/hooks"; import { marked } from "marked"; -import "../../styles/channels-search.css"; +import "../../styles/jambox-search.css"; function norm(s) { return String(s || "") diff --git a/src/islands/phone/OffersPhoneCards.jsx b/src/islands/phone/OffersPhoneCards.jsx index d861064..f2d1e2d 100644 --- a/src/islands/phone/OffersPhoneCards.jsx +++ b/src/islands/phone/OffersPhoneCards.jsx @@ -1,5 +1,5 @@ import Markdown from "../../islands/Markdown.jsx"; -import "../../styles/addons.css"; +import "../../styles/cards.css"; /** * @typedef {{ klucz: string, label: string, value: (string|number) }} PhoneParam diff --git a/src/styles/addons.css b/src/styles/addons.css index df51316..67fbd2c 100644 --- a/src/styles/addons.css +++ b/src/styles/addons.css @@ -1,310 +1,463 @@ - .f-offers { - @apply my-6; + .f-section-acc .f-accordion-header { + @apply flex items-center justify-between gap-3; } - .f-offers-grid { - @apply flex flex-wrap justify-center gap-8; + .f-accordion-header-right { + @apply flex items-center gap-3; } - .f-card { - @apply bg-[--f-bg] text-[--f-text] border border-[--f-offers-border] rounded-2xl shadow-md p-6 relative flex flex-col gap-4; - /* przewijanie uwzględnia sticky navbar */ - scroll-margin-top: calc(var(--f-navbar-height, 84px) + 16px); - flex: 1 1 100%; - max-width: 26rem; - transition: transform 220ms ease, box-shadow 220ms ease; - } - - .f-card:hover { - @apply shadow-lg; - transform: translateY(-3px); + .f-acc-chevron { + @apply opacity-60 text-sm; } - .f-card-popular { - border: 2px solid var(--f-offers-popular); - background: var(--f-offers-popular-bg); + .f-floating-total { + @apply fixed bottom-5 right-5 z-[10000]; + @apply pointer-events-auto; + @apply select-none; + touch-action: none; } - .f-card-badge { - @apply absolute top-3 right-3 bg-[--btn-background] text-[--btn-text] text-sm font-semibold px-3 py-1 rounded-full; + /* kółko */ + .f-floating-total-circle { + @apply w-24 h-24 md:w-32 md:h-32 rounded-full; + @apply flex flex-col items-center justify-center text-center; + @apply shadow-xl; + @apply bg-[--f-addons-background]; + @apply backdrop-blur-md; } - /* HEADER */ - .f-card-header { - @apply flex flex-col items-start gap-1; + /* kwota */ + .f-floating-total-amount { + @apply text-lg md:text-xl font-bold leading-none text-[--f-addons-text]; } - .f-card-name { - @apply text-3xl font-semibold; + /* jednostka */ + .f-floating-total-unit { + @apply my-1 text-xs md:text-sm opacity-70 text-[--f-addons-text]; } - .f-card-price { - @apply text-3xl font-extrabold text-[--f-offers-price]; + + @keyframes fuz-bounce { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-6px); } } - /* FEATURES */ - .f-card-features { - @apply list-none p-0 m-0; + .f-floating-total:hover .f-floating-total-circle { + animation: fuz-bounce 420ms ease-in-out; } - .f-card-row { - @apply grid grid-cols-[2fr_1fr] gap-2 py-2 border-b border-[--f-offers-border] items-center; + .f-floating-total:active .f-floating-total-circle { + transform: translateY(-4px) scale(1.02); } - .f-card-row:last-child { +/* 3D */ +.f-floating-total-circle { + @apply relative overflow-hidden; + @apply shadow-xl; + @apply bg-[--f-addons-background]; + @apply backdrop-blur-md; + + /* 3D feel */ + box-shadow: + 0 18px 35px hsla(221 47% 11% / 0.28), + 0 6px 14px hsla(221 47% 11% / 0.18), + inset 0 1px 0 hsla(0 0% 100% / 0.22), + inset 0 -10px 18px hsla(221 47% 11% / 0.25); + + /* subtelna “kopuła” */ + background-image: + radial-gradient(120% 120% at 30% 20%, hsla(0 0% 100% / 0.22) 0%, transparent 55%), + radial-gradient(140% 140% at 70% 80%, hsla(221 47% 11% / 0.22) 0%, transparent 60%); + } + + /* połysk */ + .f-floating-total-circle::before { + content: ""; + position: absolute; + inset: -30% -30% auto -30%; + height: 70%; + border-radius: 9999px; + background: radial-gradient( + closest-side, + hsla(0 0% 100% / 0.28), + transparent 70% + ); + transform: rotate(-12deg); + pointer-events: none; + } + + /* “rim”/krawędź */ + .f-floating-total-circle::after { + content: ""; + position: absolute; + inset: 0; + border-radius: 9999px; + border: 1px solid hsla(0 0% 100% / 0.16); + box-shadow: inset 0 0 0 1px hsla(221 47% 11% / 0.18); + pointer-events: none; + } + + /* lekka reakcja 3D na hover */ + @media (hover: hover) and (pointer: fine) { + .f-floating-total:hover .f-floating-total-circle { + transform: translateY(-2px) scale(1.02); + box-shadow: + 0 22px 44px hsla(221 47% 11% / 0.32), + 0 8px 18px hsla(221 47% 11% / 0.20), + inset 0 1px 0 hsla(0 0% 100% / 0.24), + inset 0 -12px 20px hsla(221 47% 11% / 0.28); + transition: transform 180ms ease, box-shadow 180ms ease; + } + } + + + + + + /* ----------- Uporządkować ------ */ + /* =========================== + TELEFON — AKORDEON + =========================== */ + + .f-modal-phone-list.f-accordion { + @apply flex flex-col gap-2; + } + + .f-accordion-item { + @apply rounded-xl border overflow-hidden bg-[--f-background]; + border-color: rgba(148, 163, 184, 0.6); + } + + .f-accordion-header { + @apply w-full flex items-center justify-between gap-4 px-4 py-2 cursor-pointer; + background: rgba(148, 163, 184, 0.06); + border: none; + outline: none; + } + + .f-accordion-header-left { + @apply flex items-center gap-1; + } + + .f-modal-phone-name { + @apply font-medium ml-2; + } + + .f-modal-phone-price { + @apply font-semibold whitespace-nowrap; + } + + .f-accordion-body { + @apply px-4 pt-2 pb-3; + border-top: 1px solid rgba(148, 163, 184, 0.4); + } + + /* wyróżnienie otwartego pakietu */ + .f-accordion-item.is-open .f-accordion-header { + background: color-mix(in srgb, var(--fuz-accent, #2563eb) 8%, transparent); + } + + /* =========================== + DODATKI — KOLUMNOWA LISTA (GRID) + checkbox: checkbox | main | price + quantity: slot | main | qty | price + =========================== */ + + .f-addon-list { + @apply flex flex-col gap-2; + } + + /* BAZA: checkbox | main | price */ + .f-addon-item { + @apply grid items-start gap-3 px-3 py-2; + border-bottom: 1px solid rgba(148, 163, 184, 0.4); + /* rounded-xl border cursor-pointer; */ + grid-template-columns: auto 1fr auto; + /* border-color: rgba(148, 163, 184, 0.5); */ + background: var(--f-background); + @apply cursor-pointer; + } + + .f-addon-item * { + @apply cursor-pointer; + } + + .f-addon-item:last-child { border-bottom: none; } - .f-card-label { - @apply text-base font-medium opacity-80; + .f-addon-item:hover { + /* border-color: color-mix( + in srgb, + var(--fuz-accent, #2563eb) 70%, + rgba(148, 163, 184, 0.5) 30% + ); */ } - .f-card-value { - @apply text-base font-semibold text-right; + .f-addon-item input[type="checkbox"] { + @apply cursor-pointer; } - .f-card-value.yes { - @apply text-green-600; + /* kolumna 1 */ + .f-addon-checkbox { + @apply flex items-center justify-center; + align-items: center; + margin-top: 0.1rem; } - .f-card-value.no { - @apply text-red-600 opacity-60; - } - - /* INTELIGENTNY UKŁAD – OD MD↑ */ - @media (min-width: 768px) { - .f-card { - flex: 1 1 300px; - max-width: 360px; - } - - /* 4 karty → 2 + 2 */ - .f-count-4 .f-card { - flex: 0 1 calc(50% - 2rem); - max-width: 420px; - } - - /* 5 kart → 3 + 2 */ - .f-count-5 .f-card { - flex: 0 1 calc(33% - 2rem); - } - - .f-count-5 .f-card:nth-child(n + 4) { - flex: 0 1 calc(50% - 2rem); - max-width: 420px; - } - - /* 7 kart → 3 + 3 + 1 (środek) */ - .f-count-7 .f-card { - flex: 0 1 calc(33% - 2rem); - } - - .f-count-7 .f-card:last-child { - flex: 0 1 calc(33% - 2rem); - margin-left: auto; - margin-right: auto; - } - - /* 8 kart → 3 + 3 + 2 */ - .f-count-8 .f-card { - flex: 0 1 calc(33% - 2rem); - } - - .f-count-8 .f-card:nth-child(n + 7) { - flex: 0 1 calc(50% - 2rem); - max-width: 420px; - } - - /* 10 kart → 3 + 3 + 3 + 1 */ - .f-count-10 .f-card:last-child { - flex: 0 1 calc(33% - 2rem); - margin-left: auto; - margin-right: auto; - } - } - - /* ========================================= - TARGET — PODŚWIETLENIE PAKIETU PO KLIKNIĘCIU - ========================================== */ - - .f-card.is-target { - outline: none; - box-shadow: - 0 0 0 3px rgba(59, 130, 246, 0.45), - 0 10px 25px rgba(15, 23, 42, 0.18); - transform: translateY(-6px) scale(1.01); - position: relative; - background-color: var(--f-navbar-background); - z-index: 2; - } - - .f-card.is-target:hover { - transform: translateY(-6px) scale(1.01); - } - - /* JAMBOX — GRID KANAŁÓW (MODAL) */ - - .jmb-channels-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 0.9rem; - align-items: stretch; - } - - .jmb-channel-card { - position: relative; - perspective: 1000px; - - background: var(--f-bg); - color: var(--f-text); - - border-radius: 0.75rem; - border: 1px solid rgba(148, 163, 184, 0.25); - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.15); - - min-height: 170px; - height: 100%; - padding: 0; - overflow: hidden; - - -webkit-tap-highlight-color: transparent; - touch-action: manipulation; + .f-addon-checkbox input[type="checkbox"] { + width: 1.05rem; + height: 1.05rem; + transform: scale(1.05); + accent-color: var(--fuz-accent, #2563eb); cursor: pointer; } - .jmb-channel-inner { - position: relative; - width: 100%; - height: 100%; - transform-style: preserve-3d; - -webkit-transform-style: preserve-3d; - transition: transform 0.6s ease; + /* kolumna 2 */ + .f-addon-main { + @apply flex flex-col gap-0.5; + min-width: 0; } - /* DESKTOP: flip na hover */ - @media (hover: hover) and (pointer: fine) { - .jmb-channel-card:hover .jmb-channel-inner { - transform: rotateY(180deg); + .f-addon-name { + @apply font-medium; + } + + .f-addon-desc { + @apply text-sm opacity-85; + } + + /* kolumna 3 (cena) */ + .f-addon-price { + @apply font-semibold whitespace-nowrap; + justify-self: end; + text-align: right; + min-width: 140px; + /* stała kolumna cen */ + } + + /* suma pod ceną (quantity) */ + .f-addon-price-total { + margin-top: 0.15rem; + font-size: 0.9em; + font-weight: 600; + opacity: 0.85; + white-space: nowrap; + color: var(--fuz-accent, #2563eb); + } + + /* WARIANT: quantity -> slot | main | qty | price */ + .f-addon-item--qty { + grid-template-columns: auto 1fr auto auto; + align-items: start; + } + + /* “pusty” slot w kolumnie 1 (żeby wyrównać do checkboxa) */ + .f-addon-item--qty .f-addon-checkbox { + visibility: hidden; + /* zajmuje miejsce, ale nie widać */ + width: 1.05rem; + height: 1.05rem; + transform: scale(1.05); + } + + /* kolumna qty (3) – bliżej prawej */ + .f-addon-item--qty .f-addon-qty { + justify-self: end; + display: inline-flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.05rem; + } + + /* wartość qty */ + .f-addon-qty-value { + min-width: 2ch; + text-align: center; + } + + /* mobile: w razie ciasnoty przenosimy qty pod main, cena zostaje po prawej */ + @media (max-width: 640px) { + .f-addon-item--qty { + grid-template-columns: auto 1fr auto; + grid-template-areas: + "slot main price" + "slot qty price"; + } + + .f-addon-item--qty .f-addon-checkbox { + grid-area: slot; + } + + .f-addon-item--qty .f-addon-main { + grid-area: main; + } + + .f-addon-item--qty .f-addon-qty { + grid-area: qty; + justify-self: start; + } + + .f-addon-item--qty .f-addon-price { + grid-area: price; } } - /* MOBILE/TAP: flip po klasie */ - .jmb-channel-card.is-flipped .jmb-channel-inner { - transform: rotateY(180deg); + + /* =========================== + PODSUMOWANIE MIESIĘCZNE + =========================== */ + + .f-summary { + @apply pt-2; } - .jmb-channel-face { - position: absolute; - inset: 0; - - padding: 0.75rem 0.5rem; - - display: flex; - flex-direction: column; - justify-content: space-between; - text-align: center; - - backface-visibility: hidden; - -webkit-backface-visibility: hidden; + .f-summary-list { + @apply flex flex-col gap-1 mt-2 p-4 rounded-xl; + background: rgba(148, 163, 184, 0.07); } - .jmb-channel-front { - transform: rotateY(0deg); + .f-summary-row, + .f-summary-total { + @apply flex items-center justify-between; } - .jmb-channel-back { - transform: rotateY(180deg); - - text-align: left; - justify-content: flex-start; - gap: 0.5rem; + .f-summary-row span:last-child { + @apply font-medium whitespace-nowrap; } - .jmb-channel-logo { - max-width: 90px; - max-height: 60px; - margin: 0 auto 0.5rem auto; - display: block; - object-fit: contain; + .f-summary-total { + @apply mt-1 pt-2; + border-top: 1px solid rgba(148, 163, 184, 0.4); } - .jmb-channel-name { - font-size: 0.85rem; + .f-summary-total span:last-child { + @apply font-bold; + font-size: 1.25rem; + color: var(--fuz-accent, #2563eb); + } + + .f-modal-section { + @apply mb-6; + } + + .f-modal-section h3 { + @apply text-xl md:text-2xl font-semibold mb-3; + } + + /* opcja "bez telefonu" */ + .f-accordion-item--no-phone .f-accordion-header { + background: rgba(148, 163, 184, 0.03); + } + + .f-accordion-header-left input[type="radio"] { + width: 1.05rem; + height: 1.05rem; + transform: scale(1.05); + accent-color: var(--fuz-accent, #2563eb); + cursor: pointer; + } + + .f-addon-checkbox input[type="checkbox"] { + width: 1.05rem; + height: 1.05rem; + transform: scale(1.05); + accent-color: var(--fuz-accent, #2563eb); + cursor: pointer; + } + + .f-accordion-header-left, + .f-addon-checkbox { + align-items: center; + } + + + /* =========================== + FLOATING TOTAL (dymek jak czat) + =========================== */ + + .f-floating-total { + position: fixed; + right: 1rem; + bottom: 1rem; + z-index: 10000; + /* wyżej niż overlay (9999) */ + pointer-events: auto; + } + + .f-floating-total-inner { + @apply flex items-center gap-3; + @apply px-4 py-3 rounded-2xl shadow-xl border; + border-color: rgba(148, 163, 184, 0.5); + background: color-mix(in srgb, var(--f-background) 92%, transparent); + backdrop-filter: blur(10px); + } + + .f-floating-total-label { + @apply text-sm opacity-80; + } + + .f-floating-total-value { + @apply font-bold whitespace-nowrap; + font-size: 1.1rem; + color: var(--fuz-accent, #2563eb); + } + + /* na bardzo małych ekranach lekko mniejszy dymek */ + @media (max-width: 420px) { + .f-floating-total-inner { + @apply px-3 py-2; + } + + .f-floating-total-value { + font-size: 1rem; + } + } + + .f-addon-price-total { + margin-top: 0.15rem; + font-size: 0.9em; font-weight: 600; - margin-bottom: 0.15rem; - } - - .jmb-channel-number { - font-size: 0.75rem; - color: rgba(148, 163, 184, 0.95); - } - - .jmb-channel-back-title { - font-size: 0.9rem; - font-weight: 700; - } - - .jmb-channel-desc { - font-size: 0.85rem; - line-height: 1.35; - opacity: 0.92; - overflow: auto; - - scrollbar-width: thin; - scrollbar-color: rgba(148, 163, 184, 0.55) transparent; - } - - .jmb-channel-desc::-webkit-scrollbar { - width: 10px; - } - - .jmb-channel-desc::-webkit-scrollbar-thumb { - background: rgba(148, 163, 184, 0.45); - border-radius: 9999px; - border: 3px solid transparent; - background-clip: content-box; - } - - .jmb-channel-desc::-webkit-scrollbar-track { - background: transparent; + opacity: 0.85; + white-space: nowrap; + color: var(--fuz-accent, #2563eb); } - /* --------------------------------- */ - .f-section-acc .f-accordion-header { - @apply flex items-center justify-between gap-3; -} -.f-accordion-header-right { - @apply flex items-center gap-3; -} + /* -------------------------- */ + .f-radio-item { + @apply grid items-start gap-3 px-3 py-2 cursor-pointer; + grid-template-columns: auto 1fr auto; + border-bottom: 1px solid rgba(148, 163, 184, 0.4); + background: var(--f-background); + } -.f-acc-chevron { - @apply opacity-60 text-sm; -} + .f-radio-item:last-child { + border-bottom: none; + } -.f-floating-total { - @apply fixed bottom-5 right-5 z-[10000]; - @apply pointer-events-auto; -} + .f-radio-check input { + @apply mt-1; + } -/* kółko */ -.f-floating-total-circle { - @apply w-24 h-24 md:w-32 md:h-32 rounded-full; - @apply flex flex-col items-center justify-center text-center; - @apply shadow-xl ; - @apply bg-[--f-addons-background] ; - @apply backdrop-blur-md; -} + .f-radio-name { + @apply font-medium; + } -/* kwota */ -.f-floating-total-amount { - @apply text-lg md:text-xl font-bold leading-none text-[--f-addons-text]; -} + .f-radio-price { + @apply whitespace-nowrap font-semibold; + } -/* jednostka */ -.f-floating-total-unit { - @apply my-1 text-xs md:text-sm opacity-70 text-[--f-addons-text]; -} + .f-radio-item.is-selected { + /* delikatne wyróżnienie wybranego */ + /* @apply rounded-xl; */ + /* background: rgba(148, 163, 184, 0.12); */ + } + + .f-radio-details { + @apply pl-10 pr-3 pb-3 -mt-1 text-sm; + /* pl-10 = przesunięcie w prawo (radio + gap) */ + } \ No newline at end of file diff --git a/src/styles/cards.css b/src/styles/cards.css new file mode 100644 index 0000000..64c15f9 --- /dev/null +++ b/src/styles/cards.css @@ -0,0 +1,128 @@ +.f-offers { + @apply my-6; +} + +.f-offers-grid { + @apply flex flex-wrap justify-center gap-8; +} + +.f-card { + @apply bg-[--f-bg] text-[--f-text] border border-[--f-offers-border] rounded-2xl shadow-md p-6 relative flex flex-col gap-4; + scroll-margin-top: calc(var(--f-navbar-height, 84px) + 16px); + flex: 1 1 100%; + max-width: 26rem; + transition: transform 220ms ease, box-shadow 220ms ease; +} + +.f-card:hover { + @apply shadow-lg; + transform: translateY(-3px); +} + +.f-card-popular { + border: 2px solid var(--f-offers-popular); + background: var(--f-offers-popular-bg); +} + +.f-card-badge { + @apply absolute top-3 right-3 bg-[--btn-background] text-[--btn-text] text-sm font-semibold px-3 py-1 rounded-full; +} + +.f-card-header { + @apply flex flex-col items-start gap-1; +} + +.f-card-name { + @apply text-3xl font-semibold; +} + +.f-card-price { + @apply text-3xl font-extrabold text-[--f-offers-price]; +} + +.f-card-features { + @apply list-none p-0 m-0; +} + +.f-card-row { + @apply grid grid-cols-[2fr_1fr] gap-2 py-2 border-b border-[--f-offers-border] items-center; +} + +.f-card-row:last-child { + @apply border-b-0; +} + +.f-card-label { + @apply text-base font-medium opacity-80; +} + +.f-card-value { + @apply text-base font-semibold text-right; +} + +@media (min-width: 768px) { + .f-card { + flex: 1 1 300px; + max-width: 360px; + } + + /* 4 karty → 2 + 2 */ + .f-count-4 .f-card { + flex: 0 1 calc(50% - 2rem); + max-width: 420px; + } + + /* 5 kart → 3 + 2 */ + .f-count-5 .f-card { + flex: 0 1 calc(33% - 2rem); + } + + .f-count-5 .f-card:nth-child(n + 4) { + flex: 0 1 calc(50% - 2rem); + max-width: 420px; + } + + /* 7 kart → 3 + 3 + 1 (środek) */ + .f-count-7 .f-card { + flex: 0 1 calc(33% - 2rem); + } + + .f-count-7 .f-card:last-child { + flex: 0 1 calc(33% - 2rem); + margin-left: auto; + margin-right: auto; + } + + /* 8 kart → 3 + 3 + 2 */ + .f-count-8 .f-card { + flex: 0 1 calc(33% - 2rem); + } + + .f-count-8 .f-card:nth-child(n + 7) { + flex: 0 1 calc(50% - 2rem); + max-width: 420px; + } + + /* 10 kart → 3 + 3 + 3 + 1 */ + .f-count-10 .f-card:last-child { + flex: 0 1 calc(33% - 2rem); + margin-left: auto; + margin-right: auto; + } +} + + +.f-card.is-target { + outline: none; + box-shadow: + 0 0 0 3px var(--card-ring), + 0 10px 25px var(--card-shadow-deep); + transform: translateY(-6px) scale(1.01); + position: relative; + background-color: var(--f-navbar-background); + z-index: 2; +} + +.f-card.is-target:hover { + transform: translateY(-6px) scale(1.01); +} \ No newline at end of file diff --git a/src/styles/jambox-modal-channel.css b/src/styles/jambox-modal-channel.css new file mode 100644 index 0000000..462e0cb --- /dev/null +++ b/src/styles/jambox-modal-channel.css @@ -0,0 +1,124 @@ +.jmb-channels-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.9rem; + align-items: stretch; + } + + .jmb-channel-card { + position: relative; + perspective: 1000px; + + background: var(--f-bg); + color: var(--f-text); + + border-radius: 0.75rem; + border: 1px solid rgba(148, 163, 184, 0.25); + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.15); + + min-height: 170px; + height: 100%; + padding: 0; + overflow: hidden; + + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; + cursor: pointer; + } + + .jmb-channel-inner { + position: relative; + width: 100%; + height: 100%; + transform-style: preserve-3d; + -webkit-transform-style: preserve-3d; + transition: transform 0.6s ease; + } + + /* DESKTOP: flip na hover */ + @media (hover: hover) and (pointer: fine) { + .jmb-channel-card:hover .jmb-channel-inner { + transform: rotateY(180deg); + } + } + + /* MOBILE/TAP: flip po klasie */ + .jmb-channel-card.is-flipped .jmb-channel-inner { + transform: rotateY(180deg); + } + + .jmb-channel-face { + position: absolute; + inset: 0; + + padding: 0.75rem 0.5rem; + + display: flex; + flex-direction: column; + justify-content: space-between; + text-align: center; + + backface-visibility: hidden; + -webkit-backface-visibility: hidden; + } + + .jmb-channel-front { + transform: rotateY(0deg); + } + + .jmb-channel-back { + transform: rotateY(180deg); + + text-align: left; + justify-content: flex-start; + gap: 0.5rem; + } + + .jmb-channel-logo { + max-width: 90px; + max-height: 60px; + margin: 0 auto 0.5rem auto; + display: block; + object-fit: contain; + } + + .jmb-channel-name { + font-size: 0.85rem; + font-weight: 600; + margin-bottom: 0.15rem; + } + + .jmb-channel-number { + font-size: 0.75rem; + color: rgba(148, 163, 184, 0.95); + } + + .jmb-channel-back-title { + font-size: 0.9rem; + font-weight: 700; + } + + .jmb-channel-desc { + font-size: 0.85rem; + line-height: 1.35; + opacity: 0.92; + overflow: auto; + + scrollbar-width: thin; + scrollbar-color: rgba(148, 163, 184, 0.55) transparent; + } + + .jmb-channel-desc::-webkit-scrollbar { + width: 10px; + } + + .jmb-channel-desc::-webkit-scrollbar-thumb { + background: rgba(148, 163, 184, 0.45); + border-radius: 9999px; + border: 3px solid transparent; + background-clip: content-box; + } + + .jmb-channel-desc::-webkit-scrollbar-track { + background: transparent; + } diff --git a/src/styles/channels-search.css b/src/styles/jambox-search.css similarity index 100% rename from src/styles/channels-search.css rename to src/styles/jambox-search.css diff --git a/src/styles/modal.css b/src/styles/modal.css index 7ec070a..bab7a27 100644 --- a/src/styles/modal.css +++ b/src/styles/modal.css @@ -1,7 +1,3 @@ -/* =========================== - MODAL — FULLSCREEN OVERLAY - =========================== */ - .f-modal-overlay { @apply fixed inset-0 z-[9999] flex flex-col; background: rgba(0, 0, 0, 0.65); @@ -18,13 +14,12 @@ @apply opacity-100; } -/* panel – pełny ekran, ale treść centrowana max-w */ .f-modal-panel { @apply w-full h-full overflow-y-auto bg-[--f-background] text-[--f-text]; @apply px-6 py-8 md:px-12 md:py-12; } -/* wersja "kompaktowa" z mniejszym max-width (używana w dodatkach) */ + .f-modal-panel.f-modal-panel--compact { @apply flex justify-center items-start; } @@ -50,330 +45,3 @@ to { opacity: 1; } } -/* =========================== - TELEFON — AKORDEON - =========================== */ - -.f-modal-phone-list.f-accordion { - @apply flex flex-col gap-2; -} - -.f-accordion-item { - @apply rounded-xl border overflow-hidden bg-[--f-background]; - border-color: rgba(148, 163, 184, 0.6); -} - -.f-accordion-header { - @apply w-full flex items-center justify-between gap-4 px-4 py-2 cursor-pointer; - background: rgba(148, 163, 184, 0.06); - border: none; - outline: none; -} - -.f-accordion-header-left { - @apply flex items-center gap-1; -} - -.f-modal-phone-name { - @apply font-medium ml-2; -} - -.f-modal-phone-price { - @apply font-semibold whitespace-nowrap; -} - -.f-accordion-body { - @apply px-4 pt-2 pb-3; - border-top: 1px solid rgba(148, 163, 184, 0.4); -} - -/* wyróżnienie otwartego pakietu */ -.f-accordion-item.is-open .f-accordion-header { - background: color-mix(in srgb, var(--fuz-accent, #2563eb) 8%, transparent); -} - -/* =========================== - DODATKI — KOLUMNOWA LISTA (GRID) - checkbox: checkbox | main | price - quantity: slot | main | qty | price - =========================== */ - -.f-addon-list { - @apply flex flex-col gap-2; -} - -/* BAZA: checkbox | main | price */ -.f-addon-item { - @apply grid items-start gap-3 px-3 py-2; - border-bottom: 1px solid rgba(148, 163, 184, 0.4); - /* rounded-xl border cursor-pointer; */ - grid-template-columns: auto 1fr auto; - /* border-color: rgba(148, 163, 184, 0.5); */ - background: var(--f-background); - @apply cursor-pointer; -} -.f-addon-item * { @apply cursor-pointer; } - -.f-addon-item:last-child { - border-bottom: none; -} - -.f-addon-item:hover { - /* border-color: color-mix( - in srgb, - var(--fuz-accent, #2563eb) 70%, - rgba(148, 163, 184, 0.5) 30% - ); */ -} - -.f-addon-item input[type="checkbox"] { - @apply cursor-pointer; -} - -/* kolumna 1 */ -.f-addon-checkbox { - @apply flex items-center justify-center; - align-items: center; - margin-top: 0.1rem; -} - -.f-addon-checkbox input[type="checkbox"] { - width: 1.05rem; - height: 1.05rem; - transform: scale(1.05); - accent-color: var(--fuz-accent, #2563eb); - cursor: pointer; -} - -/* kolumna 2 */ -.f-addon-main { - @apply flex flex-col gap-0.5; - min-width: 0; -} - -.f-addon-name { - @apply font-medium; -} - -.f-addon-desc { - @apply text-sm opacity-85; -} - -/* kolumna 3 (cena) */ -.f-addon-price { - @apply font-semibold whitespace-nowrap; - justify-self: end; - text-align: right; - min-width: 140px; /* stała kolumna cen */ -} - -/* suma pod ceną (quantity) */ -.f-addon-price-total { - margin-top: 0.15rem; - font-size: 0.9em; - font-weight: 600; - opacity: 0.85; - white-space: nowrap; - color: var(--fuz-accent, #2563eb); -} - -/* WARIANT: quantity -> slot | main | qty | price */ -.f-addon-item--qty { - grid-template-columns: auto 1fr auto auto; - align-items: start; -} - -/* “pusty” slot w kolumnie 1 (żeby wyrównać do checkboxa) */ -.f-addon-item--qty .f-addon-checkbox { - visibility: hidden; /* zajmuje miejsce, ale nie widać */ - width: 1.05rem; - height: 1.05rem; - transform: scale(1.05); -} - -/* kolumna qty (3) – bliżej prawej */ -.f-addon-item--qty .f-addon-qty { - justify-self: end; - display: inline-flex; - align-items: center; - gap: 0.5rem; - margin-top: 0.05rem; -} - -/* wartość qty */ -.f-addon-qty-value { - min-width: 2ch; - text-align: center; -} - -/* mobile: w razie ciasnoty przenosimy qty pod main, cena zostaje po prawej */ -@media (max-width: 640px) { - .f-addon-item--qty { - grid-template-columns: auto 1fr auto; - grid-template-areas: - "slot main price" - "slot qty price"; - } - - .f-addon-item--qty .f-addon-checkbox { grid-area: slot; } - .f-addon-item--qty .f-addon-main { grid-area: main; } - .f-addon-item--qty .f-addon-qty { grid-area: qty; justify-self: start; } - .f-addon-item--qty .f-addon-price { grid-area: price; } -} - - -/* =========================== - PODSUMOWANIE MIESIĘCZNE - =========================== */ - -.f-summary { - @apply pt-2; -} - -.f-summary-list { - @apply flex flex-col gap-1 mt-2 p-4 rounded-xl; - background: rgba(148, 163, 184, 0.07); -} - -.f-summary-row, -.f-summary-total { - @apply flex items-center justify-between; -} - -.f-summary-row span:last-child { - @apply font-medium whitespace-nowrap; -} - -.f-summary-total { - @apply mt-1 pt-2; - border-top: 1px solid rgba(148, 163, 184, 0.4); -} - -.f-summary-total span:last-child { - @apply font-bold; - font-size: 1.25rem; - color: var(--fuz-accent, #2563eb); -} - -.f-modal-section { - @apply mb-6; -} - -.f-modal-section h3 { - @apply text-xl md:text-2xl font-semibold mb-3; -} - -/* opcja "bez telefonu" */ -.f-accordion-item--no-phone .f-accordion-header { - background: rgba(148, 163, 184, 0.03); -} - -.f-accordion-header-left input[type="radio"] { - width: 1.05rem; - height: 1.05rem; - transform: scale(1.05); - accent-color: var(--fuz-accent, #2563eb); - cursor: pointer; -} - -.f-addon-checkbox input[type="checkbox"] { - width: 1.05rem; - height: 1.05rem; - transform: scale(1.05); - accent-color: var(--fuz-accent, #2563eb); - cursor: pointer; -} - -.f-accordion-header-left, -.f-addon-checkbox { - align-items: center; -} - - -/* =========================== - FLOATING TOTAL (dymek jak czat) - =========================== */ - -.f-floating-total { - position: fixed; - right: 1rem; - bottom: 1rem; - z-index: 10000; /* wyżej niż overlay (9999) */ - pointer-events: auto; -} - -.f-floating-total-inner { - @apply flex items-center gap-3; - @apply px-4 py-3 rounded-2xl shadow-xl border; - border-color: rgba(148, 163, 184, 0.5); - background: color-mix(in srgb, var(--f-background) 92%, transparent); - backdrop-filter: blur(10px); -} - -.f-floating-total-label { - @apply text-sm opacity-80; -} - -.f-floating-total-value { - @apply font-bold whitespace-nowrap; - font-size: 1.1rem; - color: var(--fuz-accent, #2563eb); -} - -/* na bardzo małych ekranach lekko mniejszy dymek */ -@media (max-width: 420px) { - .f-floating-total-inner { - @apply px-3 py-2; - } - .f-floating-total-value { - font-size: 1rem; - } -} - -.f-addon-price-total { - margin-top: 0.15rem; - font-size: 0.9em; - font-weight: 600; - opacity: 0.85; - white-space: nowrap; - color: var(--fuz-accent, #2563eb); -} - - - - -/* -------------------------- */ -.f-radio-item { - @apply grid items-start gap-3 px-3 py-2 cursor-pointer; - grid-template-columns: auto 1fr auto; - border-bottom: 1px solid rgba(148, 163, 184, 0.4); - background: var(--f-background); -} - -.f-radio-item:last-child { - border-bottom: none; -} - -.f-radio-check input { - @apply mt-1; -} - -.f-radio-name { - @apply font-medium; -} - -.f-radio-price { - @apply whitespace-nowrap font-semibold; -} - -.f-radio-item.is-selected { - /* delikatne wyróżnienie wybranego */ - /* @apply rounded-xl; */ - /* background: rgba(148, 163, 184, 0.12); */ -} - -.f-radio-details { - @apply pl-10 pr-3 pb-3 -mt-1 text-sm; - /* pl-10 = przesunięcie w prawo (radio + gap) */ -} - diff --git a/src/styles/theme.css b/src/styles/theme.css index a75a433..a2f27ff 100644 --- a/src/styles/theme.css +++ b/src/styles/theme.css @@ -40,6 +40,9 @@ --surface5-dark: hsla(200, 35%, 25%, 1); --surface6-dark: hsla(205, 51%, 41%, 1); + + --card-ring: hsla(217 91% 60% / 0.45); + --card-shadow-deep: hsla(221 47% 11% / 0.18); /* @@ -54,7 +57,7 @@ --cookie-accept-dark: hsla(120, 60%, 45%, 1); --f-navbar-height: 84px; - /* Wyrózznienie ceny */ + /* Wyróznienie ceny */ --f-addons-text: hsla(45, 100%, 92%, 1); --f-addons-background: hsla(165, 80%, 25%, 1);