diff --git a/src/components/IntelligenceMap.jsx b/src/components/IntelligenceMap.jsx
index 5d28440..30ff086 100644
--- a/src/components/IntelligenceMap.jsx
+++ b/src/components/IntelligenceMap.jsx
@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import { useNavigate } from 'react-router-dom';
-import MapBg from '../assets/images/intel_map_dfw_twilight.png';
+import MapBg from '../assets/images/intel_map_dfw_twilight.webp';
import IntelligenceSidePanel from './dashboard/IntelligenceSidePanel';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
diff --git a/src/components/Services.jsx b/src/components/Services.jsx
index 375b784..1404831 100644
--- a/src/components/Services.jsx
+++ b/src/components/Services.jsx
@@ -1,6 +1,10 @@
-import React from 'react';
+import React, { useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Icons } from './landing/constants';
+import gsap from 'gsap';
+import { ScrollTrigger } from 'gsap/ScrollTrigger';
+
+gsap.registerPlugin(ScrollTrigger);
const services = [
{
@@ -12,7 +16,7 @@ const services = [
badge: "Licensed",
badgeClass: "permit-stamp--verified",
badgeIcon: "\u2713",
- stat: "2,847",
+ stat: 5847,
statLabel: "Properties Inspected",
},
{
@@ -24,13 +28,47 @@ const services = [
badge: "Certified",
badgeClass: "permit-stamp--accent",
badgeIcon: "\u25B9",
- stat: "96%",
+ stat: 96,
+ statSuffix: "%",
statLabel: "Lead Accuracy",
}
];
const Services = () => {
const navigate = useNavigate();
+ const statRefs = useRef([]);
+
+ useEffect(() => {
+ // Animated Stat Counters
+ statRefs.current.forEach((el) => {
+ if (!el) return;
+
+ const target = parseFloat(el.dataset.target);
+ const suffix = el.dataset.suffix || '';
+ const counter = { value: 0 };
+
+ el.innerText = '0' + suffix;
+
+ const formatNumber = (num) => {
+ return Math.round(num).toLocaleString('en-US');
+ };
+
+ gsap.to(counter, {
+ value: target,
+ duration: 2.5,
+ ease: "power2.out",
+ scrollTrigger: {
+ trigger: el,
+ start: "top 85%",
+ toggleActions: "play none none none",
+ },
+ onUpdate: () => {
+ el.innerText = formatNumber(counter.value) + suffix;
+ },
+ snap: { value: 1 }
+ });
+ });
+ }, []);
const handleMouseMove = (e) => {
const card = e.currentTarget;
@@ -65,7 +103,7 @@ const Services = () => {
key={idx}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
- className="group relative p-8 rounded-2xl border border-zinc-200 dark:border-white/10 bg-white dark:bg-white/[0.04] hover:bg-zinc-50 dark:hover:bg-white/[0.07] backdrop-blur-sm transition-all duration-200 cursor-pointer overflow-hidden shadow-xl dark:shadow-[0_20px_60px_rgba(0,0,0,0.4)]"
+ className="group relative p-6 md:p-8 rounded-2xl border border-zinc-200 dark:border-white/10 bg-white dark:bg-white/[0.04] hover:bg-zinc-50 dark:hover:bg-white/[0.07] backdrop-blur-sm transition-all duration-200 cursor-pointer overflow-hidden shadow-xl dark:shadow-[0_20px_60px_rgba(0,0,0,0.4)]"
style={{ transformStyle: 'preserve-3d', transition: 'transform 0.1s ease-out' }}
>
{/* Measurement ticks — top */}
@@ -93,9 +131,16 @@ const Services = () => {
{service.desc}
- {/* Inline stat */}
+ {/* Inline stat with animation */}
- {service.stat}
+ statRefs.current[idx] = el}
+ data-target={service.stat}
+ data-suffix={service.statSuffix || ''}
+ className="font-mono text-2xl font-bold text-zinc-900 dark:text-white tracking-tight"
+ >
+ 0{service.statSuffix || ''}
+
{service.statLabel}
diff --git a/src/index.css b/src/index.css
index de75e73..97339d7 100644
--- a/src/index.css
+++ b/src/index.css
@@ -28,7 +28,9 @@
--crane-fill-surface: rgba(0, 0, 0, 0.04);
}
- html, body, #root {
+ html,
+ body,
+ #root {
height: 100%;
margin: 0;
padding: 0;
@@ -69,8 +71,13 @@
}
@keyframes fadeIn {
- from { opacity: 0; }
- to { opacity: 1; }
+ from {
+ opacity: 0;
+ }
+
+ to {
+ opacity: 1;
+ }
}
@keyframes slideInBottom {
@@ -78,6 +85,7 @@
transform: translateY(1rem);
opacity: 0;
}
+
to {
transform: translateY(0);
opacity: 1;
@@ -89,12 +97,16 @@
-ms-overflow-style: none;
scrollbar-width: none;
}
+
*::-webkit-scrollbar {
display: none;
}
@layer utilities {
- .no-scrollbar::-webkit-scrollbar { display: none; }
+ .no-scrollbar::-webkit-scrollbar {
+ display: none;
+ }
+
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
@@ -121,14 +133,17 @@
border-radius: 3px;
opacity: 0.7;
}
+
.permit-stamp--accent {
color: #3b82f6;
border-color: rgba(59, 130, 246, 0.4);
}
+
.permit-stamp--verified {
color: #10b981;
border-color: rgba(16, 185, 129, 0.4);
}
+
.permit-stamp--danger {
color: #ef4444;
border-color: rgba(239, 68, 68, 0.4);
@@ -141,19 +156,20 @@
width: 100%;
height: 20px;
background:
- linear-gradient(135deg, rgba(0,0,0,0.06) 25%, transparent 25%) -24px 0,
- linear-gradient(225deg, rgba(0,0,0,0.06) 25%, transparent 25%) -24px 0,
- linear-gradient(315deg, rgba(0,0,0,0.06) 25%, transparent 25%),
- linear-gradient(45deg, rgba(0,0,0,0.06) 25%, transparent 25%);
+ linear-gradient(135deg, rgba(0, 0, 0, 0.06) 25%, transparent 25%) -24px 0,
+ linear-gradient(225deg, rgba(0, 0, 0, 0.06) 25%, transparent 25%) -24px 0,
+ linear-gradient(315deg, rgba(0, 0, 0, 0.06) 25%, transparent 25%),
+ linear-gradient(45deg, rgba(0, 0, 0, 0.06) 25%, transparent 25%);
background-size: 48px 20px;
background-color: transparent;
}
+
.dark .shingle-divider {
background:
- linear-gradient(135deg, rgba(255,255,255,0.04) 25%, transparent 25%) -24px 0,
- linear-gradient(225deg, rgba(255,255,255,0.04) 25%, transparent 25%) -24px 0,
- linear-gradient(315deg, rgba(255,255,255,0.04) 25%, transparent 25%),
- linear-gradient(45deg, rgba(255,255,255,0.04) 25%, transparent 25%);
+ linear-gradient(135deg, rgba(255, 255, 255, 0.04) 25%, transparent 25%) -24px 0,
+ linear-gradient(225deg, rgba(255, 255, 255, 0.04) 25%, transparent 25%) -24px 0,
+ linear-gradient(315deg, rgba(255, 255, 255, 0.04) 25%, transparent 25%),
+ linear-gradient(45deg, rgba(255, 255, 255, 0.04) 25%, transparent 25%);
background-size: 48px 20px;
}
}
@@ -161,13 +177,11 @@
/* Safety stripe — diagonal hazard pattern for urgent/warning elements */
@layer components {
.safety-stripe {
- background: repeating-linear-gradient(
- -45deg,
- transparent,
- transparent 4px,
- rgba(245, 158, 11, 0.12) 4px,
- rgba(245, 158, 11, 0.12) 8px
- );
+ background: repeating-linear-gradient(-45deg,
+ transparent,
+ transparent 4px,
+ rgba(245, 158, 11, 0.12) 4px,
+ rgba(245, 158, 11, 0.12) 8px);
}
}
@@ -175,17 +189,19 @@
@layer components {
.measure-ticks {
background-image:
- repeating-linear-gradient(90deg, rgba(0,0,0,0.15) 0px, rgba(0,0,0,0.15) 1.5px, transparent 1.5px, transparent 24px),
- repeating-linear-gradient(90deg, rgba(0,0,0,0.07) 0px, rgba(0,0,0,0.07) 1px, transparent 1px, transparent 6px);
+ repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.15) 0px, rgba(0, 0, 0, 0.15) 1.5px, transparent 1.5px, transparent 24px),
+ repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.07) 0px, rgba(0, 0, 0, 0.07) 1px, transparent 1px, transparent 6px);
background-size: 100% 100%, 100% 50%;
background-repeat: no-repeat;
background-position: bottom, bottom;
}
+
.dark .measure-ticks {
background-image:
- repeating-linear-gradient(90deg, rgba(255,255,255,0.22) 0px, rgba(255,255,255,0.22) 1.5px, transparent 1.5px, transparent 24px),
- repeating-linear-gradient(90deg, rgba(255,255,255,0.1) 0px, rgba(255,255,255,0.1) 1px, transparent 1px, transparent 6px);
+ repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.22) 0px, rgba(255, 255, 255, 0.22) 1.5px, transparent 1.5px, transparent 24px),
+ repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.1) 0px, rgba(255, 255, 255, 0.1) 1px, transparent 1px, transparent 6px);
}
+
.measure-ticks-top {
background-position: top, top;
}
@@ -195,49 +211,65 @@
@layer components {
.blueprint-grid {
background-image:
- linear-gradient(rgba(59,130,246,0.1) 1px, transparent 1px),
- linear-gradient(90deg, rgba(59,130,246,0.1) 1px, transparent 1px);
+ linear-gradient(rgba(59, 130, 246, 0.1) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(59, 130, 246, 0.1) 1px, transparent 1px);
background-size: 48px 48px;
}
+
.dark .blueprint-grid {
background-image:
- linear-gradient(rgba(59,130,246,0.05) 1px, transparent 1px),
- linear-gradient(90deg, rgba(59,130,246,0.05) 1px, transparent 1px);
+ linear-gradient(rgba(59, 130, 246, 0.05) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(59, 130, 246, 0.05) 1px, transparent 1px);
}
+
.blueprint-grid-fine {
background-image:
- linear-gradient(rgba(59,130,246,0.06) 1px, transparent 1px),
- linear-gradient(90deg, rgba(59,130,246,0.06) 1px, transparent 1px),
- linear-gradient(rgba(59,130,246,0.1) 1px, transparent 1px),
- linear-gradient(90deg, rgba(59,130,246,0.1) 1px, transparent 1px);
+ linear-gradient(rgba(59, 130, 246, 0.06) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(59, 130, 246, 0.06) 1px, transparent 1px),
+ linear-gradient(rgba(59, 130, 246, 0.1) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(59, 130, 246, 0.1) 1px, transparent 1px);
background-size: 12px 12px, 12px 12px, 48px 48px, 48px 48px;
}
+
.dark .blueprint-grid-fine {
background-image:
- linear-gradient(rgba(59,130,246,0.03) 1px, transparent 1px),
- linear-gradient(90deg, rgba(59,130,246,0.03) 1px, transparent 1px),
- linear-gradient(rgba(59,130,246,0.06) 1px, transparent 1px),
- linear-gradient(90deg, rgba(59,130,246,0.06) 1px, transparent 1px);
+ linear-gradient(rgba(59, 130, 246, 0.03) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(59, 130, 246, 0.03) 1px, transparent 1px),
+ linear-gradient(rgba(59, 130, 246, 0.06) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(59, 130, 246, 0.06) 1px, transparent 1px);
}
}
/* Mobile bottom sheet slide-up for Intelligence Map */
@keyframes slideUpSheet {
- from { transform: translateY(100%); }
- to { transform: translateY(0); }
+ from {
+ transform: translateY(100%);
+ }
+
+ to {
+ transform: translateY(0);
+ }
}
/* Crane swing — gentle pendulum sway for suspended CTA card */
@keyframes craneSwing {
- 0%, 100% { transform: rotate(0deg); }
- 25% { transform: rotate(1.5deg); }
- 75% { transform: rotate(-1.5deg); }
+
+ 0%,
+ 100% {
+ transform: rotate(3deg);
+ }
+
+ 50% {
+ transform: rotate(-3deg);
+ }
}
+
@layer components {
.crane-swing {
animation: craneSwing 4s ease-in-out infinite;
transform-origin: top center;
}
+
.crane-swing:hover {
animation-play-state: paused;
}
@@ -245,6 +277,7 @@
/* === Attribution (existing, preserved) === */
@layer utilities {
+
.attribution-ghost,
.content-ref-x7,
.sys-ref-x7,
@@ -270,8 +303,9 @@
background: transparent;
user-select: text;
}
+
.attribution-reveal::selection {
background: #3b82f6;
color: white;
}
-}
+}
\ No newline at end of file
diff --git a/src/pages/Landing.jsx b/src/pages/Landing.jsx
index 83acfd2..8422f06 100644
--- a/src/pages/Landing.jsx
+++ b/src/pages/Landing.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
import Chatbot from '../components/Chatbot';
import { useAuth } from '../context/AuthContext';
import { useTheme } from '../context/ThemeContext';
@@ -9,13 +9,14 @@ import { SpotlightCard } from '../components/SpotlightCard';
import { ComparisonSlider } from '../components/ComparisonSlider';
import Services from '../components/Services';
import IntelligenceMap from '../components/IntelligenceMap';
+import { motion, AnimatePresence } from 'framer-motion';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import Logo from '../assets/images/LynkedUp_Pro_F_logo_Y.png';
-import HeroImg from '../assets/images/hero_aerial_scan.png';
-import ProcessImg1 from '../assets/images/process.png';
-import ProcessImg2 from '../assets/images/process-2.png';
-import ProcessImg3 from '../assets/images/process-3.png';
+import HeroImg from '../assets/images/hero_aerial_scan.webp';
+import ProcessImg1 from '../assets/images/process.webp';
+import ProcessImg2 from '../assets/images/process-2.webp';
+import ProcessImg3 from '../assets/images/process-3.webp';
gsap.registerPlugin(ScrollTrigger);
@@ -26,9 +27,32 @@ const Landing = () => {
const textRef = useRef(null);
const sectionsRef = useRef([]);
const statRefs = useRef([]);
- const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
+ const [isScrolled, setIsScrolled] = useState(false);
+ const [hoveredSplit, setHoveredSplit] = useState(null);
const cursorGlowRef = useRef(null);
+ // Scroll-aware nav — transparent at top, glass effect on scroll
+ useEffect(() => {
+ const handleScroll = () => setIsScrolled(window.scrollY > 50);
+ window.addEventListener('scroll', handleScroll, { passive: true });
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, []);
+
+ // Body scroll lock when mobile menu is open
+ useEffect(() => {
+ document.body.style.overflow = isMobileMenuOpen ? 'hidden' : '';
+ return () => { document.body.style.overflow = ''; };
+ }, [isMobileMenuOpen]);
+
+ const scrollToSection = (id) => {
+ const el = document.getElementById(id);
+ if (el) {
+ el.scrollIntoView({ behavior: 'smooth' });
+ setIsMobileMenuOpen(false);
+ }
+ };
+
// Mouse-follow ambient light — tracks cursor across the page
useEffect(() => {
const handleMouseMove = (e) => {
@@ -79,18 +103,24 @@ const Landing = () => {
el.innerText = '0' + suffix;
+ // Helper function to format numbers with commas
+ const formatNumber = (num) => {
+ return Math.round(num).toLocaleString('en-US');
+ };
+
gsap.to(counter, {
value: target,
- duration: 2,
- ease: "power1.out",
+ duration: 2.5,
+ ease: "power2.out",
scrollTrigger: {
trigger: el,
start: "top 85%",
toggleActions: "play none none none",
},
onUpdate: () => {
- el.innerText = Math.ceil(counter.value) + suffix;
- }
+ el.innerText = formatNumber(counter.value) + suffix;
+ },
+ snap: { value: 1 }
});
});
@@ -115,96 +145,208 @@ const Landing = () => {
}}
/>
- {/* Nav */}
-
-
-
-
- {/*
LynkedUp Pro */}
+ {/* ── Navigation ── */}
+
+
+ {/* Logo */}
+
window.scrollTo({ top: 0, behavior: 'smooth' })} className="flex items-center space-x-2 shrink-0">
+
+
+
+ {/* Desktop Nav Links */}
+
+ {[
+ { label: 'How It Works', id: 'how-it-works' },
+ { label: 'Services', id: 'services' },
+ { label: 'Intelligence', id: 'intelligence' },
+ ].map((item) => (
+ scrollToSection(item.id)}
+ className="px-3 py-2 text-sm font-medium text-zinc-500 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white transition-colors rounded-lg hover:bg-zinc-100/50 dark:hover:bg-white/5"
+ >
+ {item.label}
+
+ ))}
+
+ Pricing
+
- {/* Desktop Nav */}
-
+ {/* Desktop Right Actions */}
+
- {theme === 'dark' ? : }
+ {theme === 'dark' ? : }
{!user ? (
-
- Sign In
-
+ <>
+
+ Sign In
+
+
+ Get Started
+
+ >
) : (
{user.role === 'CUSTOMER' ? 'My Profile' : 'Dashboard'}
-
+
)}
{/* Mobile Menu Toggle */}
-
setIsMobileMenuOpen(!isMobileMenuOpen)}
- >
- {isMobileMenuOpen ? : }
-
-
-
- {/* Mobile Nav Dropdown */}
- {isMobileMenuOpen && (
-
+
{ toggleTheme(); setIsMobileMenuOpen(false); }}
- className="flex items-center space-x-3 p-3 rounded-xl bg-zinc-100 dark:bg-zinc-900"
+ onClick={toggleTheme}
+ className="p-2 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors text-zinc-500 dark:text-zinc-400"
+ aria-label="Toggle Theme"
>
- {theme === 'dark' ? : }
- {theme === 'dark' ? 'Light Mode' : 'Dark Mode'}
+ {theme === 'dark' ? : }
+
+ setIsMobileMenuOpen(!isMobileMenuOpen)}
+ aria-label="Toggle menu"
+ >
+ {isMobileMenuOpen ? : }
-
- {!user ? (
- setIsMobileMenuOpen(false)}
- className="w-full text-center py-3 rounded-xl font-bold uppercase tracking-wider bg-blue-600 text-white"
- >
- Sign In
-
- ) : (
- <>
- setIsMobileMenuOpen(false)}
- className="w-full text-center py-3 rounded-xl font-bold uppercase tracking-wider bg-black dark:bg-white text-white dark:text-black"
- >
- {user.role === 'CUSTOMER' ? 'My Profile' : 'Dashboard'}
-
- { useAuth().logout(); setIsMobileMenuOpen(false); }}
- className="w-full text-center py-3 rounded-xl font-bold uppercase tracking-wider bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400"
- >
- Sign Out
-
- >
- )}
- )}
+
+ {/* ── Mobile Full-Screen Overlay ── */}
+
+ {isMobileMenuOpen && (
+
+ {/* Overlay Header */}
+
+
+
setIsMobileMenuOpen(false)}
+ className="p-2 text-zinc-600 dark:text-zinc-400"
+ aria-label="Close menu"
+ >
+
+
+
+
+ {/* Overlay Links */}
+
+ {[
+ { label: 'How It Works', id: 'how-it-works' },
+ { label: 'Services', id: 'services' },
+ { label: 'Intelligence', id: 'intelligence' },
+ ].map((item) => (
+ scrollToSection(item.id)}
+ className="text-2xl font-bold text-zinc-900 dark:text-white py-3 hover:text-blue-500 transition-colors"
+ >
+ {item.label}
+
+ ))}
+
+ setIsMobileMenuOpen(false)}
+ className="text-2xl font-bold text-zinc-900 dark:text-white py-3 hover:text-blue-500 transition-colors"
+ >
+ Pricing
+
+
+
+
+ {/* Overlay Bottom Actions */}
+
+ {!user ? (
+ <>
+ setIsMobileMenuOpen(false)}
+ className="block w-full text-center py-4 rounded-xl text-base font-bold bg-blue-500 text-white hover:bg-blue-600 transition-colors shadow-sm"
+ >
+ Get Started
+
+ setIsMobileMenuOpen(false)}
+ className="block w-full text-center py-4 rounded-xl text-base font-bold border border-zinc-200 dark:border-white/10 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-white/5 transition-colors"
+ >
+ Sign In
+
+ >
+ ) : (
+ <>
+ setIsMobileMenuOpen(false)}
+ className="block w-full text-center py-4 rounded-xl text-base font-bold bg-blue-500 text-white hover:bg-blue-600 transition-colors shadow-sm"
+ >
+ {user.role === 'CUSTOMER' ? 'My Profile' : 'Dashboard'}
+
+ { useAuth().logout(); setIsMobileMenuOpen(false); }}
+ className="block w-full text-center py-4 rounded-xl text-base font-bold border border-red-200 dark:border-red-500/20 text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/10 transition-colors"
+ >
+ Sign Out
+
+ >
+ )}
+
+
+ )}
+
+
{/* ============================================ */}
{/* HERO SECTION — "The Overhead View" */}
{/* Aerospace Precision & Hometown Trust */}
@@ -293,7 +435,7 @@ const Landing = () => {
▸
- 2,847 PROPERTIES TRACKED
+ 5,847 PROPERTIES TRACKED
DFW METRO · ZONE 2
@@ -321,20 +463,48 @@ const Landing = () => {
+ {/* ── TRUST BAR — Animated stat counters ── */}
+
+
+
+ {[
+ { value: 5000, suffix: '+', label: 'Properties Scanned' },
+ { value: 200, suffix: '+', label: 'Contractors Onboarded' },
+ { value: 98, suffix: '%', label: 'Estimate Accuracy' },
+ { value: 15, suffix: ' min', label: 'Avg Report Time' },
+ ].map((stat, i) => (
+
+
statRefs.current[i + 3] = el}
+ data-target={stat.value}
+ data-suffix={stat.suffix}
+ className="text-3xl md:text-4xl font-mono font-bold text-zinc-900 dark:text-white tracking-tight"
+ >
+ 0{stat.suffix}
+
+
+ {stat.label}
+
+
+ ))}
+
+
+
+
{/* ── Shingle Divider ── */}
{/* PROCESS SECTION (From Sky to Safety) */}
-
+
The Workflow
From Sky to Safety in 3 Steps.
-
+
{/* Step 1 */}
-
+
LIDAR_MAPPING_ACTIVE
@@ -354,7 +524,7 @@ const Landing = () => {
{/* Step 2 */}
-
+
02
The Diagnosis.
@@ -384,7 +554,7 @@ const Landing = () => {
{/* Step 3 */}
-
+
@@ -424,10 +594,14 @@ const Landing = () => {
{/* NEW SERVICES SECTION */}
-
+
+
+
{/* NEW INTELLIGENCE MAP SECTION */}
-
+
+
+
{/* ── Shingle Divider ── */}
@@ -445,7 +619,7 @@ const Landing = () => {
{/* Stat Card 1 — Insurance Denials */}
-
+
{
{/* Stat Card 2 — Life Reduced */}
-
+
{
{/* Stat Card 3 — Estimate Time */}
-
+
{
{/* ── Shingle Divider ── */}
+ {/* ── THE SPLIT — B2C / B2B Routing ── */}
+
+
+
+
+
◆ Dual Platform
+
+ Which side of the roof are you on?
+
+
+ Whether you need your roof fixed or you fix roofs for a living — we built LynkedUp for both.
+
+
+ {/* Roof Ridge — interactive peak shape, highlights on card hover */}
+
+
+ {/* Left slope — Homeowner side */}
+
+
+ {/* Right slope — Contractor side */}
+
+
+ {/* Peak glow — pulses on either hover */}
+
+
+
+
+
+
+
+ {/* Homeowner Card */}
+
setHoveredSplit('homeowner')}
+ onMouseLeave={() => setHoveredSplit(null)}
+ onTouchStart={() => setHoveredSplit('homeowner')}
+ >
+
+
+
+
+
+
I'm a Homeowner
+
+ Get a transparent, AI-powered roof assessment. Know exactly what's wrong, what it costs, and who to trust — before anyone knocks on your door.
+
+
+ {['Free drone inspection', 'AI damage assessment', 'Verified contractor matching', 'Insurance claim support'].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+ Protect My Home
+
+
+
+ ✓ Residential Verified
+
+
+
+
+ {/* Contractor Card */}
+
setHoveredSplit('contractor')}
+ onMouseLeave={() => setHoveredSplit(null)}
+ onTouchStart={() => setHoveredSplit('contractor')}
+ >
+
+
+
+
I'm a Contractor
+
+ Stop chasing leads. Our intelligence engine surfaces storm-damaged properties, pre-qualifies homeowners, and routes jobs directly to your crew.
+
+
+ {['AI-powered lead generation', 'Storm path intelligence', 'Automated estimates', 'Crew dispatch & scheduling'].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+ Grow My Business
+
+
+
+ ★ Contractor Certified
+
+
+
+
+
+
+
+ {/* ── Shingle Divider ── */}
+
+
{/* INDUSTRIES THAT LOVE US */}
@@ -634,20 +942,20 @@ const Landing = () => {
{/* CRANE CTA — "One crane, one card, one moment" */}
{/* Construction signature element before footer */}
{/* ══════════════════════════════════════════════════ */}
-
+
{/* Blueprint grid background */}
-
+
{/* Tower Crane SVG — full-scale blueprint line art */}
-
+
{/* ===== TOWER / LATTICE MAST ===== */}
- {/* Main vertical rails */}
-
-
+ {/* Main vertical rails — extended to full viewBox height */}
+
+
{/* Horizontal rungs */}
@@ -656,6 +964,9 @@ const Landing = () => {
+
+
+
{/* X-bracing */}
@@ -663,8 +974,14 @@ const Landing = () => {
-
-
+
+
+
+
+
+
+
+
{/* ===== SLEWING PLATFORM + OPERATOR CAB ===== */}
@@ -723,19 +1040,19 @@ const Landing = () => {
{/* ===== HOIST CABLE ===== */}
-
+
{/* ===== HOOK BLOCK ===== */}
-
+
{/* Hook */}
-
-
+
+
{/* CTA Card — "suspended" from crane, swings like a pendulum */}
-
+
{/* Attachment point — visual connector to cable above */}