PageSourceSearch

https://propertyhub.net/about/

html propertyhub.net collected 2026-09-25 00:44:55 UTC 104,751 bytes, 898 lines download raw bytes

1<!DOCTYPE html><html lang="en" data-astro-cid-sckkx6r4> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- Mark that JS is available, as early as possible (before first paint).
2         Scroll-reveal initial states (opacity:0 / translateY) are scoped to
3         `.js` so that when JS is blocked/fails, content stays fully visible
4         instead of being permanently hidden. Must NOT be deferred. -->
4<script>document.documentElement.classList.add("js");</script>
4<link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="icon" href="/favicon.ico" sizes="16x16 32x32 48x48"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- The site is a fixed light design: declare that so browsers don't
5         auto-darken form controls/scrollbars, and tint mobile browser chrome
6         to match the white site header. --><meta name="color-scheme" content="light"><meta name="theme-color" content="#ffffff"><!-- Feed discovery: announce the RSS feed to readers and aggregators. --><link rel="alternate" type="application/rss+xml" title="Property Hub" href="/rss.xml"><!-- Consent Mode defaults must exist before either CookieYes or GTM. GTM
7         can then load immediately and send consent-safe cookieless pings while
8         advertising, analytics and behavioural vendors remain denied. -->
8<script>
9      (function () {
10        window.dataLayer = window.dataLayer || [];
11        window.gtag = window.gtag || function () { window.dataLayer.push(arguments); };
12
13        window.gtag("consent", "default", {
14          ad_storage: "denied",
15          ad_user_data: "denied",
16          ad_personalization: "denied",
17          analytics_storage: "denied",
18          functionality_storage: "denied",
19          personalization_storage: "denied",
20          security_storage: "granted",
21          wait_for_update: 2000,
22        });
23        window.gtag("set", "ads_data_redaction", true);
24
25        function cookieYesCategories() {
26          var match = document.cookie.match(/(?:^|;\s*)cookieyes-consent=([^;]*)/);
27          if (!match) return {};
28          return decodeURIComponent(match[1]).split(",").reduce(function (values, item) {
29            var separator = item.indexOf(":");
30            if (separator === -1) return values;
31            values[item.slice(0, separator)] = item.slice(separator + 1);
32            return values;
33          }, {});
34        }
35
36        function isGranted(value) {
37          return /^(yes|true)$/i.test(value || "");
38        }
39
40        function updateGoogleConsent() {
41          var categories = cookieYesCategories();
42          var analytics = isGranted(categories.analytics);
43          var advertising = isGranted(categories.advertisement);
44          var functional = isGranted(categories.functional);
45
46          window.gtag("consent", "update", {
47            ad_storage: advertising ? "granted" : "denied",
48            ad_user_data: advertising ? "granted" : "denied",
49            ad_personalization: advertising ? "granted" : "denied",
50            analytics_storage: analytics ? "granted" : "denied",
51            functionality_storage: functional ? "granted" : "denied",
52            personalization_storage: functional ? "granted" : "denied",
53          });
54        }
55
56        updateGoogleConsent();
57        document.addEventListener("cookie_consent_update", updateGoogleConsent);
58      })();
59    </script>
59<!-- CookieYes — deferred and non-blocking. -->
59<script id="cookieyes" type="text/javascript" src="https://cdn-cookieyes.com/client_data/b895e82d8e1c52eed0a7eb64/script.js" defer></script>
59<!-- Google Tag Manager — load immediately on the canonical production host.
60         Consent Mode above controls storage/vendor activation. The hostname
61         guard excludes localhost and every Netlify deploy-preview hostname. -->
61<script>
62      (function () {
63        if (!/^(www\.)?propertyhub\.net$/i.test(window.location.hostname)) return;
64        (function (w, d, s, l, i) {
65          w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
66          var f = d.getElementsByTagName(s)[0];
67          var j = d.createElement(s);
68          var dl = l != "dataLayer" ? "&l=" + l : "";
69          j.async = true;
70          j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
71          f.parentNode.insertBefore(j, f);
72        })(window, document, "script", "dataLayer", "GTM-PQS7STW");
73      })();
74    </script>
74<!-- End Google Tag Manager --><!-- SEO --><title>About Property Hub | Where Serious Investors Are Built</title><meta name="description" content="Learn about Property Hub - education for people who want to invest themselves, and a done-with-you service for people who'd rather not."><link rel="canonical" href="https://propertyhub.net/about/"><!-- Open Graph --><meta property="og:title" content="About Property Hub | Where Serious Investors Are Built"><meta property="og:description" content="Learn about Property Hub - education for people who want to invest themselves, and a done-with-you service for people who'd rather not."><meta property="og:image" content="https://assets.propertyhub.net/homepage/hero-manchester-dusk-1400w.webp"><meta property="og:url" content="https://propertyhub.net/about/"><meta property="og:type" content="website"><meta property="og:site_name" content="Property Hub"><!-- Twitter --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="About Property Hub | Where Serious Investors Are Built"><meta name="twitter:description" content="Learn about Property Hub - education for people who want to invest themselves, and a done-with-you service for people who'd rather not."><meta name="twitter:image" content="https://assets.propertyhub.net/homepage/hero-manchester-dusk-1400w.webp"><!-- Article-specific --><!-- JSON-LD -->
74<script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","name":"Property Hub","url":"https://propertyhub.net","description":"Where serious investors are built. Education and done-with-you property investment services.","address":{"@type":"PostalAddress","streetAddress":"1st Floor B, 117-119 Portland Street","addressLocality":"Manchester","postalCode":"M1 6ED","addressCountry":"GB"}}</script>
74<!-- Fonts --><meta name="generator" content="Astro v6.3.7"><link rel="preconnect" href="https://assets.propertyhub.net"><link rel="preload" href="/fonts/roboto-latin-variable.woff2" as="font" type="font/woff2" crossorigin><!-- Hero/LCP image preload (per-page, optional) --><!-- Sitewide structured data (Organization + optional Article/BreadcrumbList).
75         Kept at the end of <head>, separate from the per-page JSON-LD above,
76         so multiple ld+json blocks coexist without duplication. --><link rel="stylesheet" href="/_astro/Layout.ebJlzi19.css">
77<style>.logo-stack{display:grid;line-height:0}.logo-stack .logo-face{grid-area:1 / 1;transition:opacity .5s ease}.logo-stack .logo-standard{opacity:0}.logo-stack .logo-invest{opacity:1}.logo-stack.is-armed .logo-standard{opacity:1;transition:none}.logo-stack.is-armed .logo-invest{opacity:0;transition:none}.logo-stack.is-playing .logo-standard{opacity:0}.logo-stack.is-playing .logo-invest{opacity:1}@media(prefers-reduced-motion:reduce){.logo-stack .logo-face{transition:none}}.nav-serif,.nav-serif a,.nav-serif button{font-family:var(--font-sans);font-weight:500;letter-spacing:0}.nav-dropdown a{font-family:var(--font-sans);letter-spacing:0}.nav-dropdown-panel{opacity:0;visibility:hidden}.nav-dropdown-root:hover .nav-dropdown-panel,.nav-dropdown-root:focus-within .nav-dropdown-panel,.nav-dropdown-root.is-open .nav-dropdown-panel{opacity:1;visibility:visible}.nav-dropdown-root.is-closed .nav-dropdown-panel{opacity:0;visibility:hidden}.nav-dropdown-label{font-family:var(--font-sans);font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.18em;color:var(--color-text-tertiary, #8A929C)}.nav-cta{font-family:var(--font-sans);font-weight:500;letter-spacing:.01em}.btn.nav-cta{transition-duration:.15s}.menu-icon{position:relative;display:block;width:24px;height:24px}.menu-bar{position:absolute;left:3px;right:3px;height:2px;border-radius:1px;background:currentColor;transition:transform .25s cubic-bezier(.16,1,.3,1),opacity .15s ease}.menu-bar--top{top:6px}.menu-bar--mid{top:11px}.menu-bar--bot{top:16px}#mobile-menu-btn.is-open .menu-bar--top{transform:translateY(5px) rotate(45deg)}#mobile-menu-btn.is-open .menu-bar--mid{opacity:0;transform:scaleX(.4)}#mobile-menu-btn.is-open .menu-bar--bot{transform:translateY(-5px) rotate(-45deg)}@media(prefers-reduced-motion:reduce){.menu-bar{transition:none}}
78.footer-label[data-astro-cid-sz7xmlte]{font-family:var(--font-sans);font-weight:600;text-transform:uppercase;letter-spacing:.18em}.footer-link[data-astro-cid-sz7xmlte]{font-family:var(--font-sans);font-weight:400;letter-spacing:0;touch-action:manipulation}a[data-astro-cid-sz7xmlte].footer-link:active{color:#fff}
79.timeline-pulse[data-astro-cid-kh7btl4r]{animation:timeline-pulse 2.4s cubic-bezier(.16,1,.3,1) infinite}@keyframes timeline-pulse{0%,to{box-shadow:0 0 0 5px #ff51512e}50%{box-shadow:0 0 0 10px #ff51510f}}.timeline-track[data-astro-cid-kh7btl4r]::-webkit-scrollbar{display:none}.timeline-track[data-astro-cid-kh7btl4r]{scrollbar-width:none}.hero-about[data-astro-cid-kh7btl4r]:before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse 80% 70% at 5% 20%,rgba(255,81,81,.04) 0%,transparent 60%),radial-gradient(ellipse 60% 80% at 90% 80%,rgba(201,169,110,.035) 0%,transparent 50%);pointer-events:none}.stat-card[data-astro-cid-kh7btl4r]:after{content:"";position:absolute;bottom:0;left:50%;transform:translate(-50%);width:32px;height:2px;background:var(--color-coral);border-radius:1px;opacity:0;transition:opacity .25s cubic-bezier(.16,1,.3,1)}.stat-card[data-astro-cid-kh7btl4r]:hover{border-color:var(--color-border);box-shadow:var(--shadow-card)}.stat-card[data-astro-cid-kh7btl4r]:hover:after{opacity:1}.reveal[data-astro-cid-kh7btl4r]{opacity:0;transform:translateY(20px);transition:opacity .4s cubic-bezier(.16,1,.3,1),transform .4s cubic-bezier(.16,1,.3,1)}.reveal[data-astro-cid-kh7btl4r].visible{opacity:1;transform:translateY(0)}.reveal-stagger[data-astro-cid-kh7btl4r]>[data-astro-cid-kh7btl4r]{opacity:0;transform:translateY(20px);transition:opacity .4s cubic-bezier(.16,1,.3,1),transform .4s cubic-bezier(.16,1,.3,1)}.reveal-stagger[data-astro-cid-kh7btl4r].visible>[data-astro-cid-kh7btl4r]:nth-child(1){transition-delay:0ms;opacity:1;transform:translateY(0)}.reveal-stagger[data-astro-cid-kh7btl4r].visible>[data-astro-cid-kh7btl4r]:nth-child(2){transition-delay:80ms;opacity:1;transform:translateY(0)}.reveal-stagger[data-astro-cid-kh7btl4r].visible>[data-astro-cid-kh7btl4r]:nth-child(3){transition-delay:.16s;opacity:1;transform:translateY(0)}.reveal-stagger[data-astro-cid-kh7btl4r].visible>[data-astro-cid-kh7btl4r]:nth-child(4){transition-delay:.24s;opacity:1;transform:translateY(0)}
80</style></head> <body class="font-sans text-text-primary antialiased bg-background " data-astro-cid-sckkx6r4> <!-- Keyboard users can bypass the header nav; visually hidden until focused.
81         Target: the page's main landmark (id "main-content" — pages and
82         sub-layouts all carry it). Styles in global.css (.skip-link). -->
82 <a href="#main-content" class="skip-link" data-astro-cid-sckkx6r4>Skip to content</a> <!-- Google Tag Manager (noscript) --> <noscript> <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PQS7STW" height="0" width="0" style="display:none;visibility:hidden" data-astro-cid-sckkx6r4></iframe> </noscript> <!-- End Google Tag Manager (noscript) -->  <!-- HEADER --><header class="fixed top-0 left-0 right-0 z-[99] bg-white/95 backdrop-blur-[10px] transition-shadow duration-250" id="site-header" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);"> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-between h-[88px]"> <a href="/" class="flex-shrink-0"> <img src="/property-hub-logo.svg" alt="Property Hub" class="h-[68px] w-auto"> </a>  <!-- Desktop nav --> <nav class="hidden xl:flex items-center gap-6 nav-serif whitespace-nowrap"> <!-- Start Here (standalone) --> <a href="/learn/" class="text-[16px] hover:text-text-primary transition-colors duration-150 text-charcoal-soft font-normal">Start Here</a> <!-- Investing Guides (dropdown) --> <div class="relative group nav-dropdown-root"> <button id="nav-dd-guides-btn" aria-controls="nav-dd-guides" class="nav-dropdown-btn flex items-center gap-1.5 text-[16px] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coral transition-colors duration-150 hover:text-text-primary text-charcoal-soft font-normal" aria-expanded="false" aria-haspopup="true">
83Investing Guides
84<svg class="w-3 h-3 opacity-50 group-hover:opacity-100 transition-opacity duration-150" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg> </button> <div id="nav-dd-guides" class="nav-dropdown-panel absolute top-full left-0 pt-3 transition-[opacity,visibility] duration-250" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);"> <div class="nav-dropdown bg-white rounded-lg shadow-card border border-border-light py-2 min-w-[230px]"> <a href="/guides/getting-started/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Getting Started</a> <a href="/guides/buying-sourcing/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Buying &amp; Sourcing</a> <a href="/guides/financing-tax/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Financing &amp; Tax</a> <a href="/guides/strategy-portfolio/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Strategy &amp; Portfolio</a> <a href="/guides/lettings-landlord/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Lettings &amp; Being a Landlord</a> <a href="/guides/market-view/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Market View &amp; Timing</a> </div> </div> </div> <!-- Build Your Portfolio (dropdown) --> <div class="relative group nav-dropdown-root"> <button id="nav-dd-portfolio-btn" aria-controls="nav-dd-portfolio" class="nav-dropdown-btn flex items-center gap-1.5 text-[16px] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coral transition-colors duration-150 hover:text-text-primary text-charcoal-soft font-normal" aria-expanded="false" aria-haspopup="true">
85Build Your Portfolio
86<svg class="w-3 h-3 opacity-50 group-hover:opacity-100 transition-opacity duration-150" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg> </button> <div id="nav-dd-portfolio" class="nav-dropdown-panel absolute top-full left-0 pt-3 transition-[opacity,visibility] duration-250" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);"> <div class="nav-dropdown bg-white rounded-lg shadow-card border border-border-light py-2 min-w-[230px]"> <a href="/invest/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Invest With Us</a> <a href="/invest/book-a-consultation/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Book a Consultation</a> <a href="/invest/case-studies/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Past Deals &amp; Case Studies</a> <a href="/investor-stories/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Investor Stories</a> <a href="/invest/expats/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">For Expats</a> </div> </div> </div> <!-- Resources (dropdown) --> <div class="relative group nav-dropdown-root"> <button id="nav-dd-resources-btn" aria-controls="nav-dd-resources" class="nav-dropdown-btn flex items-center gap-1.5 text-[16px] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coral transition-colors duration-150 hover:text-text-primary text-charcoal-soft font-normal" aria-expanded="false" aria-haspopup="true">
87Resources
88<svg class="w-3 h-3 opacity-50 group-hover:opacity-100 transition-opacity duration-150" fill="none" stroke="currentColor" viewBox="0 0 24 24">
88<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg> </button> <div id="nav-dd-resources" class="nav-dropdown-panel absolute top-full left-0 pt-3 transition-[opacity,visibility] duration-250" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);"> <div class="nav-dropdown bg-white rounded-lg shadow-card border border-border-light py-2 min-w-[210px]"> <a href="/news/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Latest News</a> <a href="/pulse/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Newsletter</a> <a href="/free-tools/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Investor Toolkit</a> <a href="https://quiz.propertyhub.net/" target="_blank" rel="noopener noreferrer" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Investment Readiness Quiz</a> <a href="https://podcast.propertyhub.net/" target="_blank" rel="noopener noreferrer" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Podcast</a> <a href="https://www.youtube.com/propertyhubuk" target="_blank" rel="noopener noreferrer" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">YouTube</a> <a href="/books/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Books</a> <a href="/university/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Free Courses</a> <a href="/webinar/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Webinar</a> <a href="/ai/" class="block px-4 py-2 text-[13px] text-text-secondary hover:text-text-primary hover:bg-background transition-colors duration-150">Rob AI</a> </div> </div> </div> <!-- CTA --> <a href="/free-tools/" class="btn btn--primary btn--xs nav-cta" data-analytics-cta="true" data-cta-name="get_free_toolkit" data-cta-location="header" data-business-area="tools">Get the free toolkit</a> </nav> <div class="flex items-center gap-3"> <!-- Search button --> <button id="search-toggle" class="p-2 text-text-tertiary hover:text-text-primary transition-colors duration-150" aria-label="Search"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg> </button> <!-- Mobile menu button (hamburger morphs to cross when open) --> <button id="mobile-menu-btn" class="xl:hidden p-2 text-text-secondary" aria-label="Open menu" aria-expanded="false" aria-controls="mobile-menu"> <span class="menu-icon" aria-hidden="true"> <span class="menu-bar menu-bar--top"></span> <span class="menu-bar menu-bar--mid"></span> <span class="menu-bar menu-bar--bot"></span> </span> </button> </div> </div> <!-- Mobile menu --> <div id="mobile-menu" class="hidden xl:hidden bg-white border-t border-border-light px-4 pb-4 max-h-[80vh] overflow-y-auto"> <!-- Start Here (standalone) --> <a href="/learn/" class="nav-serif block py-3 text-[18px] font-normal text-text-primary border-b border-border-light">Start Here</a> <!-- Investing Guides --> <div class="border-b border-border-light"> <button class="mobile-dropdown-trigger nav-serif w-full flex items-center justify-between py-3 text-[18px] font-normal text-text-primary" aria-expanded="false">
89Investing Guides
90<svg class="mobile-chevron w-4 h-4 text-text-tertiary transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg> </button> <div class="mobile-dropdown hidden pl-4 pb-3 space-y-1"> <a href="/guides/getting-started/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Getting Started</a> <a href="/guides/buying-sourcing/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Buying &amp; Sourcing</a> <a href="/guides/financing-tax/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Financing &amp; Tax</a>
90 <a href="/guides/strategy-portfolio/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Strategy &amp; Portfolio</a> <a href="/guides/lettings-landlord/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Lettings &amp; Being a Landlord</a> <a href="/guides/market-view/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Market View &amp; Timing</a> </div> </div> <!-- Build Your Portfolio --> <div class="border-b border-border-light"> <button class="mobile-dropdown-trigger nav-serif w-full flex items-center justify-between py-3 text-[18px] font-normal text-text-primary" aria-expanded="false">
91Build Your Portfolio
92<svg class="mobile-chevron w-4 h-4 text-text-tertiary transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg> </button> <div class="mobile-dropdown hidden pl-4 pb-3 space-y-1"> <a href="/invest/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Invest With Us</a> <a href="/invest/book-a-consultation/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Book a Consultation</a> <a href="/invest/case-studies/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Past Deals &amp; Case Studies</a> <a href="/investor-stories/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Investor Stories</a> <a href="/invest/expats/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">For Expats</a> </div> </div> <!-- Resources --> <div class="border-b border-border-light"> <button class="mobile-dropdown-trigger nav-serif w-full flex items-center justify-between py-3 text-[18px] font-normal text-text-primary" aria-expanded="false">
93Resources
94<svg class="mobile-chevron w-4 h-4 text-text-tertiary transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg> </button> <div class="mobile-dropdown hidden pl-4 pb-3 space-y-1"> <a href="/news/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Latest News</a> <a href="/pulse/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Newsletter</a> <a href="/free-tools/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Investor Toolkit</a> <a href="https://quiz.propertyhub.net/" target="_blank" rel="noopener noreferrer" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Investment Readiness Quiz</a> <a href="https://podcast.propertyhub.net/" target="_blank" rel="noopener noreferrer" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Podcast</a> <a href="https://www.youtube.com/propertyhubuk" target="_blank" rel="noopener noreferrer" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">YouTube</a> <a href="/books/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Books</a> <a href="/university/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Free Courses</a> <a href="/webinar/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Webinar</a> <a href="/ai/" class="block py-2 text-[13px] text-text-secondary hover:text-coral transition-colors duration-150">Rob AI</a> </div> </div> <div class="pt-3"> <a href="/free-tools/" class="btn btn--primary btn--xs nav-cta w-full justify-center py-3" data-analytics-cta="true" data-cta-name="get_free_toolkit" data-cta-location="mobile_menu" data-business-area="tools">Get the free toolkit</a> </div> <div class="mt-5 pt-5 border-t border-border-light"> <div class="grid grid-cols-3 gap-2 mb-5"> <a href="/about/" class="text-center py-2 text-[13px] font-medium text-text-secondary hover:text-coral transition-colors duration-150">About</a> <a href="/contact/" class="text-center py-2 text-[13px] font-medium text-text-secondary hover:text-coral transition-colors duration-150">Contact</a> <a href="/jobs/" class="text-center py-2 text-[13px] font-medium text-text-secondary hover:text-coral transition-colors duration-150">Jobs</a> </div> <div class="flex items-center justify-center gap-5"> <a href="https://www.facebook.com/propertyhubuk/" target="_blank" rel="noopener noreferrer" class="text-text-tertiary hover:text-text-primary transition-colors duration-150" aria-label="Facebook"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
94<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"></path></svg> </a> <a href="https://www.instagram.com/propertyhubuk/" target="_blank" rel="noopener noreferrer" class="text-text-tertiary hover:text-text-primary transition-colors duration-150" aria-label="Instagram"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z"></path></svg> </a> <a href="https://www.linkedin.com/company/thepropertyhub" target="_blank" rel="noopener noreferrer" class="text-text-tertiary hover:text-text-primary transition-colors duration-150" aria-label="LinkedIn"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path></svg> </a> <a href="https://www.youtube.com/propertyhubuk" target="_blank" rel="noopener noreferrer" class="text-text-tertiary hover:text-text-primary transition-colors duration-150" aria-label="YouTube"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"></path></svg> </a> </div> </div> </div> </header> <!-- Search overlay (outside header — header's backdrop-filter creates a containing block that traps fixed children) --> <div id="search-overlay" role="dialog" aria-modal="true" aria-label="Search Property Hub" class="hidden fixed inset-0 z-[100] bg-charcoal/50 backdrop-blur-sm overflow-y-auto"> <div class="max-w-2xl mx-auto mt-24 mb-12 px-4"> <div class="bg-white rounded-[12px] shadow-elevated overflow-hidden">  <div class="flex items-center gap-3 p-6 border-b border-border-light focus-within:border-coral transition-colors duration-150"> <svg class="w-5 h-5 text-text-tertiary flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
94<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg> <input id="search-input" type="text" aria-label="Search Property Hub" placeholder="Search Property Hub..." autocomplete="off" class="w-full text-[18px] font-light text-text-primary placeholder:text-text-tertiary outline-none"> <button id="search-close" class="p-1 text-text-tertiary hover:text-text-primary transition-colors duration-150" aria-label="Close search"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg> </button> </div> <div id="search-results" aria-live="polite" class="max-h-[60vh] overflow-y-auto"></div> </div> </div> </div> 
94<script type="module" src="/_astro/Header.astro_astro_type_script_index_0_lang.PZKu0m98.js"></script>
94 <main id="main-content" class="pt-16 sm:pt-20" data-astro-cid-kh7btl4r> <!-- HERO SECTION --> <section class="pt-28 lg:pt-36 pb-16 lg:pb-24 bg-background-warm relative overflow-hidden surface-editorial" data-astro-cid-kh7btl4r> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 relative z-[1]" data-astro-cid-kh7btl4r> <div class="lg:grid lg:grid-cols-2 lg:gap-16 items-center" data-astro-cid-kh7btl4r> <!-- Left: hero video --> <div class="mb-10 lg:mb-0 reveal" data-reveal data-astro-cid-kh7btl4r> <div class="rounded-[16px] overflow-hidden shadow-card" data-astro-cid-kh7btl4r> <div style="position:relative; width:1px; min-width:100%; padding-bottom:56.25%;" data-astro-cid-kh7btl4r> <iframe allow="autoplay" class="spotlightr" allowtransparency="true" style="width:1px; min-width:100%; height:100%; position:absolute;" allowfullscreen="true" src="https://videos.cdn.spotlightr.com/watch/MjAwMTk4Mw==?fallback=true" frameborder="0" scrolling="no" name="videoPlayer" title="About Property Hub" data-astro-cid-kh7btl4r></iframe> </div> </div> </div> <!-- Right: headline + intro text --> <div class="reveal" data-reveal style="transition-delay: 0.15s;" data-astro-cid-kh7btl4r> <div class="w-16 h-px bg-coral mb-8" data-astro-cid-kh7btl4r></div> <h1 class="font-serif text-[36px] sm:text-[44px] lg:text-[52px] text-text-primary leading-[1.05] tracking-[-0.02em]" data-astro-cid-kh7btl4r>
95Building a property portfolio takes two things most people don't have: <span class="text-coral italic" data-astro-cid-kh7btl4r>knowledge, and time.</span> </h1> <p class="mt-6 sm:mt-8 text-[18px] text-text-secondary font-light leading-[1.7]" data-astro-cid-kh7btl4r>
96That's why we built two sides to Property Hub: education for people who want to do it themselves, and a done-with-you service for people who'd rather not.
97</p> </div> </div> </div> </section> <!-- WHAT WE BELIEVE --> <section class="py-24 lg:py-32 bg-surface relative overflow-hidden surface-editorial" data-astro-cid-kh7btl4r> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 relative z-[1]" data-astro-cid-kh7btl4r> <div class="lg:grid lg:grid-cols-[390px_1fr] lg:gap-8" data-astro-cid-kh7btl4r> <div class="reveal" data-reveal data-astro-cid-kh7btl4r> <p class="text-coral text-[12px] font-medium uppercase tracking-[0.12em] mb-3" data-astro-cid-kh7btl4r>Our philosophy</p> <h2 class="font-serif text-[32px] sm:text-[40px] lg:text-[52px] text-text-primary leading-[1.1] tracking-[-0.02em]" data-astro-cid-kh7btl4r>
98What we believe
99</h2> </div> <div class="mt-6 lg:mt-0 reveal" data-reveal style="transition-delay: 0.1s;" data-astro-cid-kh7btl4r> <div class="space-y-5" data-astro-cid-kh7btl4r> <p class="text-[18px] text-text-primary font-medium leading-[1.7]" data-astro-cid-kh7btl4r>
100True property wealth isn't built from rental income &ndash; it's built from owning quality assets long enough for compounding to do the work.
101</p> <p class="text-[18px] text-text-secondary font-light leading-[1.7]" data-astro-cid-kh7btl4r>
102This belief shapes everything we do. Our <a href="/books/" class="text-coral hover:underline transition-colors duration-150" data-astro-cid-kh7btl4r>books</a>, <a href="https://podcast.propertyhub.net/" target="_blank" rel="noopener noreferrer" class="text-coral hover:underline transition-colors duration-150" data-astro-cid-kh7btl4r>podcast</a> and <a href="/youtube/" class="text-coral hover:underline transition-colors duration-150" data-astro-cid-kh7btl4r>YouTube</a> channel exist to help people set clearer goals, choose the right strategy, and build a portfolio that performs over time. <a href="/invest/" class="text-coral hover:underline transition-colors duration-150" data-astro-cid-kh7btl4r>Our investment service</a> is designed for people who understand this but don't have time to execute it themselves.
103</p> <p class="text-[18px] text-text-secondary font-light leading-[1.7]" data-astro-cid-kh7btl4r>
104We're not for everyone. If you enjoy the hands-on side of property, you'll find our approach frustrating. If you're looking for a quick flip, we're the wrong partner. But if you want to build long-term financial security, independence and a legacy, you're in the right place.
105</p> </div> </div> </div> </div> </section> <!-- HOW WE WORK — dark section --> <section class="py-24 lg:py-32 bg-charcoal relative overflow-hidden" data-astro-cid-kh7btl4r> <!-- Radial glows --> <div class="absolute inset-0 pointer-events-none" style="background: radial-gradient(ellipse at 15% 80%, rgba(255, 81, 81, 0.06), transparent 50%), radial-gradient(ellipse at 85% 20%, rgba(201, 169, 110, 0.04), transparent 50%);" data-astro-cid-kh7btl4r></div> <!-- Grain --> <div class="absolute inset-0 pointer-events-none" style="background-image: var(--grain); background-repeat: repeat; background-size: 256px 256px; opacity: 0.04; mix-blend-mode: soft-light;" data-astro-cid-kh7btl4r></div> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 relative z-[1]" data-astro-cid-kh7btl4r> <div class="text-center mb-14 reveal" data-reveal data-astro-cid-kh7btl4r> <p class="text-coral text-[12px] font-medium uppercase tracking-[0.12em] mb-3" data-astro-cid-kh7btl4r>Our process</p> <h2 class="font-serif text-[32px] sm:text-[40px] lg:text-[52px] text-white leading-[1.1] tracking-[-0.02em]" data-astro-cid-kh7btl4r>
106How we work
107</h2> </div> <div class="grid grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8 reveal-stagger" data-reveal-stagger data-astro-cid-kh7btl4r> <div class="text-center" data-astro-cid-kh7btl4r> <h3 class="text-[20px] sm:text-[24px] font-medium text-white mb-2" data-astro-cid-kh7btl4r>Search the market</h3> <div class="w-8 h-[2px] bg-coral mx-auto mb-5" data-astro-cid-kh7btl4r></div> <p class="text-[16px] text-white/70 font-light leading-[1.7]" data-astro-cid-kh7btl4r>We use our exclusive relationships with developers &ndash; FTSE-listed housebuilders and major regional players &ndash; to secure properties before they reach the open market.</p> </div> <div class="text-center" data-astro-cid-kh7btl4r> <h3 class="text-[20px] sm:text-[24px] font-medium text-white mb-2" data-astro-cid-kh7btl4r>Negotiate the deal</h3> <div class="w-8 h-[2px] bg-coral mx-auto mb-5" data-astro-cid-kh7btl4r></div> <p class="text-[16px] text-white/70 font-light leading-[1.7]" data-astro-cid-kh7btl4r>Because we're buying at volume and can move quickly, we negotiate discounts that aren't available to individual buyers &ndash; securing an average 11% off the market value.</p> </div> <div class="text-center" data-astro-cid-kh7btl4r> <h3 class="text-[20px] sm:text-[24px] font-medium text-white mb-2" data-astro-cid-kh7btl4r>Present our research</h3> <div class="w-8 h-[2px] bg-coral mx-auto mb-5" data-astro-cid-kh7btl4r></div> <p class="text-[16px] text-white/70 font-light leading-[1.7]" data-astro-cid-kh7btl4r>When a development matches our criteria, we share it with clients: location, pricing, rental projections, the full picture. You do your own research and due diligence.</p> </div> <div class="text-center" data-astro-cid-kh7btl4r> <h3 class="text-[20px] sm:text-[24px] font-medium text-white mb-2" data-astro-cid-kh7btl4r>Own the process</h3> <div class="w-8 h-[2px] bg-coral mx-auto mb-5" data-astro-cid-kh7btl4r></div> <p class="text-[16px] text-white/70 font-light leading-[1.7]" data-astro-cid-kh7btl4r>If you decide to proceed, we coordinate the financing and legals and get it ready for tenants to move in.</p> </div> </div> <!-- Quote --> <div class="mt-16 pt-14 text-center border-t border-white/10 reveal" data-reveal data-astro-cid-kh7btl4r> <p class="max-w-4xl mx-auto text-[20px] sm:text-[28px] font-medium text-white leading-[1.2]" data-astro-cid-kh7btl4r>
108We do the work, but you're in control: you pay us nothing until we've found you something you love.
109</p> <div class="mt-8 flex justify-center" data-astro-cid-kh7btl4r> <div class="w-12 h-[2px] bg-coral" data-astro-cid-kh7btl4r></div> </div> </div> </div> </section> <!-- WHAT WE'VE BUILT — timeline --> <section class="py-24 lg:py-32 bg-background-warm relative overflow-hidden surface-editorial" data-astro-cid-kh7btl4r> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 relative z-[1]" data-astro-cid-kh7btl4r> <div class="max-w-[720px] mb-16 lg:mb-20 reveal" data-reveal data-astro-cid-kh7btl4r> <div class="w-16 h-px bg-coral mb-8" data-astro-cid-kh7btl4r></div> <p class="text-coral text-[12px] font-medium uppercase tracking-[0.12em] mb-3" data-astro-cid-kh7btl4r>Track record</p> <h2 class="font-serif text-[32px] sm:text-[44px] lg:text-[52px] text-text-primary leading-[1.05] tracking-[-0.02em] mb-6" data-astro-cid-kh7btl4r>
110What we've built
111</h2> <p class="text-[18px] text-text-primary font-medium leading-[1.7] mb-4" data-astro-cid-kh7btl4r>
112We've been teaching property investment for as long as we've been doing it. Most people who benefit from our expertise will never pay us anything &ndash; and that's fine.
113</p> <p class="text-[18px] text-text-secondary font-light leading-[1.7]" data-astro-cid-kh7btl4r>
114But it does mean that when someone works with us, they've usually spent months or years understanding our approach. They know whether we're right for them before the first conversation.
115</p> </div> <!-- Horizontal timeline --> <div class="relative reveal" data-reveal style="transition-delay: 0.15s;" data-astro-cid-kh7btl4r> <!-- Scroll container for mobile, static grid on desktop --> <div class="timeline-track overflow-x-auto lg:overflow-visible pb-6 lg:pb-0" data-astro-cid-kh7btl4r> <div class="timeline-inner relative min-w-[900px] lg:min-w-0 lg:grid lg:grid-cols-7 flex" data-astro-cid-kh7btl4r> <!-- Timeline axis --> <div class="absolute left-0 right-0 top-[62px] h-px bg-border-light" aria-hidden="true" data-astro-cid-kh7btl4r></div> <div class="absolute left-0 top-[62px] h-px bg-coral" aria-hidden="true" style="width: 100%; opacity: 0.25;" data-astro-cid-kh7btl4r></div> <!-- Milestone: 2013 — PH founded --> <div class="timeline-node relative flex-1 lg:col-span-1 px-4 lg:px-2 pt-0" data-astro-cid-kh7btl4r> <div class="text-[14px] font-serif text-coral mb-2" data-astro-cid-kh7btl4r>2013</div> <div class="h-[28px] flex items-center justify-start" data-astro-cid-kh7btl4r> <div class="w-3 h-3 rounded-full bg-coral shadow-[0_0_0_4px_rgba(255,81,81,0.15)]" data-astro-cid-kh7btl4r></div> </div> <p class="mt-4 text-[14px] font-medium text-text-primary leading-snug" data-astro-cid-kh7btl4r>Property Hub founded</p> <p class="mt-1 text-[12px] text-text-tertiary font-light leading-[1.5]" data-astro-cid-kh7btl4r>Rob Bence &amp; Rob Dix start teaching what they've learned investing their own money.</p> </div> <!-- Milestone: 2013 — podcast --> <div class="timeline-node relative flex-1 lg:col-span-1 px-4 lg:px-2 pt-0" data-astro-cid-kh7btl4r> <div class="text-[14px] font-serif text-coral mb-2" data-astro-cid-kh7btl4r>2013</div> <div class="h-[28px] flex items-center justify-start" data-astro-cid-kh7btl4r> <div class="w-3 h-3 rounded-full bg-coral shadow-[0_0_0_4px_rgba(255,81,81,0.15)]" data-astro-cid-kh7btl4r></div> </div> <p class="mt-4 text-[14px] font-medium text-text-primary leading-snug" data-astro-cid-kh7btl4r>The Property Podcast launches</p> <p class="mt-1 text-[12px] text-text-tertiary font-light leading-[1.5]" data-astro-cid-kh7btl4r>Now the UK's #1 property podcast, 100k+ downloads a month.</p> </div> <!-- Milestone: 2014 — Invest --> <div class="timeline-node relative flex-1 lg:col-span-1 px-4 lg:px-2 pt-0" data-astro-cid-kh7btl4r> <div class="text-[14px] font-serif text-coral mb-2" data-astro-cid-kh7btl4r>2014</div> <div class="h-[28px] flex items-center justify-start" data-astro-cid-kh7btl4r> <div class="w-3 h-3 rounded-full bg-coral shadow-[0_0_0_4px_rgba(255,81,81,0.15)]" data-astro-cid-kh7btl4r></div> </div> <p class="mt-4 text-[14px] font-medium text-text-primary leading-snug" data-astro-cid-kh7btl4r>Property Hub Invest launches</p> <p class="mt-1 text-[12px] text-text-tertiary font-light leading-[1.5]" data-astro-cid-kh7btl4r>The done-with-you service begins sourcing for the first cohort of clients.</p> </div> <!-- Milestone: 2014 — first book --> <div class="timeline-node relative flex-1 lg:col-span-1 px-4 lg:px-2 pt-0" data-astro-cid-kh7btl4r> <div class="text-[14px] font-serif text-coral mb-2" data-astro-cid-kh7btl4r>2014</div> <div class="h-[28px] flex items-center justify-start" data-astro-cid-kh7btl4r> <div class="w-3 h-3 rounded-full bg-coral shadow-[0_0_0_4px_rgba(255,81,81,0.15)]" data-astro-cid-kh7btl4r></div> </div> <p class="mt-4 text-[14px] font-medium text-text-primary leading-snug" data-astro-cid-kh7btl4r>First book published</p> <p class="mt-1 text-[12px] text-text-tertiary font-light leading-[1.5]" data-astro-cid-kh7btl4r>Now 140,000+ copies of Rob's books sold worldwide.</p> </div> <!-- Milestone: 2018 --> <div class="timeline-node relative flex-1 lg:col-span-1 px-4 lg:px-2 pt-0" data-astro-cid-kh7btl4r> <div class="text-[14px] font-serif text-coral mb-2" data-astro-cid-kh7btl4r>2018</div> <div class="h-[28px] flex items-center justify-start" data-astro-cid-kh7btl4r> <div class="w-3 h-3 rounded-full bg-coral shadow-[0_0_0_4px_rgba(255,81,81,0.15)]" data-astro-cid-kh7btl4r></div> </div> <p class="mt-4 text-[14px] font-medium text-text-primary leading-snug" data-astro-cid-kh7btl4r>Property Pulse newsletter</p> <p class="mt-1 text-[12px] text-text-tertiary font-light leading-[1.5]" data-astro-cid-kh7btl4r>Now read weekly by 70,000+ UK property investors.</p> </div> <!-- Milestone: 2024 — Sunday Times --> <div class="timeline-node relative flex-1 lg:col-span-1 px-4 lg:px-2 pt-0" data-astro-cid-kh7btl4r> <div class="text-[14px] font-serif text-coral mb-2" data-astro-cid-kh7btl4r>2024</div> <div class="h-[28px] flex items-center justify-start" data-astro-cid-kh7btl4r> <div class="w-3 h-3 rounded-full bg-coral shadow-[0_0_0_4px_rgba(255,81,81,0.15)]" data-astro-cid-kh7btl4r></div> </div> <p class="mt-4 text-[14px] font-medium text-text-primary leading-snug" data-astro-cid-kh7btl4r>Sunday Times column</p> <p class="mt-1 text-[12px] text-text-tertiary font-light leading-[1.5]" data-astro-cid-kh7btl4r>Rob Dix becomes a weekly columnist for The Sunday Times.</p> </div> <!-- Milestone: today --> <div class="timeline-node relative flex-1 lg:col-span-1 px-4 lg:px-2 pt-0" data-astro-cid-kh7btl4r> <div class="text-[14px] font-serif text-coral mb-2" data-astro-cid-kh7btl4r>Today</div> <div class="h-[28px] flex items-center justify-start" data-astro-cid-kh7btl4r> <div class="w-3.5 h-3.5 rounded-full bg-coral shadow-[0_0_0_5px_rgba(255,81,81,0.18)] timeline-pulse" data-astro-cid-kh7btl4r></div> </div> <p class="mt-4 text-[14px] font-medium text-text-primary leading-snug" data-astro-cid-kh7btl4r>35+ team in Manchester</p> <p class="mt-1 text-[12px] text-text-tertiary font-light leading-[1.5]" data-astro-cid-kh7btl4r>Acquisitions, legal, research, client support &ndash; all under one roof.</p> </div> </div> </div> <!-- Mobile hint --> <p class="text-[12px] text-text-tertiary font-light text-center mt-4 lg:hidden" data-astro-cid-kh7btl4r>&larr; Scroll to explore &rarr;</p> </div> </div> </section>  <!-- TYPOGRAPHIC MOMENT --> <section class="bg-charcoal relative overflow-hidden" data-astro-cid-kh7btl4r> <div class="absolute inset-0 pointer-events-none" style="background: radial-gradient(ellipse 60% 80% at 20% 50%, rgba(255, 81, 81, 0.09) 0%, transparent 55%), radial-gradient(ellipse 50% 60% at 85% 50%, rgba(201, 169, 110, 0.05) 0%, transparent 50%);" data-astro-cid-kh7btl4r></div> <div class="absolute inset-0 pointer-events-none" style="background-image: var(--grain); background-repeat: repeat; background-size: 256px 256px; opacity: 0.04; mix-blend-mode: soft-light;" data-astro-cid-kh7btl4r></div> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 py-24 lg:py-36 relative z-[1]" data-astro-cid-kh7btl4r> <div class="reveal" data-reveal data-astro-cid-kh7btl4r> <p class="text-coral text-[12px] font-medium uppercase tracking-[0.12em] mb-8" data-astro-cid-kh7btl4r>Last year alone</p> <p class="font-serif text-white leading-[0.9] tracking-[-0.04em]" style="font-size: clamp(80px, 14vw, 200px);" data-astro-cid-kh7btl4r>
116&pound;100m+
117</p> <p class="text-[18px] sm:text-[22px] text-white/70 font-light mt-8 max-w-xl leading-[1.5]" data-astro-cid-kh7btl4r>
118of UK property acquired on behalf of our investors.
119</p> </div> </div> </section> <!-- WHO WE ARE --> <section class="py-24 lg:py-32 bg-surface relative overflow-hidden surface-editorial" data-astro-cid-kh7btl4r> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 relative z-[1]" data-astro-cid-kh7btl4r> <div class="max-w-3xl reveal" data-reveal data-astro-cid-kh7btl4r> <p class="text-coral text-[12px] font-medium uppercase tracking-[0.12em] mb-3" data-astro-cid-kh7btl4r>The team</p> <h2 class="font-serif text-[32px] sm:text-[40px] lg:text-[52px] text-text-primary leading-[1.1] tracking-[-0.02em]" data-astro-cid-kh7btl4r>
120Who we are
121</h2> <p class="mt-5 text-[18px] text-text-secondary font-light leading-[1.7]" data-astro-cid-kh7btl4r>
122We're a Manchester-based team of 35+ across acquisitions, legal coordination, research and client support. When you work with us, you're working with people who spend their days evaluating developments, negotiating with developers, and handling the complexity so you don't have to.
123</p> </div> <!-- Team carousel --> <div class="mt-14 relative" id="team-carousel" data-astro-cid-kh7btl4r> <div class="overflow-hidden" id="team-track-wrapper" data-astro-cid-kh7btl4r> <div class="flex gap-8 transition-transform duration-500 ease-out" id="team-track" style="will-change: transform;" data-astro-cid-kh7btl4r> <div class="flex-shrink-0 w-[300px] sm:w-[380px]" data-astro-cid-kh7btl4r> <div class="bg-surface rounded-[12px] overflow-hidden border border-border-light transition-[transform,box-shadow,border-color] duration-250 hover:shadow-card hover:-translate-y-1 hover:border-coral/20" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r> <div class="aspect-[4/3] overflow-hidden bg-background" data-astro-cid-kh7btl4r> <img src="/images/team/rob-bence-headshot.png" alt="Rob Bence" class="w-full h-full object-cover object-[center_20%]" loading="lazy" data-astro-cid-kh7btl4r> </div> <div class="p-5 sm:p-6" data-astro-cid-kh7btl4r> <h3 class="text-lg font-medium text-text-primary" data-astro-cid-kh7btl4r>Rob Bence</h3>
123 <p class="text-sm text-coral font-medium mt-1" data-astro-cid-kh7btl4r>Co-founder</p> <p class="text-sm text-text-secondary font-light leading-relaxed mt-3" data-astro-cid-kh7btl4r>Built Property Hub into a business facilitating over £100m in annual property transactions.</p> </div> </div> </div><div class="flex-shrink-0 w-[300px] sm:w-[380px]" data-astro-cid-kh7btl4r> <div class="bg-surface rounded-[12px] overflow-hidden border border-border-light transition-[transform,box-shadow,border-color] duration-250 hover:shadow-card hover:-translate-y-1 hover:border-coral/20" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r> <div class="aspect-[4/3] overflow-hidden bg-background" data-astro-cid-kh7btl4r> <img src="/images/team/rob-dix.jpg" alt="Rob Dix" class="w-full h-full object-cover object-[center_20%]" loading="lazy" data-astro-cid-kh7btl4r> </div> <div class="p-5 sm:p-6" data-astro-cid-kh7btl4r> <h3 class="text-lg font-medium text-text-primary" data-astro-cid-kh7btl4r>Rob Dix</h3> <p class="text-sm text-coral font-medium mt-1" data-astro-cid-kh7btl4r>Co-founder &amp; Author</p> <p class="text-sm text-text-secondary font-light leading-relaxed mt-3" data-astro-cid-kh7btl4r>Author of seven best-selling books, co-founder, and an unashamed property geek.</p> </div> </div> </div><div class="flex-shrink-0 w-[300px] sm:w-[380px]" data-astro-cid-kh7btl4r> <div class="bg-surface rounded-[12px] overflow-hidden border border-border-light transition-[transform,box-shadow,border-color] duration-250 hover:shadow-card hover:-translate-y-1 hover:border-coral/20" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r> <div class="aspect-[4/3] overflow-hidden bg-background" data-astro-cid-kh7btl4r> <img src="/images/team/ben-speaker.jpg" alt="Tracey Jones" class="w-full h-full object-cover object-[center_20%]" loading="lazy" data-astro-cid-kh7btl4r> </div> <div class="p-5 sm:p-6" data-astro-cid-kh7btl4r> <h3 class="text-lg font-medium text-text-primary" data-astro-cid-kh7btl4r>Tracey Jones</h3> <p class="text-sm text-coral font-medium mt-1" data-astro-cid-kh7btl4r>Managing Director</p> <p class="text-sm text-text-secondary font-light leading-relaxed mt-3" data-astro-cid-kh7btl4r>Leads our strategy, performance and delivery - balancing business growth with high-quality execution.</p> </div> </div> </div><div class="flex-shrink-0 w-[300px] sm:w-[380px]" data-astro-cid-kh7btl4r> <div class="bg-surface rounded-[12px] overflow-hidden border border-border-light transition-[transform,box-shadow,border-color] duration-250 hover:shadow-card hover:-translate-y-1 hover:border-coral/20" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r> <div class="aspect-[4/3] overflow-hidden bg-background" data-astro-cid-kh7btl4r> <img src="/images/team/jenny-portrait.jpg" alt="Sam Smith" class="w-full h-full object-cover object-[center_20%]" loading="lazy" data-astro-cid-kh7btl4r> </div> <div class="p-5 sm:p-6" data-astro-cid-kh7btl4r> <h3 class="text-lg font-medium text-text-primary" data-astro-cid-kh7btl4r>Sam Smith</h3> <p class="text-sm text-coral font-medium mt-1" data-astro-cid-kh7btl4r>Director of Investments &amp; Partnerships</p> <p class="text-sm text-text-secondary font-light leading-relaxed mt-3" data-astro-cid-kh7btl4r>Leads the sourcing and evaluation of new developments, shaping the investment opportunities we bring to market.</p> </div> </div> </div><div class="flex-shrink-0 w-[300px] sm:w-[380px]" data-astro-cid-kh7btl4r> <div class="bg-surface rounded-[12px] overflow-hidden border border-border-light transition-[transform,box-shadow,border-color] duration-250 hover:shadow-card hover:-translate-y-1 hover:border-coral/20" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r> <div class="aspect-[4/3] overflow-hidden bg-background" data-astro-cid-kh7btl4r> <img src="/images/team/sophie-portrait.jpg" alt="Zoe Cairney" class="w-full h-full object-cover object-[center_20%]" loading="lazy" data-astro-cid-kh7btl4r> </div> <div class="p-5 sm:p-6" data-astro-cid-kh7btl4r> <h3 class="text-lg font-medium text-text-primary" data-astro-cid-kh7btl4r>
123Zoe Cairney</h3> <p class="text-sm text-coral font-medium mt-1" data-astro-cid-kh7btl4r>Sales Progression Manager</p> <p class="text-sm text-text-secondary font-light leading-relaxed mt-3" data-astro-cid-kh7btl4r>Responsible for our client experience: from reservation to completion, keeping purchases moving and on track.</p> </div> </div> </div> </div> </div> <!-- Carousel nav buttons --> <button id="team-prev" class="absolute left-0 top-[140px] sm:top-[180px] -translate-x-3 sm:-translate-x-6 w-12 h-12 sm:w-14 sm:h-14 rounded-full bg-coral flex items-center justify-center text-white hover:bg-coral-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coral active:scale-95 transition-[background-color,transform] duration-200 z-10" aria-label="Previous team member" data-astro-cid-kh7btl4r> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" data-astro-cid-kh7btl4r> <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" data-astro-cid-kh7btl4r></path> </svg> </button> <button id="team-next" class="absolute right-0 top-[140px] sm:top-[180px] translate-x-3 sm:translate-x-6 w-12 h-12 sm:w-14 sm:h-14 rounded-full bg-coral flex items-center justify-center text-white hover:bg-coral-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coral active:scale-95 transition-[background-color,transform] duration-200 z-10" aria-label="Next team member" data-astro-cid-kh7btl4r> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" data-astro-cid-kh7btl4r> <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" data-astro-cid-kh7btl4r></path> </svg> </button> </div> </div> </section> <!-- WE'RE GROWING — dark section --> <section class="py-24 lg:py-32 bg-charcoal relative overflow-hidden" data-astro-cid-kh7btl4r> <!-- Radial glows --> <div class="absolute inset-0 pointer-events-none" style="background: radial-gradient(ellipse at 20% 50%, rgba(255, 81, 81, 0.06), transparent 50%), radial-gradient(ellipse at 80% 50%, rgba(201, 169, 110, 0.04), transparent 50%);" data-astro-cid-kh7btl4r></div> <!-- Grain --> <div class="absolute inset-0 pointer-events-none" style="background-image: var(--grain); background-repeat: repeat; background-size: 256px 256px; opacity: 0.04; mix-blend-mode: soft-light;" data-astro-cid-kh7btl4r></div> <div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 text-center relative z-[1] reveal" data-reveal data-astro-cid-kh7btl4r> <h2 class="font-serif text-[32px] sm:text-[40px] text-white leading-[1.1] tracking-[-0.02em]" data-astro-cid-kh7btl4r>
124We're growing.
125</h2> <p class="mt-4 text-[18px] text-white/70 font-light leading-[1.7]" data-astro-cid-kh7btl4r>
126If you're interested in joining a winning team, check out our current vacancies below.
127</p> <a href="/jobs/" class="inline-block mt-8 px-8 py-[14px] bg-coral text-white font-medium text-[16px] rounded-[8px] shadow-coral-cta hover:bg-coral-hover hover:shadow-coral-hover hover:-translate-y-[1px] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coral transition-[transform,background-color,box-shadow] duration-250" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r>
128View our current vacancies
129</a> </div> </section> <!-- WHAT'S NEXT --> <section class="py-24 lg:py-32 bg-background-warm relative overflow-hidden surface-editorial" data-astro-cid-kh7btl4r> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 relative z-[1]" data-astro-cid-kh7btl4r> <div class="text-center mb-12 reveal" data-reveal data-astro-cid-kh7btl4r> <p class="text-coral text-[12px] font-medium uppercase tracking-[0.12em] mb-3" data-astro-cid-kh7btl4r>Get started</p> <h2 class="font-serif text-[32px] sm:text-[40px] text-text-primary leading-[1.15] tracking-[-0.015em]" data-astro-cid-kh7btl4r>
130What's next?
131</h2> <p class="mt-4 text-[18px] text-text-secondary font-light leading-[1.7] max-w-2xl mx-auto" data-astro-cid-kh7btl4r>
132If you've read this far, you're probably in one of two camps:
133</p> </div> <div class="grid md:grid-cols-2 gap-8 reveal-stagger" data-reveal-stagger data-astro-cid-kh7btl4r> <!-- Learn CTA — light card --> <div class="bg-surface rounded-[12px] border border-border-light p-8 transition-[transform,box-shadow,border-color] duration-250 hover:shadow-card hover:-translate-y-1 hover:border-coral/20" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r> <div class="text-center" data-astro-cid-kh7btl4r> <img src="/images/pages/property-pulse-laptop.png" alt="Property Pulse newsletter on laptop" class="w-full h-auto mb-4" loading="lazy" data-astro-cid-kh7btl4r> <div class="mt-2" data-astro-cid-kh7btl4r> <svg class="w-8 h-8 mx-auto text-coral" fill="currentColor" viewBox="0 0 24 24" data-astro-cid-kh7btl4r><path d="M12 3L1 9l4 2.18v6L12 21l7-3.82v-6l2-1.09V17h2V9L12 3zm
1336.82 6L12 12.72 5.18 9 12 5.28 18.82 9zM17 15.99l-5 2.73-5-2.73v-3.72L12 15l5-2.73v3.72z" data-astro-cid-kh7btl4r></path></svg> </div> <p class="text-coral text-[12px] font-medium uppercase tracking-[0.12em] mt-3 mb-2" data-astro-cid-kh7btl4r>Free Resources</p> <h3 class="text-[24px] font-medium text-text-primary" data-astro-cid-kh7btl4r>You might want to keep learning</h3> <p class="mt-3 text-[16px] text-text-secondary font-light leading-[1.7]" data-astro-cid-kh7btl4r>
134In which case, our free weekly newsletter is the best place to start. Market commentary, expert insights, and lessons from arranging hundreds of transactions every year.
135</p> <div class="mt-8" data-astro-cid-kh7btl4r> <a href="/pulse/" class="inline-flex items-center px-6 py-3 bg-transparent text-text-primary font-medium text-[16px] rounded-[8px] border-[1.5px] border-border hover:border-text-primary hover:bg-background hover:-translate-y-[1px] transition-[transform,border-color,background-color] duration-250" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r>
136Sign up for free
137</a> </div> </div> </div> <!-- Invest CTA — premium dark card --> <div class="bg-charcoal rounded-[16px] p-8 relative overflow-hidden transition-[transform,box-shadow] duration-250 hover:shadow-elevated hover:-translate-y-1" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r> <!-- Radial glows --> <div class="absolute inset-0 pointer-events-none" style="background: radial-gradient(ellipse at 15% 80%, rgba(255, 81, 81, 0.06), transparent 50%), radial-gradient(ellipse at 85% 20%, rgba(201, 169, 110, 0.04), transparent 50%);" data-astro-cid-kh7btl4r></div> <div class="absolute inset-0 pointer-events-none" style="background-image: var(--grain); background-repeat: repeat; background-size: 256px 256px; opacity: 0.04; mix-blend-mode: soft-light;" data-astro-cid-kh7btl4r></div> <div class="relative z-[1] text-center" data-astro-cid-kh7btl4r> <img src="/images/pages/ph-invest-laptop.png" alt="Property Hub Invest on laptop" class="w-full h-auto mb-4" loading="lazy" data-astro-cid-kh7btl4r> <div class="mt-2" data-astro-cid-kh7btl4r> <svg class="w-8 h-8 mx-auto text-warm-gold" fill="currentColor" viewBox="0 0 24 24" data-astro-cid-kh7btl4r><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" data-astro-cid-kh7btl4r></path></svg> </div> <p class="text-warm-gold text-[12px] font-medium uppercase tracking-[0.12em] mt-3 mb-2" data-astro-cid-kh7btl4r>Managed Service</p> <h3 class="text-[24px] font-medium text-white" data-astro-cid-kh7btl4r>You might be ready to talk about a portfolio</h3> <p class="mt-3 text-[16px] text-white/75 font-light leading-[1.7]" data-astro-cid-kh7btl4r>
138Book a free strategy call and we'll take the time to figure out together whether this approach makes sense for you and your goals.
139</p> <div class="mt-8" data-astro-cid-kh7btl4r> <a href="/invest/" class="inline-flex items-center px-6 py-3 bg-coral text-white font-medium text-[16px] rounded-[8px] shadow-coral-cta hover:bg-coral-hover hover:shadow-coral-hover hover:-translate-y-[1px] transition-[transform,background-color,box-shadow] duration-250" style="transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);" data-astro-cid-kh7btl4r>
140Book a call
141</a> </div> </div> </div> </div> </div> </section> </main> <footer class="bg-charcoal border-t border-white/10 py-16 lg:py-20 relative overflow-hidden" data-astro-cid-sz7xmlte> <!-- Top accent line --> <div class="absolute top-0 left-0 right-0 h-[2px]" style="background: linear-gradient(90deg, #FF5151 0%, transparent 100%);" data-astro-cid-sz7xmlte></div> <!-- Radial glows --> <div class="absolute inset-0 pointer-events-none" style="background: radial-gradient(ellipse at 15% 50%, rgba(255, 81, 81, 0.06), transparent 50%), radial-gradient(ellipse at 85% 20%, rgba(201, 169, 110, 0.04), transparent 50%);" data-astro-cid-sz7xmlte></div> <!-- Grain --> <div class="absolute inset-0 pointer-events-none" style="background-image: var(--grain); background-repeat: repeat; background-size: 256px 256px; opacity: 0.04; mix-blend-mode: soft-light;" data-astro-cid-sz7xmlte></div> <div class="max-w-[1200px] mx-auto px-4 sm:px-6 lg:px-8 relative z-[1]" data-astro-cid-sz7xmlte> <div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-10 lg:gap-12" data-astro-cid-sz7xmlte> <div data-astro-cid-sz7xmlte> <a href="/" class="inline-block" data-astro-cid-sz7xmlte> <img src="/property-hub-logo.svg" alt="Property Hub" class="h-[56px] w-auto" data-astro-cid-sz7xmlte> </a> <div class="flex gap-4 mt-7" data-astro-cid-sz7xmlte> <a href="https://www.facebook.com/propertyhubuk/" target="_blank" rel="noopener noreferrer" class="text-white/70 hover:text-white active:text-white active:scale-95 [touch-action:manipulation] transition-[transform,color] duration-150" aria-label="Facebook" data-astro-cid-sz7xmlte> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" data-astro-cid-sz7xmlte><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" data-astro-cid-sz7xmlte></path></svg> </a> <a href="https://www.instagram.com/propertyhubuk/" target="_blank" rel="noopener noreferrer" class="text-white/70 hover:text-white active:text-white active:scale-95 [touch-action:manipulation] transition-[transform,color] duration-150" aria-label="Instagram" data-astro-cid-sz7xmlte> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" data-astro-cid-sz7xmlte><path d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.
141047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z" data-astro-cid-sz7xmlte></path></svg> </a> <a href="https://www.linkedin.com/company/thepropertyhub" target="_blank" rel="noopener noreferrer" class="text-white/70 hover:text-white active:text-white active:scale-95 [touch-action:manipulation] transition-[transform,color] duration-150" aria-label="LinkedIn" data-astro-cid-sz7xmlte> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" data-astro-cid-sz7xmlte><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" data-astro-cid-sz7xmlte></path></svg> </a> <a href="https://www.youtube.com/propertyhubuk" target="_blank" rel="noopener noreferrer" class="text-white/70 hover:text-white active:text-white active:scale-95 [touch-action:manipulation] transition-[transform,color] duration-150" aria-label="YouTube" data-astro-cid-sz7xmlte> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" data-astro-cid-sz7xmlte><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" data-astro-cid-sz7xmlte></path></svg> </a> </div> </div> <div data-astro-cid-sz7xmlte> <p class="footer-label text-[11px] text-white/60 mb-5" data-astro-cid-sz7xmlte>Build Your Portfolio</p> <ul class="space-y-3" data-astro-cid-sz7xmlte> <li data-astro-cid-sz7xmlte><a href="/invest/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Invest With Us</a></li> <li data-astro-cid-sz7xmlte><a href="/invest/book-a-consultation/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Book a Consultation</a></li> <li data-astro-cid-sz7xmlte><a href="/invest/case-studies/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Past Deals &amp; Case Studies</a></li> </ul> </div> <div data-astro-cid-sz7xmlte> <p class="footer-label text-[11px] text-white/60 mb-5" data-astro-cid-sz7xmlte>Read &amp; Learn</p> <ul class="space-y-3" data-astro-cid-sz7xmlte> <li data-astro-cid-sz7xmlte><a href="/learn/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Start Here</a></li> <li data-astro-cid-sz7xmlte><a href="/guides/getting-started/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Investing Guides</a></li> <li data-astro-cid-sz7xmlte><a href="/news/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Latest News</a></li> <li data-astro-cid-sz7xmlte><a href="/free-tools/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Investor Toolkit</a></li> </ul> </div> <div data-astro-cid-sz7xmlte> <p class="footer-label text-[11px] text-white/60 mb-5" data-astro-cid-sz7xmlte>Company</p> <ul class="space-y-3" data-astro-cid-sz7xmlte> <li data-astro-cid-sz7xmlte><a href="/about/" class="footer-link text-[13px] hover:text-white transition-colors duration-150 text-coral" data-astro-cid-sz7xmlte>About</a></li> <li data-astro-cid-sz7xmlte><a href="/contact/" class="footer-link text-[13px] hover:text-white transition-colors duration-150 text-white/70" data-astro-cid-sz7xmlte>Contact</a></li> <li data-astro-cid-sz7xmlte><a href="/jobs/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Jobs</a></li> <li data-astro-cid-sz7xmlte><a href="/developers/" class="footer-link text-[13px] hover:text-white transition-colors duration-150 text-white/70" data-astro-cid-sz7xmlte>Developers</a></li> <li data-astro-cid-sz7xmlte><a href="/charities/" class="footer-link text-[13px] text-white/70 hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Charities</a></li> </ul> </div> </div> <!-- Legal strip --> <div class="mt-14 pt-8 border-t border-white/10 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between" data-astro-cid-sz7xmlte> <div class="space-y-1" data-astro-cid-sz7xmlte> <p class="footer-link text-white/60 text-[12px] leading-[1.5]" data-astro-cid-sz7xmlte>Property Hub &copy; 2026 All rights reserved. Property Hub Ltd. Registered in England and Wales.</p> <p class="footer-link text-white/60 text-[12px] leading-[1.5]" data-astro-cid-sz7xmlte>Property Hub Invest Ltd (07495608). 1st Floor B, 117-119 Portland Street, Manchester, M1 6ED.</p> </div> <nav class="flex flex-wrap gap-x-6 gap-y-2 flex-shrink-0" data-astro-cid-sz7xmlte> <a href="/privacy-policy/" class="footer-link text-white/70 text-[13px] hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Privacy Policy</a> <a href="/terms-conditions/" class="footer-link text-white/70 text-[13px] hover:text-white transition-colors duration-150" data-astro-cid-sz7xmlte>Terms &amp; Conditions</a> </nav> </div> </div> </footer>  
141<script>
142  (function () {
143    const STORAGE_KEY = 'ph_attribution_v1';
144    const UTM_KEYS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
145    // Ad-platform click IDs, plus our own short-link id (appended by the /go/*
146    // edge function). Captured + stored like UTMs (first-touch wins within the
147    // session) so they survive navigation before a form is submitted.
148    const CLICK_ID_KEYS = ['gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid', 'ph_link_id'];
149
150    function readCookie(name) {
151      const prefix = name + '=';
152      const part = document.cookie
153        .split(';')
154        .map((value) => value.trim())
155        .find((value) => value.indexOf(prefix) === 0);
156      return part ? decodeURIComponent(part.slice(prefix.length)) : '';
157    }
158
159    function hasAdvertisingConsent() {
160      const consent = readCookie('cookieyes-consent');
161      return /(?:^|,)advertisement:(?:yes|true)(?:,|$)/i.test(consent);
162    }
163
164    function readStored() {
165      try {
166        const raw = sessionStorage.getItem(STORAGE_KEY);
167        return raw ? JSON.parse(raw) : null;
168      } catch (e) {
169        return null;
170      }
171    }
172
173    function writeStored(data) {
174      try {
175        sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data));
176      } catch (e) {
177        /* sessionStorage unavailable — fail silently */
178      }
179    }
180
181    function captureFromUrl() {
182      const params = new URLSearchParams(window.location.search);
183      const stored = readStored() || {};
184
185      let updated = false;
186      UTM_KEYS.concat(CLICK_ID_KEYS).forEach((key) => {
187        const val = params.get(key);
188        if (val && !stored[key]) {
189          stored[key] = val;
190          if (key === 'fbclid') stored.fbclid_captured_at = Date.now();
191          updated = true;
192        }
193      });
194
195      if (!stored.landing_page) {
196        stored.landing_page = window.location.pathname + window.location.search;
197        updated = true;
198      }
199      if (!stored.referrer) {
200        stored.referrer = document.referrer || '';
201        updated = true;
202      }
203
204      if (updated) writeStored(stored);
205      return stored;
206    }
207
208    function generateSubmissionId() {
209      try {
210        if (window.crypto && typeof window.crypto.randomUUID === 'function') {
211          return window.crypto.randomUUID();
212        }
213      } catch (e) {
214        /* fall through to manual generation */
215      }
216      // RFC4122-ish fallback for older browsers.
217      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
218        var r = (Math.random() * 16) | 0;
219        var v = c === 'x' ? r : (r & 0x3) | 0x8;
220        return v.toString(16);
221      });
222    }
223
224    function injectHidden(form, name, value) {
225      // Set the value on an EXISTING input if there is one, otherwise create it.
226      // The attribution fields are also declared as static empty hidden inputs in
227      // LeadForm so that Netlify registers them in the form's build-time schema —
228      // Netlify only records fields it saw in the static HTML, and silently drops
229      // POSTed fields it didn't (this is why utm_* / referrer / submitted_url were
230      // missing from submissions). So here we must UPDATE the existing input's
231      // value rather than bail, then fall back to creating one for any form that
232      // doesn't declare it statically.
233      const val = value == null ? '' : value;
234      const existing = form.querySelector('input[name="' + name + '"]');
235      if (existing) {
236        existing.value = val;
237        return;
238      }
239      const input = document.createElement('input');
240      input.type = 'hidden';
241      input.name = name;
242      input.value = val;
243      form.appendChild(input);
244    }
245
246    function attachToForms(attribution) {
247      document.querySelectorAll('form[data-ph-form]').forEach((form) => {
248        form.addEventListener(
249          'submit',
250          function () {
251            const fresh = readStored() || attribution;
252            UTM_KEYS.forEach((key) => injectHidden(form, key, fresh[key] || ''));
253            CLICK_ID_KEYS.forEach((key) => injectHidden(form, key, fresh[key] || ''));
254            const advertisingConsent = hasAdvertisingConsent();
255            const fbp = advertisingConsent ? readCookie('_fbp') : '';
256            const cookieFbc = advertisingConsent ? readCookie('_fbc') : '';
257            const derivedFbc =
258              advertisingConsent && fresh.fbclid
259                ? 'fb.1.' + String(fresh.fbclid_captured_at || Date.now()) + '.' + fresh.fbclid
260                : '';
261            injectHidden(form, 'fbp', fbp);
262            injectHidden(form, 'fbc', cookieFbc || derivedFbc);
263            injectHidden(form, 'advertising_consent', advertisingConsent ? 'true' : 'false');
264            injectHidden(form, 'landing_page', fresh.landing_page || '');
265            injectHidden(form, 'referrer', fresh.referrer || '');
266            injectHidden(form, 'submitted_url', window.location.href);
267            injectHidden(form, 'submission_id', generateSubmissionId());
268          },
269          { capture: true }
270        );
271      });
272    }
273
274    const attribution = captureFromUrl();
275
276    if (document.readyState === 'loading') {
277      document.addEventListener('DOMContentLoaded', function () {
278        attachToForms(attribution);
279      });
280    } else {
281      attachToForms(attribution);
282    }
283  })();
284</script>
284 
284<script>
285  (function () {
286    var PARDOT_HANDLER_URL = 'https://go.propertyhub.net/l/641323/2026-02-16/h8hb71';
287
288    function postToPardot(form) {
289      try {
290        var emailInput = form.querySelector('input[type="email"], input[name="email"]');
291        if (!emailInput || !emailInput.value) return;
292
293        var body = new URLSearchParams();
294        body.append('email', emailInput.value);
295
296        var firstNameInput = form.querySelector('input[name="first_name"]');
297        if (firstNameInput && firstNameInput.value) {
298          body.append('first_name', firstNameInput.value);
299        }
300        var lastNameInput = form.querySelector('input[name="last_name"]');
301        if (lastNameInput && lastNameInput.value) {
302          body.append('last_name', lastNameInput.value);
303        }
304
305        // Fire-and-forget. mode:'no-cors' avoids CORS preflight; credentials:'include'
306        // ensures the Pardot tracking cookie travels with the request so Pardot can
307        // stitch the anonymous visitor to this prospect's email.
308        fetch(PARDOT_HANDLER_URL, {
309          method: 'POST',
310          body: body,
311          mode: 'no-cors',
312          credentials: 'include',
313        });
314      } catch (e) {
315        /* never block the real form submission */
316      }
317    }
318
319    // Some forms are not investor lead-gen (e.g. the developer/B2B enquiry form)
320    // and must NOT push contacts into the investor Pardot/Salesforce. Opt out
321    // with data-no-pardot on the form.
322    document.querySelectorAll('form[data-ph-form]:not([data-no-pardot])').forEach(function (form) {
323      form.addEventListener(
324        'submit',
325        function () {
326          postToPardot(form);
327        },
328        { capture: true }
329      );
330    });
331  })();
332</script>
332 
332<script type="module" src="/_astro/PhoneCountryInput.astro_astro_type_script_index_0_lang.CZn1wpMa.js"></script>
332  
332<script>
333  (function () {
334    function appendToUrl(rawUrl, params) {
335      try {
336        const url = new URL(rawUrl, window.location.origin);
337        params.forEach(([key, value]) => {
338          if (value && !url.searchParams.has(key)) {
339            url.searchParams.set(key, value);
340          }
341        });
342        const isAbsolute = /^https?:\/\//i.test(rawUrl);
343        return isAbsolute ? url.toString() : url.pathname + url.search + url.hash;
344      } catch (e) {
345        return rawUrl;
346      }
347    }
348
349    function buildRedirectUrl(form, formData) {
350      // The browser-visible success destination is deliberately separate from
351      // the form action: Netlify requires form actions to remain root-relative,
352      // while Invest submissions redirect to an external Calendly URL.
353      const action = form.dataset.successUrl || form.getAttribute('action') || '/';
354      const params = [];
355      // Never put submitted form fields in a redirect URL. URLs are copied
356      // into browser history, request logs and analytics page locations. Only
357      // the approved campaign fields are safe to carry to the destination.
358      ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach((key) => {
359        const val = formData.get(key);
360        if (val) params.push([key, String(val)]);
361      });
362      return appendToUrl(action, params);
363    }
364
365    function showThankYou(form) {
366      const selector = form.dataset.successTarget;
367      const target = selector
368        ? document.querySelector(selector)
369        : (form.parentElement && form.parentElement.querySelector('[data-thank-you]')) || null;
370      form.hidden = true;
371      if (target) {
372        target.hidden = false;
373        target.scrollIntoView({ behavior: 'smooth', block: 'center' });
374      }
375    }
376
377    const TRACKED_OUTCOMES = new Set([
378      'generate_lead',
379      'developer_lead',
380      'subscriber_signup',
381      'webinar_registration',
382    ]);
383
384    function safeValue(formData, key) {
385      const value = formData.get(key);
386      return typeof value === 'string' ? value : '';
387    }
388
389    function formLocation(form) {
390      if (form.dataset.formLocation) return form.dataset.formLocation;
391      const section = form.closest('section[id]');
392      return section ? section.id : 'inline';
393    }
394
395    function trackingPayload(form, formData, eventName) {
396      const advertisingConsent = safeValue(formData, 'advertising_consent') === 'true';
397      return {
398        event: eventName,
399        analytics_event: safeValue(formData, 'analytics_event'),
400        submission_id: safeValue(formData, 'submission_id'),
401        event_id: safeValue(formData, 'submission_id'),
402        form_name: form.getAttribute('name') || '',
403        form_type: safeValue(formData, 'form_type'),
404        form_location: formLocation(form),
405        form_label: safeValue(formData, 'form_label'),
406        signup_type: safeValue(formData, 'signup_type'),
407        asset_id: safeValue(formData, 'asset_id'),
408        asset_name: safeValue(formData, 'asset_name'),
409        asset_type: safeValue(formData, 'asset_type'),
410        webinar_id:
411          safeValue(formData, 'analytics_event') === 'webinar_registration'
412            ? safeValue(formData, 'asset_id')
413            : '',
414        provider: safeValue(formData, 'provider'),
415        advertising_consent: advertisingConsent,
416        // Meta browser/server identifiers are only present after advertising
417        // consent. They are deliberately kept out of the data layer otherwise.
418        fbp: advertisingConsent ? safeValue(formData, 'fbp') : '',
419        fbc: advertisingConsent ? safeValue(formData, 'fbc') : '',
420        source_page: safeValue(formData, 'source_page') || window.location.pathname,
421      };
422    }
423
424    function pushOutcome(form, formData, eventName, onComplete) {
425      const analyticsEvent = safeValue(formData, 'analytics_event');
426      if (!TRACKED_OUTCOMES.has(analyticsEvent)) {
427        onComplete();
428        return;
429      }
430
431      window.dataLayer = window.dataLayer || [];
432      let completed = false;
433      const finish = function () {
434        if (completed) return;
435        completed = true;
436        window.clearTimeout(fallback);
437        onComplete();
438      };
439      const fallback = window.setTimeout(finish, 1200);
440      window.dataLayer.push({
441        ...trackingPayload(form, formData, eventName),
442        eventCallback: finish,
443        eventTimeout: 1000,
444      });
445    }
446
447    // Inline submission error, announced to assistive tech via role="alert".
448    // Created lazily per form, re-used on repeat failures, cleared on retry.
449    function clearFormError(form) {
450      const el = form.querySelector('[data-form-error]');
451      if (el) el.hidden = true;
452    }
453
454    function showFormError(form) {
455      let el = form.querySelector('[data-form-error]');
456      if (!el) {
457        el = document.createElement('div');
458        el.setAttribute('data-form-error', '');
459        el.setAttribute('role', 'alert');
460        el.style.cssText =
461          'margin-top:12px;padding:12px 16px;border-radius:8px;font-size:14px;' +
462          'background:rgba(255,81,81,0.10);border:1px solid rgba(255,81,81,0.45);color:inherit;';
463        const submitBtn = form.querySelector('button[type="submit"]');
464        if (submitBtn && submitBtn.parentElement === form) {
465          submitBtn.insertAdjacentElement('afterend', el);
466        } else {
467          form.appendChild(el);
468        }
469      }
470      el.textContent =
471        "Sorry, something went wrong sending your details. Check your connection and try again — nothing has been submitted.";
472      el.hidden = false;
473    }
474
475    document.querySelectorAll('form[data-ph-form]').forEach((form) => {
476      form.addEventListener('submit', async (event) => {
477        event.preventDefault();
478        clearFormError(form);
479        const submitBtn = form.querySelector('button[type="submit"]');
480        if (submitBtn) submitBtn.disabled = true;
481
482        // Netlify uses a field named `title` for the submission summary shown in
483        // its dashboard. Populate it from the submitted email here, before
484        // FormData snapshots the form. This field is sent only to Netlify; the
485        // analytics payload below is explicitly allow-listed and excludes it.
486        const emailInput = form.querySelector('input[type="email"], input[name="email"]');
487        const titleInput = form.querySelector('input[name="title"]');
488        if (emailInput instanceof HTMLInputElement && titleInput instanceof HTMLInputElement) {
489          titleInput.value = emailInput.value.trim();
490        }
491
492        const formData = new FormData(form);
493        const body = new URLSearchParams();
494        formData.forEach((value, key) => {
495          body.append(key, typeof value === 'string' ? value : '');
496        });
497
498        // Netlify routes an AJAX-submitted form by a `form-name` field in the
499        // POST body (the form's `name` attribute alone isn't enough once we
500        // intercept and fetch() ourselves). Without it Netlify returns 404.
501        const netlifyFormName = form.getAttribute('name');
502        if (netlifyFormName) body.set('form-name', netlifyFormName);
503
504        // Normalise EVERY checkbox to a single true/false (applies to all forms:
505        // newsletter consent, sunday-times publish_consent, survey unsubscribe
506        // boxes). HTML omits an unticked box entirely, so set an explicit value
507        // from the checkbox state so downstream (Salesforce booleans, Zaps) gets
508        // a clean value. Do NOT pair a checkbox with a hidden default of the
509        // same name: a native (no-JS) submit then sends both ("false,true") and
510        // Salesforce rejects the enquiry. Replaces the old "on"/absent.
511        form.querySelectorAll('input[type="checkbox"][name]').forEach((cb) => {
512          body.set(cb.name, cb.checked ? 'true' : 'false');
513        });
514
515        try {
516          const res = await fetch('/', {
517            method: 'POST',
518            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
519            body: body.toString(),
520          });
521          if (!res.ok) throw new Error('HTTP ' + res.status);
522
523          pushOutcome(form, formData, 'ph_form_success', function () {
524            const isInline = form.hasAttribute('data-inline-success');
525            if (isInline) {
526              showThankYou(form);
527            } else {
528              window.location.href = buildRedirectUrl(form, formData);
529            }
530          });
531        } catch (err) {
532          console.error('Form submission failed:', err);
533          const analyticsEvent = safeValue(formData, 'analytics_event');
534          if (TRACKED_OUTCOMES.has(analyticsEvent)) {
535            window.dataLayer = window.dataLayer || [];
536            window.dataLayer.push({
537              ...trackingPayload(form, formData, 'ph_form_error'),
538              error_type: err instanceof Error && /^HTTP /.test(err.message) ? 'http_error' : 'network_error',
539              http_status:
540                err instanceof Error && /^HTTP \d+$/.test(err.message)
541                  ? err.message.slice(5)
542                  : '',
543            });
544          }
545          if (submitBtn) submitBtn.disabled = false;
546          showFormError(form);
547        }
548      });
549    });
550
551    document.addEventListener('click', function (event) {
552      const target = event.target;
553      const link = target instanceof Element ? target.closest('a[data-analytics-cta]') : null;
554      if (!link) return;
555      window.dataLayer = window.dataLayer || [];
556      window.dataLayer.push({
557        event: 'ph_cta_click',
558        cta_name: link.dataset.ctaName || '',
559        cta_location: link.dataset.ctaLocation || 'unknown',
560        business_area: link.dataset.businessArea || 'general',
561        destination_url: link.getAttribute('href') || '',
562      });
563    });
564  })();
565</script>
565 
565<script>
566  (function () {
567    function attachEmailToQuizLinks() {
568      const params = new URLSearchParams(window.location.search);
569      const email = params.get('email') || params.get('Email');
570      if (!email) return;
571
572      const links = document.querySelectorAll('a[href*="quiz.propertyhub.net"]:not([data-email-relayed])');
573      links.forEach(function (link) {
574        try {
575          const url = new URL(link.href);
576          url.searchParams.set('email', email);
577          link.href = url.toString();
578          link.dataset.emailRelayed = '1';
579        } catch (e) {
580          /* malformed href — skip */
581        }
582      });
583    }
584
585    if (document.readyState === 'loading') {
586      document.addEventListener('DOMContentLoaded', attachEmailToQuizLinks);
587    } else {
588      attachEmailToQuizLinks();
589    }
590
591    const observer = new MutationObserver(attachEmailToQuizLinks);
592    if (document.body) {
593      observer.observe(document.body, { childList: true, subtree: true });
594    } else {
595      document.addEventListener('DOMContentLoaded', function () {
596        observer.observe(document.body, { childList: true, subtree: true });
597      });
598    }
599  })();
600</script>
600  <!-- n8n chat widget — themed to match Property Hub.
601         Maps the widget's CSS variables to the site's brand tokens. Because
602         these reference var(--color-coral) etc. and the widget mounts inside
603         <body>, invest pages (.theme-invest) automatically render it in purple. -->  
603<script type="module">
604      // Kill switch for the n8n chat widget. Set to false to disable loading
605      // the bot site-wide (e.g. while its n8n workflow is being fixed before
606      // go-live); flip back to true to restore it — no other change needed.
607      // The whole loader below is left intact so re-enabling is a one-line flip.
608      const CHAT_ENABLED = false;
609
610      // The chat bundle is large and secondary to the page's main task. Load it
611      // only after the visitor interacts, rather than competing with LCP and
612      // parsing 1.9MB of JavaScript during an untouched initial page load.
613      let chatLoadPromise;
614
615      async function loadPropertyHubChat() {
616        if (chatLoadPromise) return chatLoadPromise;
617
618        chatLoadPromise = (async () => {
619          const [{ createChat }] = await Promise.all([
620            import(/* @vite-ignore */ "https://cdn.jsdelivr.net/npm/@n8n/chat/dist/chat.bundle.es.js"),
621            loadChatStyles(),
622          ]);
623
624          createChat({
625            webhookUrl:
626              "https://primary-production-86ea3.up.railway.app/webhook/d718b778-6558-4da9-96dc-cfcf5086e5a5/chat",
627            initialMessages: [
628              "Hello! I'm here to answer your questions about PropertyHub. How can I help?",
629            ],
630          });
631
632          const chatRoot = await waitForChatRoot();
633          patchChatA11y(chatRoot);
634          observeChatA11y(chatRoot);
635          document.dispatchEvent(new CustomEvent("propertyhub:chat-ready"));
636        })().catch((error) => {
637          chatLoadPromise = undefined;
638          throw error;
639        });
640
641        return chatLoadPromise;
642      }
643
644      function loadChatStyles() {
645        if (document.getElementById("propertyhub-chat-styles")) {
646          return Promise.resolve();
647        }
648
649        return new Promise((resolve, reject) => {
650          const link = document.createElement("link");
651          link.id = "propertyhub-chat-styles";
652          link.rel = "stylesheet";
653          link.href = "https://cdn.jsdelivr.net/npm/@n8n/chat/dist/style.css";
654          link.onload = () => resolve();
655          link.onerror = () => reject(new Error("Chat styles failed to load"));
656          document.head.appendChild(link);
657        });
658      }
659
660      function waitForChatRoot() {
661        const existing = document.querySelector("#n8n-chat, .n8n-chat");
662        if (existing) return Promise.resolve(existing);
663
664        return new Promise((resolve, reject) => {
665          const timeout = window.setTimeout(() => {
666            observer.disconnect();
667            reject(new Error("Chat widget did not mount"));
668          }, 5000);
669          const observer = new MutationObserver(() => {
670            const root = document.querySelector("#n8n-chat, .n8n-chat");
671            if (!root) return;
672            window.clearTimeout(timeout);
673            observer.disconnect();
674            resolve(root);
675          });
676          observer.observe(document.body, { childList: true, subtree: true });
677        });
678      }
679
680      // Accessibility patches for the n8n widget's own markup, which we can't
681      // configure. Two problems it ships with:
682      //   1. Its "Hi there! 👋" greeting is an <h1>, so every page ends up with a
683      //      second page-level heading (present even while the chat is closed).
684      //      We can't swap the tag in place, so we demote it with ARIA: role
685      //      "heading" at level 2 wins over the implicit <h1> level for
686      //      assistive tech, leaving the page's real <h1> unique.
687      //   2. Its launcher is a bare <div>/SVG with no role or label, so keyboard
688      //      users can't reach or activate it. We give it a button role, a name,
689      //      and Enter/Space handling.
690      // The widget mounts asynchronously and re-renders on open/close, so we
691      // re-apply on every mutation rather than once.
692      function patchChatA11y(root) {
693        root.querySelectorAll("h1").forEach(function (h) {
694          h.setAttribute("role", "heading");
695          h.setAttribute("aria-level", "2");
696        });
697
698        var toggle = root.querySelector(".chat-window-toggle, [class*='toggle']");
699        if (toggle && toggle.tagName !== "BUTTON" && !toggle.dataset.a11yPatched) {
700          toggle.dataset.a11yPatched = "true";
701          toggle.setAttribute("role", "button");
702          toggle.setAttribute("tabindex", "0");
703          toggle.setAttribute("aria-label", "Open chat");
704          toggle.addEventListener("keydown", function (e) {
705            if (e.key === "Enter" || e.key === " ") {
706              e.preventDefault();
707              toggle.click();
708            }
709          });
710        }
711      }
712
713      function observeChatA11y(root) {
714        new MutationObserver(() => patchChatA11y(root)).observe(root, {
715          childList: true,
716          subtree: true,
717        });
718      }
719
720      window.loadPropertyHubChat = loadPropertyHubChat;
721
722      // Only wire up the interaction listeners that trigger chat loading when
723      // the kill switch is on. With CHAT_ENABLED = false the widget never loads
724      // and no bubble appears; everything above stays ready to run again.
725      if (CHAT_ENABLED) {
726        const intentEvents = ["pointerdown", "keydown", "scroll"];
727        const handleFirstIntent = () => {
728          intentEvents.forEach((eventName) =>
729            window.removeEventListener(eventName, handleFirstIntent),
730          );
731          loadPropertyHubChat().catch(() => {
732            // Chat is progressive enhancement; navigation and forms must keep
733            // working if the third-party CDN or webhook is unavailable.
734          });
735        };
736        intentEvents.forEach((eventName) =>
737          window.addEventListener(eventName, handleFirstIntent, {
738            once: true,
739            passive: true,
740          }),
741        );
742      }
743    </script>
743 <!-- Chat-widget suppression (issue #36).
744         The n8n chat bubble is secondary to conversion, so we hide it in two
745         situations:
746           1. While any element flagged `[data-suppress-chat]` is present AND
747              actually shown — e.g. the mobile sticky CTA bar, which slides in
748              from the bottom corner and would otherwise overlap the bubble.
749           2. While any lead form is (even partially) in the viewport — the
750              bubble is a distraction at the moment we want a form fill.
751         Suppression toggles the `chat-suppressed` class on <html>; the CSS in
752         the style block above does the actual hiding.
753
754         This script is deliberately self-contained and does NOT depend on the
755         chat widget having loaded: if the CDN module is blocked the class
756         still toggles harmlessly, and nothing throws. It also works no matter
757         the merge order of the sticky-CTA-bar PR — it discovers suppressors
758         from the DOM rather than referencing the bar directly. --> 
758<script>
759      (function () {
760        var html = document.documentElement;
761
762        // --- form suppressors ---------------------------------------------
763        // Every real lead form on the site renders a <form> (LeadForm.astro
764        // and a handful of inline page forms). We observe all of them except
765        // ones that are explicitly opted out or never rendered.
766        var formsIntersecting = new Set();
767
768        // --- generic [data-suppress-chat] suppressors ---------------------
769        // The mobile CTA bar (and any future element) flags itself with
770        // `data-suppress-chat`. The bar slides in/out, so presence alone isn't
771        // enough: we only suppress while such an element is actually shown.
772        // "Shown" = on screen (IntersectionObserver) AND not visually hidden
773        // (display/visibility/opacity 0, or data-suppress-chat="false"/"off").
774        // We watch both intersection and attribute/class changes so the bar
775        // toggling its visible state re-evaluates suppression.
776        var flagIntersecting = new Set();
777
778        function flagIsActive(el) {
779          if (!flagIntersecting.has(el)) return false;
780          var val = (el.getAttribute("data-suppress-chat") || "").toLowerCase();
781          if (val === "false" || val === "off" || val === "0") return false;
782          var cs = window.getComputedStyle(el);
783          if (
784            cs.display === "none" ||
785            cs.visibility === "hidden" ||
786            parseFloat(cs.opacity) === 0
787          ) {
788            return false;
789          }
790          return true;
791        }
792
793        function update() {
794          var suppress = formsIntersecting.size > 0;
795          if (!suppress) {
796            // Only walk the flag set if no form already triggered suppression.
797            var flags = document.querySelectorAll("[data-suppress-chat]");
798            for (var i = 0; i < flags.length; i++) {
799              if (flagIsActive(flags[i])) {
800                suppress = true;
801                break;
802              }
803            }
804          }
805          html.classList.toggle("chat-suppressed", suppress);
806        }
807
808        // IntersectionObserver shared by forms; threshold 0 = "even partially".
809        var io;
810        try {
811          io = new IntersectionObserver(
812            function (entries) {
813              entries.forEach(function (entry) {
814                var set =
815                  entry.target.tagName === "FORM"
816                    ? formsIntersecting
817                    : flagIntersecting;
818                if (entry.isIntersecting) set.add(entry.target);
819                else set.delete(entry.target);
820              });
821              update();
822            },
823            { threshold: 0 }
824          );
825        } catch (e) {
826          // No IntersectionObserver (very old browser) — bail out quietly;
827          // the chat simply never gets suppressed, which is a safe default.
828          return;
829        }
830
831        function isHidden(el) {
832          // Skip forms that aren't actually visible (e.g. the honeypot row or
833          // a form inside a closed accordion) — they shouldn't suppress chat.
834          return !el.offsetParent && el.offsetWidth === 0 && el.offsetHeight === 0;
835        }
836
837        var watchedForms = new WeakSet();
838        var watchedFlags = new WeakSet();
839
840        function observeForms() {
841          document.querySelectorAll("form").forEach(function (form) {
842            if (watchedForms.has(form)) return;
843            if (form.hasAttribute("data-no-suppress-chat")) return;
844            if (isHidden(form)) return;
845            watchedForms.add(form);
846            io.observe(form);
847          });
848        }
849
850        function observeFlags() {
851          document
852            .querySelectorAll("[data-suppress-chat]")
853            .forEach(function (el) {
854              if (watchedFlags.has(el)) return;
855              watchedFlags.add(el);
856              io.observe(el);
857            });
858        }
859
860        function scan() {
861          observeForms();
862          observeFlags();
863          update();
864        }
865
866        scan();
867
868        // The CTA bar mounts/toggles after load and Astro view transitions can
869        // swap page content; a MutationObserver keeps our observers current and
870        // re-evaluates when a flagged element changes its class/attributes
871        // (e.g. the bar adding/removing `is-visible`).
872        try {
873          var mo = new MutationObserver(function (mutations) {
874            var needsRescan = false;
875            var needsUpdate = false;
876            mutations.forEach(function (m) {
877              if (m.type === "childList") needsRescan = true;
878              else if (m.type === "attributes") {
879                // A flagged element changed visibility-affecting attributes.
880                needsUpdate = true;
881              }
882            });
883            if (needsRescan) scan();
884            else if (needsUpdate) update();
885          });
886          mo.observe(document.body, {
887            childList: true,
888            subtree: true,
889            attributes: true,
890            attributeFilter: ["class", "style", "data-suppress-chat", "hidden"],
891          });
892        } catch (e) {
893          /* MutationObserver unavailable — initial scan still applies. */
894        }
895
896        document.addEventListener("astro:page-load", scan);
897      })();
898    </script>
898 </body> </html> 
898<script type="module">const s=document.getElementById("team-track"),r=document.getElementById("team-prev"),c=document.getElementById("team-next");if(s&&r&&c){let a=function(){const e=s.parentElement;if(!e)return 0;const i=Math.floor((e.offsetWidth+l)/d());return Math.max(0,f-i)},n=function(){const e=d();s.style.transform=`translateX(-${t*e}px)`;const i=a();r.style.opacity=t===0?"0.4":"1",c.style.opacity=t>=i?"0.4":"1"},t=0;const p=()=>window.innerWidth<640?300:380,l=32,d=()=>p()+l,f=s.children.length;r.addEventListener("click",()=>{t>0&&(t--,n())}),c.addEventListener("click",()=>{const e=a();t<e&&(t++,n())});let m=setInterval(()=>{const e=a();t<e?t++:t=0,n()},4e3);const o=s.parentElement;o&&(o.addEventListener("mouseenter",()=>clearInterval(m)),o.addEventListener("mouseleave",()=>{m=setInterval(()=>{const e=a();t<e?t++:t=0,n()},4e3)})),n(),window.addEventListener("resize",()=>{const e=a();t>e&&(t=e),n()})}</script>
898 <!-- Scroll reveal animations --> 
898<script type="module">const s={threshold:.1,rootMargin:"0px 0px -40px 0px"},t=new IntersectionObserver(e=>{e.forEach(r=>{r.isIntersecting&&(r.target.classList.add("visible"),t.unobserve(r.target))})},s);document.querySelectorAll("[data-reveal]").forEach(e=>{t.observe(e)});const a=new IntersectionObserver(e=>{e.forEach(r=>{r.isIntersecting&&(r.target.classList.add("visible"),a.unobserve(r.target))})},s);document.querySelectorAll("[data-reveal-stagger]").forEach(e=>{a.observe(e)});</script>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.