/* 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