// ───────────────────────────────────────────────────────────────────────── // pages.jsx — Home, Banks, Markets, Programs, Articles, FAQ, Contact, Privacy, Terms // ───────────────────────────────────────────────────────────────────────── const { useState: useStateP, useEffect: useEffectP, useMemo: useMemoP } = React; /* ═════════════════════════════════════════════════════════════ HOME ═════════════════════════════════════════════════════════════ */ function HomePage({ country, navigate, lang, tweaks, tiles, visits = 0 }) { const t = window.I18N[lang]?.hero || window.I18N.pl.hero; const T = (k, fb) => window.t(lang, k, fb); return (
{/* Hero */}
{t.kicker}

{t.title1}
{t.title2}

{t.sub}

{/* Stats row */}
{/* Bento grid */}
{tiles.map(tile => ( ))}
{/* Subtle ad break */}
{/* Why us */}
{(window.t(lang, "whyUs.features", []) || []).map((f, i) => (
{f[0]}

{f[1]}

{f[2]}

))}
{/* CTA strip */}

{T("ctaStrip.title", "Ready to calculate?")}

{T("ctaStrip.sub", "Choose your loan type and compare offers from 15+ banks in under a minute.")}

); } function HeroStat({ value, label }) { return (
{value}
{label}
); } /* ═════════════════════════════════════════════════════════════ MORTGAGE / CASH page ═════════════════════════════════════════════════════════════ */ function CalcPage({ kind, country, navigate, lang }) { const isMortgage = kind === "mortgage"; const prefix = isMortgage ? "pages.mortgage" : "pages.cash"; const labels = { kicker: window.t(lang, `${prefix}.kicker`, isMortgage ? "🏠 Mortgage" : "💳 Cash Loan"), title: window.t(lang, `${prefix}.title`, isMortgage ? "Mortgage Calculator" : "Cash Loan Calculator"), sub: window.t(lang, `${prefix}.sub`, isMortgage ? "Enter your parameters and compare bank offers." : "Check your monthly payment with total cost."), }; const tips = window.t(lang, `${prefix}.tips`, [ ["Check creditworthiness", "Estimate your approval probability first."], ["Compare offers", "A 0.5% difference can save a lot over time."], ["APR matters", "APR reflects the true total cost of borrowing."], ["Currency risk", "FX movement can change your monthly payment."], ]); return (
{tips.map(([i, t, b], k) => (
{i}

{t}

{b}

))}
); } /* ═════════════════════════════════════════════════════════════ BANKS RANKING ═════════════════════════════════════════════════════════════ */ function BanksPage({ country, navigate, lang }) { const T = (k, fb) => window.t(lang, `pages.banks.${k}`, fb); const [filter, setFilter] = useStateP("all"); const [sort, setSort] = useStateP("mortgage"); const filtered = window.BANKS.filter(b => { if (filter === "all") return true; return b.country === filter; }); const sorted = [...filtered].sort((a, b) => a[sort] - b[sort]); return (
{[["all", `🌍 ${window.t(lang, "common.all", "All")}`], ["pl", "🇵🇱 PL"], ["de", "🇩🇪 DE"], ["fr", "🇫🇷 FR"], ["cz", "🇨🇿 CZ"]].map(([k, l]) => ( ))}
{T("lastUpdate", "Last update: 2026-05-12")}
setSort("mortgage")}>{T("th.mortgage", "Mortgage")} setSort("cash")}>{T("th.cash", "Cash")} {sorted.map((b, i) => { const c = window.COUNTRIES.find(c => c.code === b.country); return ( e.currentTarget.style.background = "var(--bg-2)"} onMouseLeave={e => e.currentTarget.style.background = "transparent"}> ); })}
# {T("th.bank", "Bank")} {T("th.country", "Country")} {T("th.rrso", "APR")} {T("th.maxLtv", "Max LTV")} {T("th.prov", "Fee")} {T("th.trend", "Trend")}
{i + 1} {b.name} {c?.flag} {b.mortgage.toFixed(2)}% {b.cash.toFixed(2)}% {b.rrso.toFixed(2)}% {b.ltv}% {b.prov.toFixed(1)}%

