/* PCS marketing site — structural parts: Photo, Header (mega-menu), Hero,
Marquee, TrustStrip, Footer, CookieBanner.
Composes design-system primitives from window.PCSDesignSystem_269f6d
and icons from window.PCSIcons. Extends window.PCSHome. */
(function () {
const W = (window.PCSHome = window.PCSHome || {});
const CALENDLY = "https://calendly.com/propertyclinicsolutions/free-consultation";
W.CALENDLY = CALENDLY;
/* Ticketing link for the featured event (Empowerment Through Property). */
const EVENTBRITE = "https://www.eventbrite.com/e/empowerment-through-property-landlord-expo-wealth-meets-health-tickets-1990065361066?lid=zj5izzdshlrl";
W.EVENTBRITE = EVENTBRITE;
/* Web3Forms access key for the contact form (form-to-email relay). Get a free
key at https://web3forms.com (enter your inbox email) and paste it here. */
const WEB3FORMS_KEY = "d379e4a7-a1d0-46b7-b087-16db369a8d3f";
W.WEB3FORMS_KEY = WEB3FORMS_KEY;
/* Brevo subscription-form endpoint for the newsletter signup. Created in Brevo
(Contacts > Forms) with double opt-in ON, so Brevo sends the confirmation
email and manages unsubscribes. The site posts the email here directly. */
const BREVO_NEWSLETTER_URL = "https://c781fac4.sibforms.com/serve/MUIFACFLwA9tG6K2st4h_ItOJxsNbHLveaCEKOFPZNvIazvaLT2FeVi8B7Iy5NcEHFz8Htt7zRhwTuksYaXc7BU-aFHhxojW-QoczOiDlJfXLAL9imdp5rhsLZDa3AGfG1Wqc7L21-MSee3jmUphcbszI0Qvceo-AbZY049sFRXZSKSRsKpvIPGJxlBdh0XShgUBxTjfoqRFDWrxgw==";
W.BREVO_NEWSLETTER_URL = BREVO_NEWSLETTER_URL;
/* Google Analytics 4 — loaded only after the visitor accepts the "Statistics"
cookie (GDPR). Cookieless Cloudflare analytics loads separately via the
beacon in each page's
. */
const GA4_ID = "G-5LQ774ZZNW";
W.GA4_ID = GA4_ID;
let ga4Loaded = false;
function loadGA4() {
if (ga4Loaded || !GA4_ID || GA4_ID.indexOf("G-") !== 0) return;
ga4Loaded = true;
const s = document.createElement("script");
s.async = true;
s.src = "https://www.googletagmanager.com/gtag/js?id=" + GA4_ID;
document.head.appendChild(s);
window.dataLayer = window.dataLayer || [];
window.gtag = function () { window.dataLayer.push(arguments); };
window.gtag("js", new Date());
window.gtag("config", GA4_ID, { anonymize_ip: true });
}
W.loadGA4 = loadGA4;
try {
const saved = JSON.parse(localStorage.getItem("pcs-cookie-consent-v1") || "null");
if (saved && saved.statistics) loadGA4();
} catch (e) {}
const RISK = "Your home may be repossessed if you do not keep up repayments on your mortgage.";
W.RISK = RISK;
const reduceMotion = () =>
typeof window !== "undefined" && window.matchMedia &&
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/* Counts a stat up from zero when it scrolls into view. Parses a leading
number out of values like "500+", "£48m", "14 days" and keeps the rest. */
function CountUp({ value, duration = 1400 }) {
const ref = React.useRef(null);
const [disp, setDisp] = React.useState(value);
const [pop, setPop] = React.useState(false);
React.useEffect(() => {
const m = String(value).match(/^(\D*)([\d.,]+)(.*)$/);
if (!m) { setDisp(value); return; }
const pre = m[1], rawNum = m[2], suf = m[3];
const target = parseFloat(rawNum.replace(/,/g, ""));
if (isNaN(target)) { setDisp(value); return; }
if (reduceMotion()) { setDisp(value); return; }
const decimals = (rawNum.split(".")[1] || "").length;
const fmt = (n) => pre + n.toLocaleString("en-GB", { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) + suf;
const el = ref.current;
let raf, running = false, popTimer;
const run = () => {
running = true;
const start = performance.now();
const tick = (now) => {
const t = Math.min(1, (now - start) / duration);
const e = 1 - Math.pow(1 - t, 3);
if (t < 1) { setDisp(fmt(target * e)); raf = requestAnimationFrame(tick); }
else { setDisp(value); running = false; setPop(true); popTimer = setTimeout(() => setPop(false), 380); }
};
raf = requestAnimationFrame(tick);
};
setDisp(fmt(0));
if (!("IntersectionObserver" in window) || !el) { run(); return; }
// Re-fires every time the stat scrolls back into view.
const io = new IntersectionObserver((ents) => {
ents.forEach((en) => {
if (en.isIntersecting) { if (!running) run(); }
else { if (raf) cancelAnimationFrame(raf); running = false; setDisp(fmt(0)); }
});
}, { threshold: 0.5 });
io.observe(el);
return () => { if (raf) cancelAnimationFrame(raf); clearTimeout(popTimer); io.disconnect(); };
}, [value, duration]);
return {disp};
}
W.CountUp = CountUp;
/* Thin Royal-Blue bar at the very top that tracks scroll progress. */
function ScrollProgress() {
const ref = React.useRef(null);
React.useEffect(() => {
const onScroll = () => {
const d = document.documentElement;
const max = d.scrollHeight - d.clientHeight;
const p = max > 0 ? d.scrollTop / max : 0;
if (ref.current) ref.current.style.transform = "scaleX(" + Math.min(1, Math.max(0, p)) + ")";
};
window.addEventListener("scroll", onScroll, { passive: true });
window.addEventListener("resize", onScroll);
onScroll();
return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); };
}, []);
return ;
}
W.ScrollProgress = ScrollProgress;
/* Unsplash photo with a branded gradient + symbol fallback that always shows
if the network image fails to load. */
function Photo({ id, alt = "", h, tone = "blue", radius = 24, eager = false, style, className = "" }) {
const grad = tone === "emerald"
? "linear-gradient(150deg,#E3F4EC,#d3ecdf)"
: tone === "ink"
? "linear-gradient(150deg,#13224a,#0C1A3A)"
: "linear-gradient(150deg,#E7ECFA,#dbe3f9)";
const src = (window.__resources && window.__resources["photo_" + id]) || `assets/photos/${id}.jpg`;
return (
);
}
W.Photo = Photo;
const MORTGAGE_LINKS = [
{ icon: "key", title: "First-Time Buyers", desc: "From Agreement in Principle to keys in hand.", href: "first-time-buyers" },
{ icon: "house2", title: "Remortgage", desc: "Switch before you slip onto the standard variable rate.", href: "remortgage" },
{ icon: "trendingUp", title: "Buy to Let", desc: "Standard BTL, HMOs, limited company and portfolios.", href: "buy-to-let" },
{ icon: "user", title: "Self-Employed Mortgages", desc: "Lenders who assess real income properly.", href: "self-employed" },
{ icon: "calculator", title: "Mortgage Calculator", desc: "See what you could borrow in plain numbers.", href: "calculator" },
];
const PROTECTION_LINKS = [
{ icon: "heart", title: "Life Insurance", desc: "Cover that pays out so your home stays yours.", href: "life-insurance" },
{ icon: "shield", title: "Income Protection", desc: "Replace part of your income if you cannot work.", href: "income-protection" },
{ icon: "award", title: "Critical Illness", desc: "A lump sum if you are diagnosed with a serious illness.", href: "critical-illness" },
{ icon: "building", title: "Buildings and Contents", desc: "Protect the building and everything inside it.", href: "buildings-contents" },
];
function MegaPanel({ links, footnote, overview, onNavigate }) {
const { Icon } = window.PCSIcons;
return (
Independent UK mortgages and protection for first-time buyers, self-employed, landlords, and every case in between. For life, not just one transaction.
}>Book a Free Consultation
}>See Upcoming Events
{RISK}
Whole of market
90+ lenders, not a panel
Independent advice
);
}
W.Hero = Hero;
function TrustStrip() {
const { Icon } = window.PCSIcons;
const items = ["Whole of Market", "90+ Lenders", "No Upfront Fees", "Independent Advice"];
return (
{items.map((t) => (
{t}
))}
);
}
W.TrustStrip = TrustStrip;
function Marquee({ items, tone = "ink" }) {
const bg = tone === "blue" ? "var(--pcs-blue)" : "var(--pcs-ink)";
const row = (