Untitled
unknown
plain_text
a year ago
28 kB
16
Indexable
import React, { useMemo, useState } from "react";
import { motion } from "framer-motion";
import {
Car,
Home,
ShieldCheck,
LifeBuoy,
Building2,
Phone,
Mail,
MapPin,
CheckCircle2,
ChevronRight,
Quote,
Star,
Menu,
X,
} from "lucide-react";
/**
* IDG-style insurance website
* - TailwindCSS only, no external UI kit required
* - Single-file React component ready to drop into your app
* - Clean, conversion-focused layout similar to a modern independent agency site
*/
const NavLink = ({ href, children, onClick }: any) => (
<a
href={href}
onClick={onClick}
className="px-3 py-2 text-sm font-medium text-slate-700 hover:text-slate-900 hover:underline underline-offset-4"
>
{children}
</a>
);
const Section = ({ id, className = "", children, bg = "" }: any) => (
<section id={id} className={`${bg} ${className}`}>
{children}
</section>
);
const Container = ({ children, className = "" }: any) => (
<div className={`mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 ${className}`}>{children}</div>
);
const Stat = ({ label, value }: any) => (
<div className="text-center">
<div className="text-3xl sm:text-4xl font-bold tracking-tight text-slate-900">{value}</div>
<div className="mt-1 text-sm text-slate-600">{label}</div>
</div>
);
const Feature = ({ Icon, title, children }: any) => (
<div className="rounded-2xl border border-slate-200 p-6 shadow-sm bg-white">
<div className="flex items-center gap-3">
<div className="rounded-xl bg-slate-100 p-2">
<Icon className="h-5 w-5" />
</div>
<h3 className="font-semibold text-slate-900">{title}</h3>
</div>
<p className="mt-3 text-sm text-slate-600 leading-6">{children}</p>
</div>
);
const TestimonialCard = ({ quote, author, role }: any) => (
<div className="rounded-2xl border border-slate-200 p-6 bg-white shadow-sm h-full flex flex-col">
<Quote className="h-6 w-6 opacity-30" />
<p className="mt-3 text-slate-700">{quote}</p>
<div className="mt-6 flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-slate-200" />
<div>
<div className="font-semibold text-slate-900">{author}</div>
<div className="text-xs text-slate-600">{role}</div>
</div>
</div>
</div>
);
const carriers = [
"Progressive",
"Travelers",
"Nationwide",
"Safeco",
"Allied",
"Liberty Mutual",
"MetLife",
"Chubb",
];
const useScrollLock = (locked: boolean) => {
React.useEffect(() => {
if (locked) {
const original = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = original;
};
}
}, [locked]);
};
function Header({ onQuoteClick }: { onQuoteClick: () => void }) {
const [open, setOpen] = useState(false);
useScrollLock(open);
const close = () => setOpen(false);
const NavLinks = ({ onClick }: any) => (
<>
<NavLink href="#personal" onClick={onClick}>Personal</NavLink>
<NavLink href="#business" onClick={onClick}>Business</NavLink>
<NavLink href="#about" onClick={onClick}>About</NavLink>
<NavLink href="#resources" onClick={onClick}>Resources</NavLink>
<NavLink href="#contact" onClick={onClick}>Contact</NavLink>
</>
);
return (
<header className="sticky top-0 z-50 w-full border-b border-slate-200 bg-white/80 backdrop-blur">
<Container className="flex h-16 items-center justify-between">
<a href="#" className="flex items-center gap-2">
<div className="h-9 w-9 rounded-xl bg-slate-900 text-white grid place-content-center font-bold">ID</div>
<div className="text-slate-900 font-semibold leading-tight">
<div>InsuranceDirect</div>
<div className="text-xs font-normal text-slate-500 -mt-0.5">Independent Agency</div>
</div>
</a>
<nav className="hidden md:flex items-center">
<NavLinks />
</nav>
<div className="hidden md:flex items-center gap-3">
<a href="tel:+15551234567" className="inline-flex items-center text-sm text-slate-700 hover:text-slate-900">
<Phone className="h-4 w-4 mr-2" /> (555) 123-4567
</a>
<button
onClick={onQuoteClick}
className="rounded-xl bg-slate-900 px-4 py-2 text-white text-sm font-semibold shadow hover:shadow-md transition"
>
Get a Quote
</button>
</div>
<button className="md:hidden p-2" onClick={() => setOpen(true)} aria-label="Open menu">
<Menu className="h-6 w-6" />
</button>
</Container>
{/* Mobile menu */}
{open && (
<div className="fixed inset-0 z-50 bg-white">
<Container className="flex h-16 items-center justify-between border-b">
<div className="flex items-center gap-2">
<div className="h-9 w-9 rounded-xl bg-slate-900 text-white grid place-content-center font-bold">ID</div>
<div className="text-slate-900 font-semibold">InsuranceDirect</div>
</div>
<button className="p-2" onClick={() => setOpen(false)} aria-label="Close menu">
<X className="h-6 w-6" />
</button>
</Container>
<Container className="py-4 space-y-1">
<NavLinks onClick={close} />
<button
onClick={() => {
close();
onQuoteClick();
}}
className="mt-4 w-full rounded-xl bg-slate-900 px-4 py-3 text-white text-sm font-semibold shadow"
>
Get a Quote
</button>
<a href="tel:+15551234567" className="mt-2 inline-flex items-center text-sm text-slate-700">
<Phone className="h-4 w-4 mr-2" /> (555) 123-4567
</a>
</Container>
</div>
)}
</header>
);
}
function Hero({ onQuoteClick }: { onQuoteClick: () => void }) {
return (
<Section id="home" className="relative overflow-hidden bg-gradient-to-b from-slate-50 to-white">
<Container className="py-16 sm:py-24 grid lg:grid-cols-2 gap-10 items-center">
<div>
<motion.h1
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="text-4xl sm:text-5xl font-bold tracking-tight text-slate-900"
>
Insurance that works the way you do.
</motion.h1>
<p className="mt-4 text-lg text-slate-700 max-w-prose">
As an independent agency, we shop top-rated carriers to get you the right coverage at the right price—without the hassle.
</p>
<div className="mt-6 flex flex-wrap items-center gap-3">
<button
onClick={onQuoteClick}
className="rounded-xl bg-slate-900 px-5 py-3 text-white font-semibold shadow hover:shadow-md"
>
Get a Quote
</button>
<a href="#about" className="inline-flex items-center font-semibold text-slate-900">
Why choose us <ChevronRight className="ml-1 h-5 w-5" />
</a>
</div>
<div className="mt-10 grid grid-cols-3 gap-6">
<Stat label="Clients protected" value="8,400+" />
<Stat label="Carriers compared" value="30+" />
<Stat label="Claims supported" value="$12M" />
</div>
</div>
<div className="relative">
<div className="aspect-[4/3] w-full rounded-3xl bg-gradient-to-tr from-slate-200 to-slate-100 shadow-inner" />
<div className="absolute -bottom-6 -left-6 rounded-2xl bg-white shadow p-4 border border-slate-200">
<div className="text-sm font-semibold">Average savings</div>
<div className="text-2xl font-bold">$642/yr</div>
<div className="text-xs text-slate-500">Based on new clients 2024–2025</div>
</div>
</div>
</Container>
</Section>
);
}
function CarriersMarquee() {
return (
<Section className="py-8 border-y border-slate-200 bg-white">
<Container>
<div className="text-center text-xs uppercase tracking-wider text-slate-500 mb-3">We compare quotes from</div>
<div className="flex flex-wrap items-center justify-center gap-x-8 gap-y-4 opacity-80">
{carriers.map((c) => (
<div key={c} className="text-slate-700 font-semibold text-sm">{c}</div>
))}
</div>
</Container>
</Section>
);
}
function PersonalLines({ onQuoteClick }: { onQuoteClick: () => void }) {
const items = [
{
icon: Car,
title: "Auto Insurance",
copy:
"Customized protection with discounts for safe drivers, bundling, telematics and more.",
},
{
icon: Home,
title: "Homeowners & Renters",
copy:
"Coverage for what matters most—your home, belongings, and liability, tailored to your risks.",
},
{
icon: LifeBuoy,
title: "Life & Disability",
copy:
"Term and permanent options to secure your family’s future with flexible riders.",
},
];
return (
<Section id="personal" className="py-16 bg-slate-50">
<Container>
<div className="max-w-2xl">
<h2 className="text-3xl font-bold tracking-tight text-slate-900">Personal Insurance</h2>
<p className="mt-2 text-slate-600">
Smarter coverage for your car, home and life. We shop multiple carriers and guide you every step of the way.
</p>
</div>
<div className="mt-8 grid grid-cols-1 md:grid-cols-3 gap-6">
{items.map((it) => (
<Feature key={it.title} Icon={it.icon} title={it.title}>
{it.copy}
</Feature>
))}
</div>
<div className="mt-8">
<button
onClick={onQuoteClick}
className="rounded-xl bg-slate-900 px-5 py-3 text-white font-semibold shadow hover:shadow-md"
>
Get Personal Quote
</button>
</div>
</Container>
</Section>
);
}
function BusinessLines({ onQuoteClick }: { onQuoteClick: () => void }) {
const items = [
{
icon: Building2,
title: "General Liability",
copy: "Protection for injuries, property damage and advertising liability.",
},
{
icon: ShieldCheck,
title: "Commercial Auto",
copy: "Covers vehicles used for business, with custom fleet options.",
},
{
icon: LifeBuoy,
title: "Workers’ Comp & Benefits",
copy: "Take care of your people with compliant, cost‑effective coverage.",
},
];
return (
<Section id="business" className="py-16 bg-white">
<Container>
<div className="max-w-2xl">
<h2 className="text-3xl font-bold tracking-tight text-slate-900">Business Insurance</h2>
<p className="mt-2 text-slate-600">
Tailored policies for contractors, retailers, professionals, and more. Bundle and save across lines.
</p>
</div>
<div className="mt-8 grid grid-cols-1 md:grid-cols-3 gap-6">
{items.map((it) => (
<Feature key={it.title} Icon={it.icon} title={it.title}>
{it.copy}
</Feature>
))}
</div>
<div className="mt-8">
<button
onClick={onQuoteClick}
className="rounded-xl bg-slate-900 px-5 py-3 text-white font-semibold shadow hover:shadow-md"
>
Get Business Quote
</button>
</div>
</Container>
</Section>
);
}
function WhyChooseUs() {
const points = [
"Independent & unbiased guidance",
"Proactive annual policy reviews",
"Local support when you need it",
"Bundle discounts & flexible billing",
];
return (
<Section id="about" className="py-16 bg-slate-50">
<Container className="grid lg:grid-cols-2 gap-10 items-center">
<div>
<h2 className="text-3xl font-bold tracking-tight text-slate-900">Why choose us</h2>
<p className="mt-3 text-slate-600">
We believe insurance should be simple, transparent and tailored to you. Our licensed advisors compare options across top carriers and help you select coverage with confidence.
</p>
<ul className="mt-6 space-y-3">
{points.map((p) => (
<li key={p} className="flex items-start gap-3">
<CheckCircle2 className="mt-0.5 h-5 w-5 text-slate-900" />
<span className="text-slate-700">{p}</span>
</li>
))}
</ul>
</div>
<div className="rounded-3xl border border-slate-200 p-6 bg-white shadow-sm">
<div className="text-sm font-semibold text-slate-900">Agency at a glance</div>
<div className="mt-4 grid grid-cols-2 gap-6">
<Stat label="Years combined exp." value="50+" />
<Stat label="5‑star reviews" value="1,200+" />
<Stat label="States served" value="15" />
<Stat label="Carrier partners" value="30+" />
</div>
</div>
</Container>
</Section>
);
}
function Resources() {
const faqs = [
{
q: "How does an independent agency work?",
a: "We represent many different carriers. We’ll gather your details once, compare coverage and pricing, and present clear recommendations.",
},
{
q: "Can I bundle policies?",
a: "Yes. Bundling home, auto, and other lines often unlocks significant discounts and simplifies billing.",
},
{
q: "Do you charge broker fees?",
a: "In most states, no. We’re compensated by the carrier you choose. If a fee applies, we’ll disclose it upfront.",
},
{
q: "How fast can I get proof of insurance?",
a: "Often same‑day for many personal lines and within 24–48 hours for common business policies.",
},
];
return (
<Section id="resources" className="py-16 bg-white">
<Container>
<div className="max-w-2xl">
<h2 className="text-3xl font-bold tracking-tight text-slate-900">Resources & FAQs</h2>
<p className="mt-2 text-slate-600">Quick answers to common questions.</p>
</div>
<div className="mt-8 grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2 space-y-3">
{faqs.map(({ q, a }) => (
<details key={q} className="group rounded-2xl border border-slate-200 bg-white p-5">
<summary className="cursor-pointer select-none list-none font-semibold text-slate-900 flex items-center justify-between">
{q}
<ChevronRight className="h-5 w-5 transition group-open:rotate-90" />
</summary>
<p className="mt-3 text-slate-700">{a}</p>
</details>
))}
</div>
<div className="rounded-2xl border border-slate-200 p-6 bg-slate-50">
<div className="text-sm font-semibold text-slate-900">Latest articles</div>
<ul className="mt-3 space-y-3 text-sm">
<li className="flex items-start gap-2">
<Star className="h-4 w-4 mt-0.5" />
<a href="#" className="hover:underline">Home insurance basics: deductible vs. premium</a>
</li>
<li className="flex items-start gap-2">
<Star className="h-4 w-4 mt-0.5" />
<a href="#" className="hover:underline">How to cut auto rates without cutting coverage</a>
</li>
<li className="flex items-start gap-2">
<Star className="h-4 w-4 mt-0.5" />
<a href="#" className="hover:underline">Business insurance checklist for new LLCs</a>
</li>
</ul>
</div>
</div>
</Container>
</Section>
);
}
function Testimonials() {
const items = [
{
quote:
"Made switching effortless and saved us over $500 a year while improving our coverage.",
author: "Jordan L.",
role: "Home & Auto",
},
{
quote:
"They explained everything clearly and handled my COI requests within hours.",
author: "Avery P.",
role: "Contractor",
},
{
quote:
"Finally an agency that answers the phone and advocates during claims.",
author: "Sam D.",
role: "Small Business Owner",
},
];
return (
<Section className="py-16 bg-slate-50">
<Container>
<div className="max-w-2xl">
<h2 className="text-3xl font-bold tracking-tight text-slate-900">You’re in good company</h2>
<p className="mt-2 text-slate-600">What clients are saying.</p>
</div>
<div className="mt-8 grid grid-cols-1 md:grid-cols-3 gap-6">
{items.map((t, i) => (
<TestimonialCard key={i} {...t} />
))}
</div>
</Container>
</Section>
);
}
function Contact() {
return (
<Section id="contact" className="py-16 bg-white">
<Container className="grid lg:grid-cols-2 gap-10">
<div>
<h2 className="text-3xl font-bold tracking-tight text-slate-900">Let’s talk</h2>
<p className="mt-2 text-slate-600">Questions, quotes or policy help—our licensed team is here for you.</p>
<div className="mt-6 space-y-3 text-sm text-slate-700">
<div className="flex items-center"><Phone className="h-4 w-4 mr-2" />(555) 123-4567</div>
<div className="flex items-center"><Mail className="h-4 w-4 mr-2" />[email protected]</div>
<div className="flex items-center"><MapPin className="h-4 w-4 mr-2" />123 Main St, Suite 200, Your City, ST</div>
</div>
</div>
<form className="rounded-2xl border border-slate-200 p-6 bg-slate-50 shadow-sm grid gap-4">
<div>
<label className="block text-sm font-medium text-slate-700">Name</label>
<input className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" placeholder="Jane Doe" />
</div>
<div className="grid sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-slate-700">Email</label>
<input type="email" className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" placeholder="[email protected]" />
</div>
<div>
<label className="block text-sm font-medium text-slate-700">Phone</label>
<input className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" placeholder="(555) 555‑5555" />
</div>
</div>
<div>
<label className="block text-sm font-medium text-slate-700">Message</label>
<textarea rows={4} className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" placeholder="How can we help?"></textarea>
</div>
<button type="button" className="rounded-xl bg-slate-900 px-5 py-3 text-white font-semibold shadow hover:shadow-md">Send message</button>
<p className="text-xs text-slate-500">By submitting, you agree to our <a href="#" className="underline">Privacy Policy</a>.</p>
</form>
</Container>
</Section>
);
}
function Footer() {
return (
<footer className="border-t border-slate-200 bg-white">
<Container className="py-10 grid md:grid-cols-4 gap-10">
<div>
<div className="flex items-center gap-2">
<div className="h-9 w-9 rounded-xl bg-slate-900 text-white grid place-content-center font-bold">ID</div>
<div className="text-slate-900 font-semibold">InsuranceDirect</div>
</div>
<p className="mt-3 text-sm text-slate-600">Independent, client‑first insurance across personal and commercial lines.</p>
</div>
<div>
<div className="text-sm font-semibold text-slate-900">Personal</div>
<ul className="mt-3 space-y-2 text-sm text-slate-600">
<li><a href="#personal" className="hover:underline">Auto</a></li>
<li><a href="#personal" className="hover:underline">Home</a></li>
<li><a href="#personal" className="hover:underline">Life</a></li>
</ul>
</div>
<div>
<div className="text-sm font-semibold text-slate-900">Business</div>
<ul className="mt-3 space-y-2 text-sm text-slate-600">
<li><a href="#business" className="hover:underline">General Liability</a></li>
<li><a href="#business" className="hover:underline">Commercial Auto</a></li>
<li><a href="#business" className="hover:underline">Workers’ Comp</a></li>
</ul>
</div>
<div>
<div className="text-sm font-semibold text-slate-900">Company</div>
<ul className="mt-3 space-y-2 text-sm text-slate-600">
<li><a href="#about" className="hover:underline">About</a></li>
<li><a href="#resources" className="hover:underline">Resources</a></li>
<li><a href="#contact" className="hover:underline">Contact</a></li>
<li><a href="#" className="hover:underline">Privacy</a></li>
</ul>
</div>
</Container>
<div className="border-t border-slate-200">
<Container className="py-6 text-xs text-slate-500 flex flex-col sm:flex-row items-center justify-between gap-2">
<div>© {new Date().getFullYear()} Your Agency Name. All rights reserved.</div>
<div className="flex items-center gap-4">
<a href="tel:+15551234567" className="inline-flex items-center"><Phone className="h-3.5 w-3.5 mr-1" />(555) 123-4567</a>
<a href="mailto:[email protected]" className="inline-flex items-center"><Mail className="h-3.5 w-3.5 mr-1" />[email protected]</a>
</div>
</Container>
</div>
</footer>
);
}
function QuoteModal({ open, onClose }: { open: boolean; onClose: () => void }) {
const [type, setType] = useState("Personal");
const [step, setStep] = useState(1);
React.useEffect(() => {
if (!open) {
setStep(1);
setType("Personal");
}
}, [open]);
if (!open) return null;
return (
<div className="fixed inset-0 z-50 bg-black/40 backdrop-blur-sm grid place-items-center p-4">
<div className="w-full max-w-2xl rounded-2xl bg-white shadow-xl border border-slate-200">
<div className="flex items-center justify-between p-4 border-b">
<div className="font-semibold">Start your quote</div>
<button onClick={onClose} className="p-2" aria-label="Close"><X className="h-5 w-5" /></button>
</div>
<div className="p-6 grid gap-4">
{step === 1 && (
<div className="grid gap-4">
<div className="text-sm text-slate-600">What kind of coverage are you looking for?</div>
<div className="grid sm:grid-cols-2 gap-3">
{["Personal", "Business"].map((t) => (
<button
key={t}
onClick={() => setType(t)}
className={`rounded-xl border px-4 py-3 text-left ${
type === t ? "border-slate-900 bg-slate-50" : "border-slate-300"
}`}
>
<div className="font-semibold">{t}</div>
<div className="text-sm text-slate-600">Auto, Home, Life · GL, WC, BOP</div>
</button>
))}
</div>
<div className="flex justify-end">
<button onClick={() => setStep(2)} className="rounded-xl bg-slate-900 px-5 py-3 text-white font-semibold">Next</button>
</div>
</div>
)}
{step === 2 && (
<div className="grid gap-4">
<div className="grid sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-slate-700">Full name</label>
<input className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" />
</div>
<div>
<label className="block text-sm font-medium text-slate-700">Email</label>
<input type="email" className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" />
</div>
</div>
<div className="grid sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-slate-700">Phone</label>
<input className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" />
</div>
<div>
<label className="block text-sm font-medium text-slate-700">ZIP code</label>
<input className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" />
</div>
</div>
<div>
<label className="block text-sm font-medium text-slate-700">Notes</label>
<textarea rows={3} className="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400" placeholder={`Tell us about your ${type.toLowerCase()} insurance needs...`} />
</div>
<div className="flex items-center justify-between">
<button onClick={() => setStep(1)} className="px-4 py-2 rounded-xl border border-slate-300">Back</button>
<button onClick={onClose} className="rounded-xl bg-slate-900 px-5 py-3 text-white font-semibold">Submit</button>
</div>
<p className="text-xs text-slate-500">Submitting this form doesn’t affect your credit score. We’ll only use your information to provide quotes.</p>
</div>
)}
</div>
</div>
</div>
);
}
export default function InsuranceAgencySite() {
const [quoteOpen, setQuoteOpen] = useState(false);
return (
<div className="min-h-screen bg-white text-slate-900">
{/* Top bar */}
<div className="bg-slate-900 text-white">
<Container className="flex items-center justify-between py-2 text-xs">
<div className="flex items-center gap-4">
<span className="hidden sm:inline">Licensed in 15 states</span>
<span className="hidden sm:inline">|</span>
<a href="tel:+15551234567" className="inline-flex items-center"><Phone className="h-3.5 w-3.5 mr-1" />24/7 Claims Support</a>
</div>
<a href="mailto:[email protected]" className="inline-flex items-center"><Mail className="h-3.5 w-3.5 mr-1" />[email protected]</a>
</Container>
</div>
<Header onQuoteClick={() => setQuoteOpen(true)} />
<Hero onQuoteClick={() => setQuoteOpen(true)} />
<CarriersMarquee />
<PersonalLines onQuoteClick={() => setQuoteOpen(true)} />
<BusinessLines onQuoteClick={() => setQuoteOpen(true)} />
<WhyChooseUs />
<Resources />
<Testimonials />
<Contact />
<Footer />
<QuoteModal open={quoteOpen} onClose={() => setQuoteOpen(false)} />
</div>
);
}
Editor is loading...
Leave a Comment