{T("disclaimer", "* Indicative data only. Final rate depends on individual credit assessment.")}

); } function th(align = "center") { return { padding: "14px 12px", textAlign: align, fontSize: 10, fontWeight: 800, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: "0.08em", }; } function td(align = "center") { return { padding: "14px 12px", textAlign: align, fontSize: 13, color: "var(--text-2)" }; } function SortTh({ active, onClick, children }) { return ( {children} {active ? "↓" : ""} ); } /* ═════════════════════════════════════════════════════════════ MARKETS ═════════════════════════════════════════════════════════════ */ function MarketsPage({ navigate, lang }) { const T = (k, fb) => window.t(lang, `pages.markets.${k}`, fb); return (
{window.REFERENCE_RATES.map(r => (
{r.name}
{r.value.toFixed(2)}%
))}
{window.FX.map(fx => ( ))}
{window.t(lang, "common.fxPair", "FX Pair")} {window.t(lang, "common.rate", "Rate")} 24h 7d {window.t(lang, "common.source", "Source")}
{fx.pair} {fx.value.toFixed(4)} {fx.source}

{T("ratesNote", "Indicative values. Source: NBP / ECB.")}

); } /* ═════════════════════════════════════════════════════════════ PROGRAMS ═════════════════════════════════════════════════════════════ */ function ProgramsPage({ navigate, lang }) { const T = (k, fb) => window.t(lang, `pages.programs.${k}`, fb); const programs = (window.getLocalizedPrograms && window.getLocalizedPrograms(lang)) || window.PROGRAMS; return (
{programs.map(p => { const c = window.COUNTRIES.find(c => c.code === p.country); return (
{p.icon}
{c?.flag} {c?.name}

{p.name}

{p.short}

{T("benefit", "Benefit")}
{p.benefit}
{T("whoCan", "Who can apply?")} {p.eligible}
); })}
); } /* ═════════════════════════════════════════════════════════════ ARTICLES list + Article view ═════════════════════════════════════════════════════════════ */ function ArticlesPage({ navigate, lang }) { const T = (k, fb) => window.t(lang, `pages.articles.${k}`, fb); const localizedArticles = (window.getLocalizedArticles && window.getLocalizedArticles(lang)) || window.ARTICLES; const [cat, setCat] = useStateP("all"); const cats = ["all", ...new Set(localizedArticles.map(a => a.cat))]; const list = cat === "all" ? localizedArticles : localizedArticles.filter(a => a.cat === cat); return (
{cats.map(c => ( ))}
{list.map(a => ( ))}
); } function getArticleBodyCopy(lang) { const copy = { en: { takeaways: [ "Current WIBOR 3M is at 5.86%, the lowest level since 2022.", "Average mortgage rate: 6.80% in PL vs 3.85% in the euro area.", "A ~3pp spread can mean roughly 2,000 PLN/month difference on a 400k loan.", "FX risk is real: a 10% PLN weakening can increase EUR-loan installments.", ], recent: "The ECB cut its deposit rate by 25 bps in March and EURIBOR 3M dropped below 2.7%. European banks quickly reflected that in pricing. NBP kept rates at 5.75%, while markets still price cuts in the second half of 2026.", quote: "If you plan a loan in 2026, timing matters. Waiting a quarter for lower benchmarks can reduce your starting rate by 0.3–0.5pp.", action: "If you already have a loan, check whether recalculation now improves your payment path. If you are applying soon, compare at least 5 offers using the calculator.", }, pl: { takeaways: [ "Aktualna stopa WIBOR 3M to 5.86%, najniĹĽej od 2022 r.", "Ĺšrednie oprocentowanie hipoteczne: 6.80% w PL vs 3.85% w strefie euro.", "Różnica ~3 pp. może oznaczać około 2 000 PLN/mies. przy kredycie 400k.", "Ryzyko walutowe pozostaje realne: 10% osłabienia PLN podnosi ratę kredytu w EUR.", ], recent: "EBC obniżył stopę depozytową o 25 pb. w marcu, a EURIBOR 3M spadł poniżej 2.7%. Banki szybko odzwierciedliły to w cennikach. NBP utrzymał stopy na 5.75%, choć rynek nadal wycenia obniżki w drugiej połowie 2026.", quote: "Jeśli planujesz kredyt w 2026 r., timing ma znaczenie. Czekanie kwartał na niższe stopy bazowe może obniżyć stawkę startową o 0.3–0.5 pp.", action: "Jeśli masz już kredyt, sprawdź czy przeliczenie poprawi ratę. Jeśli składasz nowy wniosek, porównaj minimum 5 ofert w kalkulatorze.", }, de: { takeaways: [ "Der aktuelle WIBOR 3M liegt bei 5,86% und ist damit auf dem niedrigsten Stand seit 2022.", "Durchschnittlicher Hypothekenzins: 6,80% in Polen vs. 3,85% im Euroraum.", "Ein Spread von ~3 Prozentpunkten kann bei 400k Kredit rund 2.000 PLN/Monat bedeuten.", "Währungsrisiko bleibt relevant: 10% schwächerer PLN erhöht EUR-Raten spürbar.", ], recent: "Die EZB senkte im März den Einlagensatz um 25 Basispunkte, EURIBOR 3M fiel unter 2,7%. Europäische Banken passten ihre Konditionen schnell an. Die NBP hielt 5,75%, der Markt preist dennoch Senkungen im zweiten Halbjahr 2026 ein.", quote: "Wenn Sie 2026 finanzieren wollen, ist das Timing entscheidend. Ein Quartal warten kann den Startzins um 0,3–0,5 Prozentpunkte senken.", action: "Bei bestehendem Kredit lohnt sich eine Neuberechnung. Bei neuer Finanzierung sollten mindestens 5 Angebote verglichen werden.", }, fr: { takeaways: [ "Le WIBOR 3M est à 5,86%, son plus bas niveau depuis 2022.", "Taux hypothécaire moyen: 6,80% en Pologne contre 3,85% en zone euro.", "Un écart d'environ 3 points peut représenter ~2 000 PLN/mois sur 400k.", "Le risque de change reste réel: -10% du PLN augmente la mensualité d'un prêt en EUR.", ], recent: "La BCE a abaissé son taux de dépôt de 25 pdb en mars et l'EURIBOR 3M est passé sous 2,7%. Les banques européennes ont ajusté leurs barèmes rapidement. La NBP reste à 5,75%, mais le marché anticipe encore des baisses au second semestre 2026.", quote: "Si vous préparez un crédit en 2026, le timing est clé. Attendre un trimestre peut réduire le taux de départ de 0,3 à 0,5 point.", action: "Si vous avez déjà un prêt, vérifiez l'intérêt d'un recalcul. Pour une nouvelle demande, comparez au moins 5 offres.", }, cs: { takeaways: [ "Aktuální WIBOR 3M je 5,86 %, nejníže od roku 2022.", "Průměrná hypotéka: 6,80 % v PL vs 3,85 % v eurozóně.", "Rozdíl ~3 p.b. může u úvěru 400k znamenat cca 2 000 PLN měsíčně.", "Kurzové riziko je reálné: oslabení PLN o 10 % zvyšuje splátku úvěru v EUR.", ], recent: "ECB v březnu snížila depozitní sazbu o 25 bb a EURIBOR 3M klesl pod 2,7 %. Evropské banky to rychle promítly do sazeb. NBP drží 5,75 %, trh však stále očekává snížení ve druhé polovině 2026.", quote: "Pokud plánujete úvěr v roce 2026, načasování je zásadní. Čekání jedno čtvrtletí může snížit startovní sazbu o 0,3–0,5 p.b.", action: "U stávajícího úvěru zvažte přepočet. U nové žádosti porovnejte alespoň 5 nabídek.", }, hu: { takeaways: [ "A WIBOR 3M jelenleg 5,86%, ami 2022 óta a legalacsonyabb.", "Átlagos jelzálogkamat: 6,80% Lengyelországban vs 3,85% az eurózónában.", "Kb. 3 százalékpontos különbség 400k hitelnél ~2 000 PLN/hó eltérést jelenthet.", "Az árfolyamkockázat valós: 10% PLN-gyengülés növeli az EUR-hitel törlesztőjét.", ], recent: "Az EKB márciusban 25 bázisponttal csökkentette a betéti rátát, az EURIBOR 3M 2,7% alá esett. Az európai bankok gyorsan átáraztak. Az NBP 5,75%-on tart, de a piac 2026 második felére további vágást vár.", quote: "Ha 2026-ban hitelt tervezel, az időzítés kulcsfontosságú. Egy negyedév várakozás 0,3–0,5 százalékponttal alacsonyabb induló kamatot hozhat.", action: "Meglévő hitelnél érdemes újraszámolni. Új hitelnél legalább 5 ajánlatot hasonlíts össze.", }, nl: { takeaways: [ "WIBOR 3M staat op 5,86%, het laagste niveau sinds 2022.", "Gemiddelde hypotheekrente: 6,80% in PL tegenover 3,85% in de eurozone.", "Een spread van ~3 procentpunt kan bij 400k circa 2.000 PLN/maand schelen.", "Valutarisico blijft reëel: 10% zwakkere PLN verhoogt EUR-leninglasten.", ], recent: "De ECB verlaagde in maart de depositorente met 25 bp en EURIBOR 3M dook onder 2,7%. Europese banken verwerkten dat snel in prijzen. NBP bleef op 5,75%, terwijl de markt nog steeds verlagingen voor H2 2026 inprijst.", quote: "Als je in 2026 financiert, is timing belangrijk. Een kwartaal wachten kan de startmarge met 0,3–0,5 procentpunt verlagen.", action: "Heb je al een lening, herbereken dan je scenario. Voor een nieuwe aanvraag: vergelijk minstens 5 aanbiedingen.", }, }; return copy[lang] || copy.en; } function PublicArticleMarkdown({ body }) { const blocks = String(body || "").split(/\n{2,}/).filter(Boolean); return ( <> {blocks.map((block, idx) => { const trimmed = block.trim(); if (trimmed.startsWith("## ")) { return

{trimmed.slice(3)}

; } if (trimmed.startsWith("- ")) { return ( ); } return

{trimmed}

; })} ); } function ArticlePage({ id, navigate, lang }) { const T = (k, fb) => window.t(lang, `pages.article.${k}`, fb); const bodyCopy = getArticleBodyCopy(lang); const localizedArticles = (window.getLocalizedArticles && window.getLocalizedArticles(lang)) || window.ARTICLES; const a = localizedArticles.find(x => x.id === id) || localizedArticles[0]; return (
{a.cat}

{a.title}

{a.author} · {a.date} · {a.read} {window.t(lang, "common.minutes", "min read")}

{a.excerpt}

{a.body ? ( ) : ( <>

{window.t(lang, "article.content.keyTakeaways", "Key takeaways")}

    {bodyCopy.takeaways.map((point, idx) =>
  • {point}
  • )}

{window.t(lang, "article.content.recentMonths", "What changed recently")}

{bodyCopy.recent}

"{bodyCopy.quote}"

{window.t(lang, "article.content.whatYouCanDo", "What you can do")}

{bodyCopy.action}

)}

{T("calcCard", "📊 Calculate now")}

{T("calcCardSub", "Check potential savings with your own numbers.")}

); } function h2Style() { return { marginTop: 32, marginBottom: 16, fontSize: 22, fontWeight: 800, letterSpacing: "-0.015em" }; } function ulStyle() { return { marginTop: 12, paddingLeft: 20, lineHeight: 1.8, color: "var(--text-2)" }; } function blockquoteStyle() { return { margin: "24px 0", padding: "16px 20px", borderLeft: "3px solid var(--accent)", background: "var(--bg-2)", fontStyle: "italic", fontSize: 16, color: "var(--text)", borderRadius: "0 12px 12px 0", }; } /* ═════════════════════════════════════════════════════════════ FAQ ═════════════════════════════════════════════════════════════ */ function FaqPage({ navigate, lang }) { const T = (k, fb) => window.t(lang, `pages.faq.${k}`, fb); const localizedFaqs = (window.getLocalizedFaqs && window.getLocalizedFaqs(lang)) || window.FAQS; const [open, setOpen] = useStateP(null); return (
{localizedFaqs.map((f, i) => (
{open === i && (
{f.a}
)}
))}
); } /* ═════════════════════════════════════════════════════════════ CONTACT ═════════════════════════════════════════════════════════════ */ function ContactPage({ navigate, lang }) { const T = (k, fb) => window.t(lang, `pages.contact.${k}`, fb); const topicLabels = window.t(lang, "pages.contact.topics", { general: "General question", bug: "Bug report", ads: "Advertising / partnership", rodo: "Data deletion (GDPR)", }); const [form, setForm] = useStateP({ name: "", email: "", topic: "general", msg: "" }); const [sent, setSent] = useStateP(false); const submit = (e) => { e.preventDefault(); // Save to internal inbox (localStorage) try { const inbox = JSON.parse(localStorage.getItem("kr_inbox") || "[]"); inbox.unshift({ id: Date.now(), name: form.name, email: form.email, topic: form.topic, msg: form.msg, date: new Date().toISOString(), status: "new", }); localStorage.setItem("kr_inbox", JSON.stringify(inbox)); } catch (err) {} setSent(true); }; return (
{sent ? (
âś…

{T("sent", "Message sent!")}

{T("sentSub", "Your message reached our internal inbox.")}

) : (
setForm({ ...form, name: e.target.value })} />
setForm({ ...form, email: e.target.value })} />