lmao
This commit is contained in:
parent
03445188ea
commit
524580eb25
102 changed files with 36625 additions and 1295 deletions
|
|
@ -1,9 +1,8 @@
|
|||
import { useRef, useState, useEffect } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useFadeInRef } from '../../hooks/useFadeIn';
|
||||
import HexCanvas from './HexCanvas';
|
||||
import HomeDemo from './HomeDemo';
|
||||
import ScrollStory from './ScrollStory';
|
||||
import BottomIllustration from './BottomIllustration';
|
||||
import CategoryArt from './CategoryArt';
|
||||
import { TickerValue } from '../ui/TickerValue';
|
||||
import type { FeatureMeta } from '../../types';
|
||||
|
||||
|
|
@ -18,46 +17,36 @@ export default function HomePage({
|
|||
theme?: 'light' | 'dark';
|
||||
features?: FeatureMeta[];
|
||||
}) {
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const [statsActive, setStatsActive] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setStatsActive(true), 300);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const heroRef = useFadeInRef();
|
||||
const demoRef = useFadeInRef();
|
||||
const scaleRef = useFadeInRef();
|
||||
const problemRef = useFadeInRef();
|
||||
const whyRef = useFadeInRef();
|
||||
const howRef = useFadeInRef();
|
||||
const ctaRef = useFadeInRef();
|
||||
|
||||
return (
|
||||
<div ref={scrollRef} className="flex-1 overflow-y-auto bg-warm-50 dark:bg-navy-950 relative">
|
||||
<div className="flex-1 overflow-y-auto bg-warm-50 dark:bg-navy-950 relative">
|
||||
<div className="relative" style={{ zIndex: 1 }}>
|
||||
{/* Hero — full-bleed */}
|
||||
<div
|
||||
ref={heroRef}
|
||||
className="fade-in-section relative overflow-hidden bg-gradient-to-br from-navy-950 via-navy-900 to-teal-900 dark:from-navy-950 dark:via-navy-900 dark:to-teal-900/60 pt-16 pb-20 md:pt-24 md:pb-28 shadow-[0_12px_50px_0px_rgba(13,148,136,0.5)] dark:shadow-[0_12px_50px_0px_rgba(13,148,136,0.4)]"
|
||||
>
|
||||
{/* Hero */}
|
||||
<div className="relative overflow-hidden bg-gradient-to-br from-navy-950 via-navy-900 to-teal-900 dark:from-navy-950 dark:via-navy-900 dark:to-teal-900/60 pt-16 pb-20 md:pt-24 md:pb-28 shadow-[0_12px_50px_0px_rgba(13,148,136,0.5)] dark:shadow-[0_12px_50px_0px_rgba(13,148,136,0.4)]">
|
||||
<HexCanvas isDark={theme === 'dark'} />
|
||||
{/* Radial teal glow */}
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[400px] bg-teal-500/[0.07] rounded-full blur-3xl pointer-events-none" />
|
||||
<div className="relative z-10 max-w-4xl mx-auto px-6 md:px-10 py-6 backdrop-blur-sm bg-navy-950/30 rounded-2xl">
|
||||
<p className="text-teal-400 font-semibold tracking-wide uppercase text-sm mb-4">
|
||||
Browsing listings is not a strategy. Knowing what you want is.
|
||||
</p>
|
||||
<h1 className="text-3xl md:text-5xl font-extrabold text-white mb-6 leading-[1.1] tracking-tight">
|
||||
Find your{' '}
|
||||
<span className="text-teal-400">perfect postcode</span>
|
||||
<br />
|
||||
<span className="text-warm-300">before you find your property.</span>
|
||||
<h1 className="text-3xl md:text-5xl font-extrabold text-white mb-4 leading-[1.1] tracking-tight">
|
||||
Get more <span className="text-teal-400">home</span> for your money.
|
||||
</h1>
|
||||
<p className="text-xl text-warm-300 mb-8 leading-relaxed max-w-xl">
|
||||
Set the sliders to your expectations and the map highlights the areas that actually
|
||||
match. Instantly.
|
||||
<p className="text-xl text-warm-300 mb-6 leading-relaxed max-w-xl">
|
||||
Buying a home may be your most important decision. Why not ensure you make your
|
||||
best-ever decision?
|
||||
</p>
|
||||
<div className="flex items-center gap-4 mb-12">
|
||||
<p className="text-lg text-warm-400 mb-8 max-w-lg">
|
||||
You have so many options. Picking the best one is daunting and stressful. It
|
||||
won't be anymore when looking at the property landscape through our dashboard.
|
||||
</p>
|
||||
<div className="flex items-center gap-4 mb-10">
|
||||
<button
|
||||
onClick={onOpenDashboard}
|
||||
className="px-7 py-3.5 bg-coral-500 text-white rounded-lg font-semibold hover:bg-coral-600 transition-colors text-base shadow-lg shadow-coral-500/25"
|
||||
|
|
@ -92,124 +81,85 @@ export default function HomePage({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Map + Slider demo */}
|
||||
<div className="max-w-4xl mx-auto px-6 pt-16 pb-20">
|
||||
<div ref={demoRef} className="fade-in-section">
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-navy-950 dark:text-warm-100 mb-2">
|
||||
See it in action
|
||||
{/* Scrollytelling: Problem + Solution + Demo map */}
|
||||
<ScrollStory features={features} theme={theme} />
|
||||
|
||||
{/* Why existing tools don't cut it */}
|
||||
<div className="max-w-4xl mx-auto px-6 py-20">
|
||||
<div ref={whyRef} className="fade-in-section">
|
||||
<h2 className="text-3xl font-bold text-navy-950 dark:text-warm-100 mb-10 text-center">
|
||||
Why existing tools don't cut it
|
||||
</h2>
|
||||
<p className="text-warm-500 dark:text-warm-400 mb-5 max-w-lg">
|
||||
Drag the sliders and watch the map respond. Every postcode scored, every filter instant.
|
||||
</p>
|
||||
<div className="rounded-2xl backdrop-blur-sm bg-warm-50/40 dark:bg-navy-800/40 border border-warm-200/50 dark:border-navy-700/50 p-4 md:p-6">
|
||||
<HomeDemo features={features} theme={theme} />
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
{WHY_CARDS.map((card) => (
|
||||
<div
|
||||
key={card.title}
|
||||
className="rounded-xl border border-warm-200 dark:border-warm-700 bg-white dark:bg-warm-800 p-6 shadow-sm"
|
||||
>
|
||||
<div className="text-2xl mb-3">{card.icon}</div>
|
||||
<h3 className="font-bold text-navy-950 dark:text-warm-100 mb-2">{card.title}</h3>
|
||||
<p className="text-warm-600 dark:text-warm-400 text-sm leading-relaxed">
|
||||
{card.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-center mt-10 text-lg text-warm-700 dark:text-warm-300 max-w-2xl mx-auto leading-relaxed">
|
||||
We do. 13 million historical transactions. 56 data layers. Real travel-time routing to
|
||||
any destination. Every postcode in England, scored and filterable, on a single map.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scale — "That's just two" + category cards */}
|
||||
<div className="max-w-4xl mx-auto px-6 pb-20">
|
||||
<div ref={scaleRef} className="fade-in-section">
|
||||
<h2 className="text-3xl font-bold text-navy-950 dark:text-warm-100 mb-2 text-center">
|
||||
That's just three. We've built 43.
|
||||
{/* How to use it */}
|
||||
<div className="max-w-3xl mx-auto px-6 pb-20">
|
||||
<div ref={howRef} className="fade-in-section">
|
||||
<h2 className="text-3xl font-bold text-navy-950 dark:text-warm-100 mb-10 text-center">
|
||||
How to use it
|
||||
</h2>
|
||||
<p className="text-warm-500 dark:text-warm-400 text-center mb-10 max-w-lg mx-auto">
|
||||
Spanning transport links, amenities, demographics, environment risk, broadband speeds,
|
||||
crime, and more.
|
||||
</p>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
{CATEGORIES.map((c) => (
|
||||
<div
|
||||
key={c.label}
|
||||
className={`rounded-xl border-l-4 border border-warm-200 dark:border-warm-700 bg-white dark:bg-warm-800 p-4 shadow-sm hover:shadow-md transition-shadow ${c.borderClass} ${c.hoverBgClass}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2.5 min-w-0">
|
||||
<div
|
||||
className={`shrink-0 flex items-center justify-center rounded-lg w-8 h-8 text-base ${c.iconBgClass}`}
|
||||
>
|
||||
{c.icon}
|
||||
</div>
|
||||
<span className="font-semibold text-navy-950 dark:text-warm-100 text-sm">
|
||||
{c.label}
|
||||
</span>
|
||||
</div>
|
||||
<CategoryArt
|
||||
category={c.group === 'Environment' && c.label === 'Broadband' ? 'Broadband' : c.group}
|
||||
className={`shrink-0 ${c.artColorClass} opacity-40`}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-8">
|
||||
{HOW_STEPS.map((step, i) => (
|
||||
<div key={i} className="flex gap-5">
|
||||
<div className="shrink-0 w-10 h-10 rounded-full bg-teal-600 text-white flex items-center justify-center font-bold text-lg">
|
||||
{i + 1}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-navy-950 dark:text-warm-100 mb-1">
|
||||
{step.title}
|
||||
</h3>
|
||||
<p className="text-warm-600 dark:text-warm-400 leading-relaxed">
|
||||
{step.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Problem / solution / philosophy */}
|
||||
<div className="max-w-4xl mx-auto px-6 pb-20 relative">
|
||||
{/* Cereal box — quirky margin note, hidden on narrow screens */}
|
||||
<div className="hidden lg:block group absolute -right-44 top-8 cursor-pointer">
|
||||
<div className="cereal-wobble">
|
||||
<img src="/cereal.png" alt="Discounted cereal box" className="w-36 h-auto" />
|
||||
</div>
|
||||
<p className="cereal-text text-sm italic text-warm-500 dark:text-warm-400 mt-2 w-[9rem] leading-snug">
|
||||
Your home is not a box of cereal. Don't let a discount on the wrong
|
||||
property distract you from finding the right one.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div ref={problemRef} className="fade-in-section">
|
||||
<p className="text-lg text-warm-700 dark:text-warm-300 leading-relaxed mb-6">
|
||||
Here's the problem with property search: listings only show you what's on
|
||||
the market{' '}
|
||||
<strong className="font-semibold text-navy-950 dark:text-warm-100">right now</strong>{' '}
|
||||
— a thin slice of what an area is actually like. And even if you could look
|
||||
beyond them, there are{' '}
|
||||
<strong className="font-semibold text-navy-950 dark:text-warm-100">
|
||||
millions of postcodes
|
||||
</strong>{' '}
|
||||
across England. You can't research them all yourself.
|
||||
</p>
|
||||
<p className="text-lg text-warm-700 dark:text-warm-300 leading-relaxed mb-6">
|
||||
We built this for you — years of historical transactions and public records,
|
||||
extended with proprietary algorithms so the map doesn't just show raw data, it{' '}
|
||||
<strong className="font-semibold text-navy-950 dark:text-warm-100">
|
||||
surfaces the patterns that matter
|
||||
</strong>
|
||||
.
|
||||
</p>
|
||||
<p className="text-xl font-bold text-navy-950 dark:text-warm-100 leading-relaxed">
|
||||
Understand areas first. Then find the right property within them, with expectations
|
||||
you've set — not ones the market set for you.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Final CTA */}
|
||||
{/* The real cost CTA */}
|
||||
<div className="max-w-3xl mx-auto px-6 pb-12">
|
||||
<div ref={ctaRef} className="fade-in-section text-center">
|
||||
<h2 className="text-3xl font-bold text-navy-950 dark:text-warm-100 mb-3">
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-navy-950 dark:text-warm-100 mb-4 leading-snug">
|
||||
The biggest financial decision of your life
|
||||
<br />
|
||||
deserves proper tools behind it.
|
||||
</h2>
|
||||
<p className="text-warm-500 dark:text-warm-400 mb-8 max-w-md mx-auto">
|
||||
One payment, lifetime access. Set your filters and go.
|
||||
<p className="text-warm-600 dark:text-warm-400 mb-3 max-w-xl mx-auto leading-relaxed">
|
||||
Stamp duty on a £400k house: £10,000. Solicitor fees: £1,500.
|
||||
Survey: £500. Moving costs: £1,000. And that's just the money. Get the
|
||||
wrong area and you're stuck — with a long commute, bad schools, or a street
|
||||
that looked fine on the listing photos but turns out to be on a motorway.
|
||||
</p>
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<button
|
||||
onClick={onOpenDashboard}
|
||||
className="px-8 py-4 bg-coral-500 text-white rounded-lg font-semibold hover:bg-coral-600 transition-colors text-lg shadow-lg shadow-coral-500/25"
|
||||
>
|
||||
Give your journey a headstart
|
||||
</button>
|
||||
<button
|
||||
onClick={onOpenPricing}
|
||||
className="px-[30px] py-[14px] border-2 border-navy-950 dark:border-warm-300 text-navy-950 dark:text-warm-300 rounded-lg font-semibold hover:bg-navy-950/5 dark:hover:bg-warm-300/5 transition-colors text-lg"
|
||||
>
|
||||
See pricing
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-warm-700 dark:text-warm-300 mb-8 font-semibold">
|
||||
One payment. Lifetime access. Less than your survey costs and vastly more useful.
|
||||
</p>
|
||||
<button
|
||||
onClick={onOpenDashboard}
|
||||
className="px-8 py-4 bg-coral-500 text-white rounded-lg font-semibold hover:bg-coral-600 transition-colors text-lg shadow-lg shadow-coral-500/25"
|
||||
>
|
||||
Explore the map
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -220,107 +170,46 @@ export default function HomePage({
|
|||
);
|
||||
}
|
||||
|
||||
interface Category {
|
||||
icon: string;
|
||||
label: string;
|
||||
group: string;
|
||||
borderClass: string;
|
||||
hoverBgClass: string;
|
||||
iconBgClass: string;
|
||||
artColorClass: string;
|
||||
}
|
||||
|
||||
const CATEGORIES: Category[] = [
|
||||
const WHY_CARDS = [
|
||||
{
|
||||
icon: '\u{1F3E0}',
|
||||
label: 'Property',
|
||||
group: 'Property',
|
||||
borderClass: 'border-l-teal-400 dark:border-l-teal-500',
|
||||
hoverBgClass: 'hover:bg-teal-50/50 dark:hover:bg-teal-900/20',
|
||||
iconBgClass: 'bg-teal-100 dark:bg-teal-900/40',
|
||||
artColorClass: 'text-teal-400 dark:text-teal-600',
|
||||
|
||||
icon: '\u{1F3D8}\uFE0F',
|
||||
title: 'Listing portals',
|
||||
description:
|
||||
"Show you what's for sale today. That's a snapshot, not a strategy. You can filter by price and bedrooms \u2014 that's about it. They tell you nothing about the area.",
|
||||
},
|
||||
{
|
||||
icon: '\u{1F686}',
|
||||
label: 'Transport',
|
||||
group: 'Transport',
|
||||
borderClass: 'border-l-blue-400 dark:border-l-blue-500',
|
||||
hoverBgClass: 'hover:bg-blue-50/50 dark:hover:bg-blue-900/20',
|
||||
iconBgClass: 'bg-blue-100 dark:bg-blue-900/40',
|
||||
artColorClass: 'text-blue-400 dark:text-blue-600',
|
||||
|
||||
icon: '\u{1F4CD}',
|
||||
title: '\u201CCheck my postcode\u201D sites',
|
||||
description:
|
||||
"Give you stats for one postcode at a time. Useful if you already know where to look. Useless if you don't \u2014 and there are 1.5 million postcodes in England.",
|
||||
},
|
||||
{
|
||||
icon: '\u{1F3EB}',
|
||||
label: 'Schools',
|
||||
group: 'Education',
|
||||
borderClass: 'border-l-amber-400 dark:border-l-amber-500',
|
||||
hoverBgClass: 'hover:bg-amber-50/50 dark:hover:bg-amber-900/20',
|
||||
iconBgClass: 'bg-amber-100 dark:bg-amber-900/40',
|
||||
artColorClass: 'text-amber-400 dark:text-amber-600',
|
||||
|
||||
},
|
||||
{
|
||||
icon: '\u{1F6A8}',
|
||||
label: 'Crime',
|
||||
group: 'Crime',
|
||||
borderClass: 'border-l-rose-400 dark:border-l-rose-500',
|
||||
hoverBgClass: 'hover:bg-rose-50/50 dark:hover:bg-rose-900/20',
|
||||
iconBgClass: 'bg-rose-100 dark:bg-rose-900/40',
|
||||
artColorClass: 'text-rose-400 dark:text-rose-600',
|
||||
|
||||
},
|
||||
{
|
||||
icon: '\u{1F465}',
|
||||
label: 'Demographics',
|
||||
group: 'Demographics',
|
||||
borderClass: 'border-l-violet-400 dark:border-l-violet-500',
|
||||
hoverBgClass: 'hover:bg-violet-50/50 dark:hover:bg-violet-900/20',
|
||||
iconBgClass: 'bg-violet-100 dark:bg-violet-900/40',
|
||||
artColorClass: 'text-violet-400 dark:text-violet-600',
|
||||
|
||||
},
|
||||
{
|
||||
icon: '\u{1F3EA}',
|
||||
label: 'Amenities',
|
||||
group: 'Amenities',
|
||||
borderClass: 'border-l-emerald-400 dark:border-l-emerald-500',
|
||||
hoverBgClass: 'hover:bg-emerald-50/50 dark:hover:bg-emerald-900/20',
|
||||
iconBgClass: 'bg-emerald-100 dark:bg-emerald-900/40',
|
||||
artColorClass: 'text-emerald-400 dark:text-emerald-600',
|
||||
|
||||
},
|
||||
{
|
||||
icon: '\u{1F30D}',
|
||||
label: 'Environment',
|
||||
group: 'Environment',
|
||||
|
||||
borderClass: 'border-l-orange-400 dark:border-l-orange-500',
|
||||
hoverBgClass: 'hover:bg-orange-50/50 dark:hover:bg-orange-900/20',
|
||||
iconBgClass: 'bg-orange-100 dark:bg-orange-900/40',
|
||||
artColorClass: 'text-orange-400 dark:text-orange-600',
|
||||
|
||||
},
|
||||
{
|
||||
icon: '\u{1F4E1}',
|
||||
label: 'Broadband',
|
||||
group: 'Environment',
|
||||
|
||||
borderClass: 'border-l-sky-400 dark:border-l-sky-500',
|
||||
hoverBgClass: 'hover:bg-sky-50/50 dark:hover:bg-sky-900/20',
|
||||
iconBgClass: 'bg-sky-100 dark:bg-sky-900/40',
|
||||
artColorClass: 'text-sky-400 dark:text-sky-600',
|
||||
|
||||
},
|
||||
{
|
||||
icon: '\u{1F4CA}',
|
||||
label: 'Deprivation',
|
||||
group: 'Deprivation',
|
||||
borderClass: 'border-l-fuchsia-400 dark:border-l-fuchsia-500',
|
||||
hoverBgClass: 'hover:bg-fuchsia-50/50 dark:hover:bg-fuchsia-900/20',
|
||||
iconBgClass: 'bg-fuchsia-100 dark:bg-fuchsia-900/40',
|
||||
artColorClass: 'text-fuchsia-400 dark:text-fuchsia-600',
|
||||
|
||||
icon: '\u{1F5FA}\uFE0F',
|
||||
title: 'Area guides',
|
||||
description:
|
||||
"Show one statistic on a map \u2014 crime, or school ratings, or prices. But you care about the intersection: affordable AND safe AND good schools AND short commute. Nobody else shows you that.",
|
||||
},
|
||||
];
|
||||
|
||||
const HOW_STEPS = [
|
||||
{
|
||||
title: 'Set your non-negotiables',
|
||||
description:
|
||||
'Budget, commute, bedrooms, whatever matters most. The map narrows to only the areas that qualify.',
|
||||
},
|
||||
{
|
||||
title: "Explore what\u2019s left",
|
||||
description:
|
||||
"Zoom in. Toggle layers. See crime, schools, noise, amenities. Discover areas you didn\u2019t know existed.",
|
||||
},
|
||||
{
|
||||
title: 'Drill into postcodes',
|
||||
description:
|
||||
'At street level, see individual properties, what they sold for, floor area, energy rating, estimated current value.',
|
||||
},
|
||||
{
|
||||
title: 'Go to viewings with a shortlist, not a prayer',
|
||||
description:
|
||||
"You\u2019ve already done the hard part. Every area on your list meets your actual criteria, not just what happened to be listed that week.",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue