diff --git a/app/klimatyzacja/[miasto]/page.tsx b/app/klimatyzacja/[miasto]/page.tsx index b7c7bc4..e9510cf 100644 --- a/app/klimatyzacja/[miasto]/page.tsx +++ b/app/klimatyzacja/[miasto]/page.tsx @@ -71,7 +71,7 @@ export default async function MiastoPage({ params }: Props) { {/* HERO */}

- Profesjonalny montaż klimatyzacji w {city.nameDopelniacz} + Profesjonalny montaż klimatyzacji {city.przyimek} {city.nameDopelniacz}

{city.introText}

{city.localFacts}

@@ -106,7 +106,7 @@ export default async function MiastoPage({ params }: Props) {

Czas montażu

-

Standardowy montaż split w {city.nameDopelniacz} trwa 4–8 godzin. Multisplit — 1–2 dni robocze.

+

Standardowy montaż split {city.przyimek} {city.nameDopelniacz} trwa 4–8 godzin. Multisplit — 1–2 dni robocze.

@@ -144,7 +144,7 @@ export default async function MiastoPage({ params }: Props) { @@ -161,7 +161,7 @@ export default async function MiastoPage({ params }: Props) { Pompy ciepła {city.name}

- Pompa ciepła w {city.nameDopelniacz} + Pompa ciepła {city.przyimek} {city.nameDopelniacz}

{city.heatPumpTeaser}

@@ -243,7 +243,7 @@ export default async function MiastoPage({ params }: Props) { {/* KOŃCOWE CTA */} ('klimatyzacja'); @@ -46,21 +46,42 @@ export function KalkulatorClient({ products }: Props) { const [pumpForm, setPumpForm] = useState({ name: '', phone: '', city: '', message: '' }); const [step, setStep] = useState(0); const [building, setBuilding] = useState('apartment'); - const [m2, setM2] = useState(25); + const [pomieszczenia, setPomieszczenia] = useState<{ nazwa: string; m2: number }[]>([ + { nazwa: 'Pokój 1', m2: 20 } + ]); const [floor, setFloor] = useState('middle'); // ground | middle | top const [sun, setSun] = useState('south'); // north | east | south | west - - const [systemType, setSystemType] = useState<'auto' | 'scienne' | 'multisplit'>('auto'); - const [rooms, setRooms] = useState(1); + + const addRoom = () => { + if (pomieszczenia.length < 5) { + setPomieszczenia(prev => [...prev, { nazwa: `Pokój ${prev.length + 1}`, m2: 15 }]); + } + }; + + const removeRoom = (i: number) => { + if (pomieszczenia.length > 1) { + setPomieszczenia(prev => prev.filter((_, idx) => idx !== i)); + } + }; + + const updateRoom = (i: number, m2: number) => { + setPomieszczenia(prev => prev.map((r, idx) => idx === i ? { ...r, m2 } : r)); + }; const [pipes, setPipes] = useState('medium'); // short(<3m) | medium(3-6m) | long(6-10m) | verylong(>10m) const [budget, setBudget] = useState(8000); const [showResults, setShowResults] = useState(false); const [preferredBrand, setPreferredBrand] = useState(''); - const requiredPower = useMemo(() => calculatePower(m2, floor, sun, building), [m2, floor, sun, building]); + const totalM2 = pomieszczenia.reduce((sum, p) => sum + p.m2, 0); + const rooms = pomieszczenia.length; + const requiredPower = useMemo(() => + pomieszczenia.reduce((sum, p) => sum + calculatePower(p.m2, floor, sun, building), 0), + [pomieszczenia, floor, sun, building] + ); + const requiredPowerRounded = Math.round(requiredPower * 10) / 10; const installCost = useMemo(() => estimateInstall(pipes, rooms), [pipes, rooms]); - const category = systemType === 'auto' ? (rooms === 1 ? 'scienne' : 'multisplit') : systemType; + const category = rooms === 1 ? 'scienne' : 'multisplit'; const results = useMemo(() => { if (!showResults || deviceType === 'pompa') return []; @@ -151,8 +172,8 @@ export function KalkulatorClient({ products }: Props) {
{[ - { label: 'Wymagana moc', value: `${requiredPower} kW` }, - { label: 'Powierzchnia', value: `${m2} m²` }, + { label: 'Wymagana moc', value: `${requiredPowerRounded} kW` }, + { label: 'Powierzchnia', value: `${totalM2} m²` }, { label: 'Typ instalacji', value: category === 'scienne' ? 'Split (1 pokój)' : `Multisplit (${rooms} pokoje)` }, { label: 'Szac. montaż', value: `${installCost.min.toLocaleString('pl-PL')}–${installCost.max.toLocaleString('pl-PL')} zł` }, ].map((s, i) => ( @@ -410,35 +431,80 @@ export function KalkulatorClient({ products }: Props) {
-
-
- Powierzchnia - {m2} m² -
-
-
-
- setM2(Number(e.target.value))} - className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10" - /> -
-
-
- 10 m² - 100 m² -
-
- Wymagana moc: ok. {requiredPower} kW - (dla {m2} m²) -
+ {/* Lista pomieszczeń */} +
+ {pomieszczenia.map((p, i) => ( +
+
+ {p.nazwa} + {pomieszczenia.length > 1 && ( + + )} +
+
+
+ Powierzchnia + {p.m2} m² +
+
+
+
+ updateRoom(i, Number(e.target.value))} + className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10" + /> +
+
+
+ 5 m² + 100 m² +
+ {(() => { + const mocPokoju = Math.round(calculatePower(p.m2, floor, sun, building) * 10) / 10; + const dostepneMoce = [2.5, 3.5, 5.0, 7.0]; + const zalecana = dostepneMoce.reduce((prev, curr) => + Math.abs(curr - mocPokoju) < Math.abs(prev - mocPokoju) ? curr : prev + ); + return ( +
+ Zalecana moc jednostki + {zalecana} kW +
+ ); + })()} +
+
+ ))} +
+ + {pomieszczenia.length < 5 && ( + + )} + +
+

+ Łączna wymagana moc: ok. {requiredPowerRounded} kW + + ({pomieszczenia.length === 1 ? 'klimatyzator split' : `multisplit ${pomieszczenia.length}+1`}, łącznie {totalM2} m²) + +

@@ -513,64 +579,8 @@ export function KalkulatorClient({ products }: Props) {
)} - {/* Krok 2: Konfiguracja */} + {/* Krok 2: Instalacja */} {step === 2 && ( -
-
-
- -
-
-

Konfiguracja

-

Ilość pomieszczeń i typ instalacji

-
-
- -
- -
- {[ - { value: 'auto', label: 'Dobierz automatycznie' }, - { value: 'scienne', label: 'Split (1 agregat = 1 pokój)' }, - { value: 'multisplit', label: 'Multisplit (1 agregat = wiele)' }, - ].map(o => ( - - ))} -
-
- -
- -
- {[ - { value: 1, label: '1 pomieszczenie', sub: 'Pojedynczy pokój', icon: '🏠' }, - { value: 2, label: '2 pomieszczenia', sub: 'Dwa pokoje', icon: '🏡' }, - { value: 3, label: '3 pomieszczenia', sub: 'Trzy pokoje', icon: '🏘️' }, - { value: 4, label: '4-5 pomieszczeń', sub: 'Wiele pokoi', icon: '🏗️' }, - ].map(o => ( - - ))} -
-
- -
- {category === 'scienne' - ? '✅ Wybrano: Klimatyzator split — jedna jednostka wewnętrzna i jedna zewnętrzna. Dedykowane dla jednego pokoju.' - : `✅ Wybrano: System multisplit — jedna jednostka zewnętrzna obsłuży ${rooms} jednostki wewnętrzne. Lepsza estetyka na zewnątrz budynku.`} -
-
- )} - - {/* Krok 3: Instalacja */} - {step === 3 && (
@@ -628,8 +638,8 @@ export function KalkulatorClient({ products }: Props) {
)} - {/* Krok 4: Budżet */} - {step === 4 && ( + {/* Krok 3: Budżet */} + {step === 3 && (
@@ -684,7 +694,7 @@ export function KalkulatorClient({ products }: Props) {
- 💡 Podsumowanie: szukamy klimatyzatora {category === 'scienne' ? 'split ściennego' : 'multisplit'} o mocy ok. {requiredPower} kW w cenie do {(budget * 0.75).toLocaleString('pl-PL')} zł + 💡 Podsumowanie: szukamy klimatyzatora {category === 'scienne' ? 'split ściennego' : 'multisplit'} o mocy ok. {requiredPowerRounded} kW w cenie do {(budget * 0.75).toLocaleString('pl-PL')} zł
diff --git a/components/sections/HomeServices.tsx b/components/sections/HomeServices.tsx new file mode 100644 index 0000000..86ecec9 --- /dev/null +++ b/components/sections/HomeServices.tsx @@ -0,0 +1,153 @@ +import Link from "next/link"; +import { Wrench, Thermometer, Wind, Zap, Calendar, Phone, Check } from "lucide-react"; +import { companyDetails } from "@/content/company"; + +export function HomeServices() { + return ( +
+
+
+
+
+ + Cennik usług +
+

+ Montaż, serwis i czyszczenie klimatyzacji –
już od 150 zł +

+

+ Profesjonalna obsługa, certyfikowani specjaliści i szybki czas reakcji na terenie całego Wrocławia. Zadbaj o czyste powietrze i wydajność swoich urządzeń. +

+
+ + {/* Cards Grid */} +
+ + {/* Card 1: Montaż klimatyzacji */} +
+
+
+ +
+
+

Montaż klimatyzacji

+

Profesjonalny montaż urządzeń split i multisplit. Zapewniamy estetykę wykonania i pełne uruchomienie.

+
+
+
+

Cena usługi

+

od 1400 zł

+
+
+ + {/* Card 2: Czyszczenie */} +
+
+
+ +
+
+

Czyszczenie i odgrzybianie

+

Kompleksowe czyszczenie, dezynfekcja chemiczna, odgrzybianie parowe, czyszczenie filtrów.

+
+
+
+

Cena usługi

+

od 250 zł

+
+
+ + {/* Card 3: Przegląd techniczny */} +
+
+
+ +
+
+

Przegląd techniczny

+

Sprawdzenie parametrów pracy, kontrola szczelności układu, pomiar ciśnień roboczych.

+
+
+
+

Cena usługi

+

od 150 zł

+
+
+ + {/* Card 4: Uzupełnienie czynnika */} +
+
+
+ +
+
+

Uzupełnienie czynnika

+

Lokalizacja nieszczelności, próba ciśnieniowa azotem, dobicie czynnika chłodniczego.

+
+
+
+

Cena usługi

+

od 180 zł

+
+
+ + {/* Card 5: Naprawa usterek */} +
+
+
+ +
+
+

Naprawa usterek

+

Diagnoza awarii, wymiana uszkodzonych podzespołów elektroniki, sprężarek, wentylatorów.

+
+
+
+

Cena usługi

+

Wycena indywidualna

+
+
+ + {/* Card 6: Serwis sezonowy (Dark card) */} +
+
+
+ +
+
+

Serwis sezonowy / Abonament

+

Regularna opieka nad instalacją przed sezonem letnim i zimowym. Priorytetowy czas reakcji w przypadku awarii.

+
+
+
+

Cena usługi

+

od 199 zł / rok

+
+
+ + {/* Card 6: Gotowy na świeże powietrze? (Full width) */} +
+
+

Gotowy na świeże powietrze?

+

Nasi certyfikowani technicy są dostępni od poniedziałku do soboty. Zarezerwuj termin już dziś, aby uniknąć kolejek w sezonie.

+
+
+ + + + + + +
+
+ +
+
+
+
+ ); +} diff --git a/content/cities.ts b/content/cities.ts index b5c6768..6ce70f8 100644 --- a/content/cities.ts +++ b/content/cities.ts @@ -1,4 +1,5 @@ export interface CityData { + przyimek: string; slug: string; name: string; nameDopelniacz: string; @@ -20,6 +21,7 @@ export interface CityData { export const dolnySlaskCities: CityData[] = [ { + przyimek: "we", slug: "wroclaw", name: "Wrocław", nameDopelniacz: "Wrocławiu", @@ -29,7 +31,7 @@ export const dolnySlaskCities: CityData[] = [ distanceFromHQ: "siedziba firmy", lat: 51.1079, lng: 17.0385, - heroImage: "https://images.unsplash.com/photo-1585155784229-aff921ccfa22?w=1600&q=80", + heroImage: "/images/cities/wroclaw.webp", seoTitle: "Klimatyzacja Wrocław — Montaż i Serwis | ThermCool", seoDescription: "Montaż klimatyzacji we Wrocławiu od specjalistów z uprawnieniami F-Gaz. Bezpłatny audyt, split i multisplit, serwis gwarancyjny. Zadzwoń po wycenę!", heroSubtitle: "Profesjonalny montaż klimatyzacji we Wrocławiu — split, multisplit, kasetonowe. Bezpłatny audyt, estetyczna instalacja, pełna gwarancja.", @@ -39,6 +41,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Planujesz ogrzewanie pompy ciepła we Wrocławiu? Pomagamy w doborze, montażu i uzyskaniu dofinansowania z programu Czyste Powietrze. Realizujemy instalacje powietrze-woda na terenie całego Wrocławia i powiatu wrocławskiego.", }, { + przyimek: "w", slug: "legnica", name: "Legnica", nameDopelniacz: "Legnicy", @@ -58,6 +61,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Interesuje Cię pompa ciepła w Legnicy? Dobieramy urządzenia dopasowane do zapotrzebowania budynku, pomagamy uzyskać dofinansowanie z Czystego Powietrza i realizujemy kompletną instalację powietrze-woda.", }, { + przyimek: "w", slug: "walbrzych", name: "Wałbrzych", nameDopelniacz: "Wałbrzychu", @@ -77,6 +81,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Wałbrzychu to inwestycja szczególnie opłacalna — wyższe położenie i chłodniejszy klimat sprawiają że urządzenia pracują wydajniej. Pomożemy w doborze, montażu i uzyskaniu dotacji z Czystego Powietrza.", }, { + przyimek: "w", slug: "jelenia-gora", name: "Jelenia Góra", nameDopelniacz: "Jeleniej Górze", @@ -96,6 +101,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Jeleniej Górze — region górski to idealne warunki dla nowoczesnych pomp ciepła, które pracują wydajnie nawet w niskich temperaturach. Realizujemy instalacje z pełną pomocą w uzyskaniu dofinansowania.", }, { + przyimek: "w", slug: "lubin", name: "Lubin", nameDopelniacz: "Lubinie", @@ -115,6 +121,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Lubinie — właściciele domów jednorodzinnych w rejonie Lubina coraz chętniej wymieniają kotły gazowe i węglowe na pompy ciepła. Pomagamy w całym procesie — od audytu przez montaż po dotację.", }, { + przyimek: "w", slug: "glogow", name: "Głogów", nameDopelniacz: "Głogowie", @@ -134,6 +141,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Głogowie i powiecie głogowskim — pomagamy właścicielom domów jednorodzinnych przejść z ogrzewania węglowego na ekologiczne i tańsze w eksploatacji pompy ciepła powietrze-woda.", }, { + przyimek: "w", slug: "swidnica", name: "Świdnica", nameDopelniacz: "Świdnicy", @@ -153,6 +161,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Świdnicy i powiecie świdnickim — realizujemy instalacje powietrze-woda z pełnym wsparciem w procesie aplikacji o dofinansowanie z programu Czyste Powietrze.", }, { + przyimek: "w", slug: "dzierzoniow", name: "Dzierżoniów", nameDopelniacz: "Dzierżoniowie", @@ -172,6 +181,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Dzierżoniowie — dobieramy urządzenia dostosowane do wielkości i izolacji budynku. Pomagamy w uzyskaniu dotacji z Czystego Powietrza, co znacząco obniża koszt inwestycji.", }, { + przyimek: "w", slug: "bielawa", name: "Bielawa", nameDopelniacz: "Bielawie", @@ -191,6 +201,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Bielawie — dla właścicieli domów jednorodzinnych to szansa na uniezależnienie się od rosnących cen gazu i węgla. Realizujemy instalacje z pomocą w dotacjach z Czystego Powietrza.", }, { + przyimek: "w", slug: "olesnica", name: "Oleśnica", nameDopelniacz: "Oleśnicy", @@ -210,6 +221,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Oleśnicy — bliskość Wrocławia i dobra dostępność naszego serwisu sprawiają że obsługa instalacji jest wyjątkowo komfortowa. Realizujemy instalacje powietrze-woda z pełnym pakietem dofinansowania.", }, { + przyimek: "w", slug: "olawa", name: "Oława", nameDopelniacz: "Oławie", @@ -229,6 +241,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Oławie — realizujemy instalacje dla domów jednorodzinnych w Oławie i całym powiecie oławskim. Krótki dojazd to też sprawny serwis przez cały okres użytkowania urządzenia.", }, { + przyimek: "w", slug: "strzelin", name: "Strzelin", nameDopelniacz: "Strzelinie", @@ -248,6 +261,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Strzelinie — powiat strzeliński to rejon gdzie wiele domów nadal ogrzewa się węglem. Pompa ciepła to ekologiczna i ekonomiczna alternatywa, a dofinansowanie z Czystego Powietrza czyni ją realnie dostępną.", }, { + przyimek: "w", slug: "sroda-slaska", name: "Środa Śląska", nameDopelniacz: "Środzie Śląskiej", @@ -267,6 +281,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Środzie Śląskiej — przy nowych domach jednorodzinnych w tej okolicy pompa ciepła jest często wybierana jako podstawowe źródło ogrzewania. Pomagamy w całym procesie inwestycji.", }, { + przyimek: "w", slug: "trzebnica", name: "Trzebnica", nameDopelniacz: "Trzebnicy", @@ -286,6 +301,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Trzebnicy — obsługujemy zarówno prywatne domy jak i większe obiekty. Bliskość Wrocławia to szybki serwis i krótkie czasy reakcji przez cały okres użytkowania pompy.", }, { + przyimek: "w", slug: "siechnice", name: "Siechnice", nameDopelniacz: "Siechnicach", @@ -305,6 +321,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Pompa ciepła w Siechnicach — przy nowych domach w tej gminie pompa ciepła jest standardowym wyborem. Realizujemy instalacje powietrze-woda i gruntowe, doradzamy w wyborze optymalnego rozwiązania dla danej działki.", }, { + przyimek: "w", slug: "opole", name: "Opole", nameDopelniacz: "Opolu", @@ -324,6 +341,7 @@ export const dolnySlaskCities: CityData[] = [ heatPumpTeaser: "Interesuje Cię pompa ciepła w Opolu? Realizujemy instalacje powietrze-woda z pomocą w uzyskaniu dofinansowania z programu Czyste Powietrze. Skontaktuj się z nami — dobierzemy optymalne rozwiązanie dla Twojego budynku.", }, { + przyimek: "w", slug: "klodzko", name: "Kłodzko", nameDopelniacz: "Kłodzku", diff --git a/content/pricing.ts b/content/pricing.ts index 95d18fc..f21435d 100644 --- a/content/pricing.ts +++ b/content/pricing.ts @@ -10,39 +10,63 @@ export interface PricingItem { export const pricingData: PricingItem[] = [ { id: 'montaz-split-podstawowy', - name: 'Montaż klimatyzacji typu SPLIT (do 3.5 kW)', - priceNet: 1200, - priceGrossPrivate: 1296, - priceGrossCompany: 1476, + name: 'Montaż klimatyzatora split — podstawowy (do 4,5 kW)', + priceNet: 1400, + priceGrossPrivate: 1512, + priceGrossCompany: 1722, category: 'split', }, { - id: 'montaz-split-rozszerzony', - name: 'Montaż klimatyzacji typu SPLIT (powyżej 3.5 kW)', - priceNet: 1500, - priceGrossPrivate: 1620, - priceGrossCompany: 1845, + id: 'montaz-split-istniejaca', + name: 'Montaż klimatyzatora split przy istniejącej instalacji', + priceNet: 800, + priceGrossPrivate: 864, + priceGrossCompany: 984, category: 'split', }, { id: 'montaz-multisplit-2', - name: 'Montaż klimatyzacji MULTISPLIT (2 jednostki wew.)', - priceNet: 2200, - priceGrossPrivate: 2376, - priceGrossCompany: 2706, + name: 'Montaż multisplit 2+1 — podstawowy (do 6 m instalacji)', + priceNet: 2400, + priceGrossPrivate: 2592, + priceGrossCompany: 2952, category: 'multisplit', }, { id: 'montaz-multisplit-3', - name: 'Montaż klimatyzacji MULTISPLIT (3 jednostki wew.)', - priceNet: 3000, - priceGrossPrivate: 3240, - priceGrossCompany: 3690, + name: 'Montaż multisplit 3+1 — podstawowy (do 9 m instalacji)', + priceNet: 3200, + priceGrossPrivate: 3456, + priceGrossCompany: 3936, category: 'multisplit', }, { - id: 'dodatkowy-metr', - name: 'Dodatkowy metr instalacji chłodniczej', + id: 'montaz-multisplit-4', + name: 'Montaż multisplit 4+1 — podstawowy (do 12 m instalacji)', + priceNet: 3800, + priceGrossPrivate: 4104, + priceGrossCompany: 4674, + category: 'multisplit', + }, + { + id: 'dodatkowy-metr-korytko', + name: 'Dodatkowy metr instalacji freonowej — korytko PCV', + priceNet: 80, + priceGrossPrivate: 86, + priceGrossCompany: 98, + category: 'additional', + }, + { + id: 'dodatkowy-metr-bruzda', + name: 'Dodatkowy metr instalacji freonowej — bruzda w ścianie', + priceNet: 90, + priceGrossPrivate: 97, + priceGrossCompany: 111, + category: 'additional', + }, + { + id: 'przewiert', + name: 'Dopłata za przewiert otworu instalacyjnego w betonie', priceNet: 100, priceGrossPrivate: 108, priceGrossCompany: 123, @@ -50,18 +74,34 @@ export const pricingData: PricingItem[] = [ }, { id: 'kucie-bruzdy', - name: 'Kucie bruzdy w betonie/żelbecie (mb)', + name: 'Dopłata za kucie bruzdy instalacyjnej w żelbecie', priceNet: 150, priceGrossPrivate: 162, - priceGrossCompany: 184.5, + priceGrossCompany: 184, + category: 'additional', + }, + { + id: 'pompa-skroplin', + name: 'Instalacja pompy skroplin', + priceNet: 400, + priceGrossPrivate: 432, + priceGrossCompany: 492, + category: 'additional', + }, + { + id: 'demontaz', + name: 'Demontaż klimatyzacji', + priceNet: 300, + priceGrossPrivate: 324, + priceGrossCompany: 369, category: 'additional', }, { id: 'przeglad-klimatyzacji', - name: 'Okresowy przegląd i odgrzybianie klimatyzacji', + name: 'Czyszczenie i odgrzybianie klimatyzatora', priceNet: 250, priceGrossPrivate: 270, - priceGrossCompany: 307.5, + priceGrossCompany: 307, category: 'additional', }, ]; diff --git a/public/images/cities/wroclaw.webp b/public/images/cities/wroclaw.webp new file mode 100644 index 0000000..341c4d7 Binary files /dev/null and b/public/images/cities/wroclaw.webp differ