first commit

Made-with: Cursor
This commit is contained in:
“kirukib” 2026-03-17 19:24:08 +03:00
parent df26bc92ce
commit 29a76a17c8
16 changed files with 988 additions and 70 deletions

View File

@ -0,0 +1,67 @@
import { EmbeddedScheduler } from "@/components/embedded-scheduler";
import { ContactForm } from "@/components/contact-form";
import { offices } from "@/lib/site-config";
export default function AppointmentPage() {
return (
<div className="bg-slate-50 py-10 sm:py-12 md:py-16">
<div className="mx-auto max-w-5xl px-4 md:px-6">
<header className="mb-8 space-y-3">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500">
Appointments
</p>
<h1 className="text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl">
Book your tax, insurance, or bookkeeping appointment
</h1>
<p className="max-w-2xl text-sm text-slate-600 sm:text-base">
Choose a convenient time for an inperson or virtual appointment.
Use the form below to tell us what you need and which office you
prefer, and we&apos;ll follow up to confirm the details.
</p>
</header>
<div className="grid gap-6 md:grid-cols-[3fr,2fr]">
<div className="space-y-4">
<EmbeddedScheduler />
<ContactForm mode="appointment" />
</div>
<aside className="space-y-4 rounded-2xl border border-slate-200 bg-white p-5 text-sm text-slate-800">
<h2 className="text-base font-semibold text-slate-900">
Office details
</h2>
{offices.map((office) => (
<div key={office.id} id={office.id} className="space-y-1">
<div className="font-semibold">{office.label}</div>
<div className="text-slate-700">
{office.addressLines.map((line) => (
<div key={line}>{line}</div>
))}
</div>
<div>
<a
href={office.phoneHref}
className="text-slate-900 hover:text-sky-700"
>
{office.phone}
</a>
</div>
<div>
<a
href={office.emailHref}
className="text-slate-700 hover:text-sky-700"
>
{office.email}
</a>
</div>
</div>
))}
<p className="pt-2 text-xs text-slate-600">
Please bring any recent tax documents, identification, and
financial records that may help us serve you better.
</p>
</aside>
</div>
</div>
</div>
);
}

43
src/app/contact/page.tsx Normal file
View File

@ -0,0 +1,43 @@
import { ContactForm } from "@/components/contact-form";
import { LocationsSection } from "@/components/locations-section";
export default function ContactPage() {
return (
<div className="bg-slate-50">
<section className="py-10 sm:py-12 md:py-16">
<div className="mx-auto max-w-5xl px-4 md:px-6">
<header className="mb-8 space-y-3">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500">
Contact
</p>
<h1 className="text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl">
We&apos;re here to help
</h1>
<p className="max-w-2xl text-sm text-slate-600 sm:text-base">
Have a question about your taxes, insurance, or bookkeeping? Send
us a message and our team will respond as soon as possible.
</p>
</header>
<div className="grid gap-6 md:grid-cols-[3fr,2fr]">
<ContactForm mode="contact" />
<div className="rounded-2xl border border-slate-200 bg-white p-5 text-sm text-slate-800">
<h2 className="text-base font-semibold text-slate-900">
Phone &amp; email
</h2>
<p className="mt-2 text-slate-700">
You can also reach us by phone or email using the office
details below.
</p>
<p className="mt-3 text-xs text-slate-600">
For timesensitive questions during tax season, calling one of
our offices is usually the fastest way to get help.
</p>
</div>
</div>
</div>
</section>
<LocationsSection />
</div>
);
}

View File

@ -121,9 +121,14 @@
@apply border-border outline-ring/50; @apply border-border outline-ring/50;
} }
body { body {
font-family: var(--font-sans), system-ui, -apple-system, sans-serif;
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
html { html {
@apply font-sans; font-family: var(--font-sans), system-ui, -apple-system, sans-serif;
} }
}
.font-serif {
font-family: var(--font-display), "Playfair Display", serif;
} }

View File

@ -1,20 +1,24 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Inter, Playfair_Display } from "next/font/google";
import "./globals.css"; import "./globals.css";
import { SiteHeader } from "@/components/site-header";
import { SiteFooter } from "@/components/site-footer";
import { FloatingCallButton } from "@/components/floating-call-button";
const geistSans = Geist({ const sans = Inter({
variable: "--font-geist-sans",
subsets: ["latin"], subsets: ["latin"],
variable: "--font-sans",
}); });
const geistMono = Geist_Mono({ export const playfair = Playfair_Display({
variable: "--font-geist-mono",
subsets: ["latin"], subsets: ["latin"],
variable: "--font-display",
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "Ghion Financial — Tax, Insurance & Bookkeeping",
description: "Generated by create next app", description:
"Ghion Financial provides trusted income tax preparation, insurance services, and bookkeeping for individuals, families, and businesses across Virginia and Maryland.",
}; };
export default function RootLayout({ export default function RootLayout({
@ -25,9 +29,14 @@ export default function RootLayout({
return ( return (
<html lang="en"> <html lang="en">
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${sans.variable} ${playfair.variable} min-h-screen bg-slate-50 text-slate-900 antialiased`}
> >
{children} <div className="flex min-h-screen flex-col">
<SiteHeader />
<main className="flex-1">{children}</main>
<SiteFooter />
</div>
<FloatingCallButton />
</body> </body>
</html> </html>
); );

View File

@ -1,65 +1,17 @@
import Image from "next/image"; import { Hero } from "@/components/hero";
import { ServicesSection } from "@/components/services-section";
import { AppointmentCards } from "@/components/appointment-cards";
import { LocationsSection } from "@/components/locations-section";
import { ReviewsSection } from "@/components/reviews-section";
export default function Home() { export default function Home() {
return ( return (
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black"> <>
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start"> <Hero />
<Image <ServicesSection />
className="dark:invert" <AppointmentCards />
src="/next.svg" <ReviewsSection />
alt="Next.js logo" <LocationsSection />
width={100} </>
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</div>
</main>
</div>
); );
} }

View File

@ -0,0 +1,76 @@
import Link from "next/link";
import { offices } from "@/lib/site-config";
import { Button } from "@/components/ui/button";
export function AppointmentCards() {
return (
<section className="bg-slate-900 py-10 sm:py-12 md:py-16">
<div className="mx-auto max-w-6xl px-4 text-slate-50 md:px-6">
<div className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
<div className="max-w-xl">
<h2 className="text-2xl font-semibold tracking-tight sm:text-3xl">
Book an appointment in minutes
</h2>
<p className="mt-3 text-sm text-slate-300 sm:text-base">
Choose the office that is most convenient for you. We offer
inperson and virtual appointments for tax preparation,
insurance, and bookkeeping.
</p>
</div>
<p className="text-xs text-slate-400">
Serving Virginia &amp; Maryland clients yearround.
</p>
</div>
<div className="mt-6 grid gap-4 md:grid-cols-2">
{offices.map((office) => (
<article
key={office.id}
className="flex flex-col justify-between rounded-2xl border border-slate-700 bg-slate-900/60 p-5"
>
<div>
<h3 className="text-lg font-semibold text-sky-300">
{office.label}
</h3>
<div className="mt-2 text-sm text-slate-200">
{office.addressLines.map((line) => (
<div key={line}>{line}</div>
))}
</div>
<div className="mt-2 text-sm">
<a
href={office.phoneHref}
className="font-medium text-sky-300 hover:underline"
>
{office.phone}
</a>
</div>
<div className="mt-1 text-xs text-slate-300">
Inperson &amp; virtual appointments available
</div>
</div>
<div className="mt-4 flex flex-wrap gap-2">
<Button
className="rounded-full bg-sky-500 px-5 text-xs font-semibold text-slate-950 hover:bg-sky-400"
>
<Link href={`/appointment#${office.id}`}>
Book {office.label.split(" ")[0]} Appointment
</Link>
</Button>
<Button
variant="outline"
size="sm"
className="rounded-full border-slate-500 bg-transparent text-xs text-slate-100 hover:bg-slate-800"
>
<a href={office.mapUrl} target="_blank" rel="noreferrer">
View on map
</a>
</Button>
</div>
</article>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,172 @@
"use client";
import { useState } from "react";
import { offices } from "@/lib/site-config";
import { Button } from "@/components/ui/button";
type Mode = "contact" | "appointment";
export function ContactForm({ mode }: { mode: Mode }) {
const [status, setStatus] = useState<"idle" | "submitted">("idle");
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const form = event.currentTarget;
if (!form.checkValidity()) {
form.reportValidity();
return;
}
setStatus("submitted");
}
const heading =
mode === "appointment"
? "Request an appointment"
: "Send us a message";
return (
<form
onSubmit={handleSubmit}
className="space-y-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"
noValidate
>
<h2 className="text-lg font-semibold text-slate-900">{heading}</h2>
<p className="text-xs text-slate-600">
Fill out the form and a member of our team will follow up within one
business day.
</p>
<div className="grid gap-3 sm:grid-cols-2">
<div className="space-y-1">
<label
htmlFor={`${mode}-name`}
className="text-xs font-medium text-slate-800"
>
Full name
</label>
<input
id={`${mode}-name`}
name="name"
required
className="h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900 outline-none ring-0 placeholder:text-slate-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-200"
/>
</div>
<div className="space-y-1">
<label
htmlFor={`${mode}-email`}
className="text-xs font-medium text-slate-800"
>
Email
</label>
<input
id={`${mode}-email`}
name="email"
type="email"
required
className="h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900 outline-none ring-0 placeholder:text-slate-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-200"
/>
</div>
</div>
<div className="grid gap-3 sm:grid-cols-2">
<div className="space-y-1">
<label
htmlFor={`${mode}-phone`}
className="text-xs font-medium text-slate-800"
>
Phone
</label>
<input
id={`${mode}-phone`}
name="phone"
type="tel"
required
className="h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900 outline-none ring-0 placeholder:text-slate-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-200"
/>
</div>
<div className="space-y-1">
<label
htmlFor={`${mode}-office`}
className="text-xs font-medium text-slate-800"
>
Preferred office
</label>
<select
id={`${mode}-office`}
name="office"
className="h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900 outline-none ring-0 focus:border-sky-500 focus:ring-2 focus:ring-sky-200"
defaultValue={offices[0]?.id}
>
{offices.map((office) => (
<option key={office.id} value={office.id}>
{office.label}
</option>
))}
</select>
</div>
</div>
{mode === "appointment" && (
<div className="grid gap-3 sm:grid-cols-2">
<div className="space-y-1">
<label
htmlFor="appointment-service"
className="text-xs font-medium text-slate-800"
>
Service type
</label>
<select
id="appointment-service"
name="service"
className="h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900 outline-none ring-0 focus:border-sky-500 focus:ring-2 focus:ring-sky-200"
defaultValue="tax"
>
<option value="tax">Tax preparation</option>
<option value="insurance">Insurance services</option>
<option value="bookkeeping">Bookkeeping</option>
</select>
</div>
<div className="space-y-1">
<label
htmlFor="appointment-preference"
className="text-xs font-medium text-slate-800"
>
Preferred time
</label>
<input
id="appointment-preference"
name="preferredTime"
placeholder="Weekday evenings, weekends, etc."
className="h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900 outline-none ring-0 placeholder:text-slate-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-200"
/>
</div>
</div>
)}
<div className="space-y-1">
<label
htmlFor={`${mode}-message`}
className="text-xs font-medium text-slate-800"
>
How can we help?
</label>
<textarea
id={`${mode}-message`}
name="message"
rows={4}
className="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none ring-0 placeholder:text-slate-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-200"
placeholder="Share any details that will help us prepare for your call or appointment."
/>
</div>
<Button
type="submit"
className="w-full h-11 rounded-full bg-sky-500 text-sm font-semibold text-slate-950 hover:bg-sky-400"
>
{mode === "appointment" ? "Submit appointment request" : "Send message"}
</Button>
{status === "submitted" && (
<p className="text-xs text-emerald-700">
Thank you. Your information has been received. We will contact you
soon.
</p>
)}
</form>
);
}

View File

@ -0,0 +1,15 @@
export function EmbeddedScheduler() {
return (
<div className="rounded-2xl border border-slate-200 bg-slate-50 p-4 text-sm text-slate-700">
<p className="font-semibold text-slate-900">
Online scheduler coming soon
</p>
<p className="mt-2">
This space is reserved for an embedded scheduling tool (such as
Calendly or a similar service). For now, please use the form on this
page or call one of our offices to request an appointment time.
</p>
</div>
);
}

View File

@ -0,0 +1,22 @@
"use client";
import { offices } from "@/lib/site-config";
export function FloatingCallButton() {
const vaOffice = offices.find((o) => o.id === "va");
if (!vaOffice) return null;
return (
<a
href={vaOffice.phoneHref}
className="fixed bottom-4 right-4 z-40 inline-flex h-12 items-center justify-center rounded-full bg-sky-500 px-4 text-sm font-semibold text-white shadow-lg shadow-sky-500/40 hover:bg-sky-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300"
>
<span className="mr-2 inline-flex h-7 w-7 items-center justify-center rounded-full bg-sky-600">
<span className="block h-3 w-3 rotate-45 border-2 border-white border-t-transparent border-l-transparent" />
</span>
Call Ghion Financial
</a>
);
}

65
src/components/hero.tsx Normal file
View File

@ -0,0 +1,65 @@
import Link from "next/link";
import { Button } from "@/components/ui/button";
export function Hero() {
return (
<section className="bg-slate-900 py-12 sm:py-16 md:py-20">
<div className="mx-auto flex max-w-6xl flex-col gap-10 px-4 md:flex-row md:items-center md:px-6">
<div className="max-w-xl space-y-5">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-sky-300">
Tax & Insurance Experts in the DMV
</p>
<div className="space-y-2">
<h1 className="font-serif text-4xl font-bold uppercase leading-tight tracking-tight text-white sm:text-5xl">
TAX &amp; CPA
</h1>
<h1 className="font-serif text-4xl font-bold uppercase leading-tight tracking-tight text-white sm:text-5xl">
BOOKKEEPING
</h1>
<p className="font-serif text-3xl italic leading-tight text-sky-300 sm:text-4xl">
+ SERVICES
</p>
</div>
<p className="max-w-lg text-sm leading-relaxed text-slate-200 sm:text-base">
At Ghion Financial, we help individuals, families, and businesses
with accurate income tax preparation, smart insurance choices, and
reliable bookkeeping so you can focus on what matters most.
</p>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
<Button
className="h-11 rounded-full bg-sky-500 px-6 text-sm font-semibold text-slate-950 hover:bg-sky-400"
>
<Link href="/appointment">Book Tax Appointment</Link>
</Button>
<Button
variant="outline"
className="h-11 rounded-full border-sky-400 bg-transparent px-6 text-sm font-semibold text-sky-200 hover:bg-slate-800"
>
<a href="#services">See Services</a>
</Button>
</div>
</div>
<div className="flex w-full max-w-md flex-col gap-4 rounded-3xl bg-slate-800 p-5 shadow-lg shadow-slate-950/40 md:ml-auto">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-300">
Why clients choose us
</p>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<div className="text-3xl font-bold text-white">15Y+</div>
<div className="mt-1 text-slate-300">Experience in tax &amp; CPA services</div>
</div>
<div>
<div className="text-3xl font-bold text-white">150+</div>
<div className="mt-1 text-slate-300">Businesses &amp; families supported</div>
</div>
</div>
<p className="text-xs text-slate-400">
Serving clients across Virginia and Maryland with inperson and
virtual appointments.
</p>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,77 @@
import { offices } from "@/lib/site-config";
export function LocationsSection() {
return (
<section
id="locations"
className="bg-white py-10 sm:py-12 md:py-16 border-t border-slate-200"
>
<div className="mx-auto max-w-6xl px-4 md:px-6">
<div className="mb-6 grid gap-6 md:grid-cols-[minmax(0,2fr),minmax(0,3fr)] md:items-start">
<div className="max-w-2xl">
<h2 className="text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl">
Visit one of our offices
</h2>
<p className="mt-3 text-sm text-slate-600 sm:text-base">
Convenient locations in Virginia and Maryland, with options for
virtual appointments if you prefer to meet online.
</p>
</div>
<div className="hidden overflow-hidden rounded-3xl border border-slate-200 bg-slate-100 shadow-sm md:block">
<iframe
title="Sample map showing Ghion Financial locations"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d6209.810247424868!2d-77.135!3d38.848!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x0!2zMzjCsDUwJzUxLjAiTiA3N8KwMDgnMDYuMCJX!5e0!3m2!1sen!2sus!4v1700000000000"
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
className="h-64 w-full border-0"
/>
</div>
</div>
<div className="grid gap-4 md:grid-cols-2">
{offices.map((office) => (
<article
key={office.id}
className="rounded-2xl border border-slate-200 bg-slate-50/70 p-5 shadow-sm"
>
<h3 className="text-lg font-semibold text-slate-900">
{office.label}
</h3>
<div className="mt-2 text-sm text-slate-700">
{office.addressLines.map((line) => (
<div key={line}>{line}</div>
))}
</div>
<div className="mt-2 text-sm">
<a
href={office.phoneHref}
className="font-medium text-slate-900 hover:text-sky-700"
>
{office.phone}
</a>
</div>
<div className="mt-1 text-sm">
<a
href={office.emailHref}
className="text-slate-700 hover:text-sky-700"
>
{office.email}
</a>
</div>
<div className="mt-3 text-xs">
<a
href={office.mapUrl}
target="_blank"
rel="noreferrer"
className="font-medium text-sky-700 hover:underline"
>
View on map
</a>
</div>
</article>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,69 @@
import { reviews, googleReviewsUrl } from "@/lib/site-config";
function Stars({ count }: { count: number }) {
return (
<div aria-label={`${count} out of 5 stars`} className="flex gap-0.5">
{Array.from({ length: 5 }).map((_, index) => (
<span
key={index}
className={
index < count ? "text-amber-400" : "text-slate-300"
}
>
</span>
))}
</div>
);
}
export function ReviewsSection() {
return (
<section
id="reviews"
className="bg-slate-50 py-10 sm:py-12 md:py-16 border-t border-slate-200"
>
<div className="mx-auto max-w-6xl px-4 md:px-6">
<div className="mb-6 flex flex-col gap-3 md:flex-row md:items-end md:justify-between">
<div>
<h2 className="text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl">
Trusted by clients across the DMV
</h2>
<p className="mt-3 max-w-xl text-sm text-slate-600 sm:text-base">
Clients choose Ghion Financial for our responsiveness,
attention to detail, and clear explanations at every step of
the process.
</p>
</div>
<a
href={googleReviewsUrl}
target="_blank"
rel="noreferrer"
className="text-xs font-semibold text-sky-700 hover:underline"
>
Read more reviews on Google
</a>
</div>
<div className="grid gap-4 md:grid-cols-3">
{reviews.map((review) => (
<figure
key={review.id}
className="flex h-full flex-col justify-between rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"
>
<div className="space-y-2">
<Stars count={review.rating} />
<blockquote className="text-sm text-slate-700">
{review.quote}
</blockquote>
</div>
<figcaption className="mt-4 text-sm font-semibold text-slate-900">
{review.name}
</figcaption>
</figure>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,71 @@
export function ServicesSection() {
const services = [
{
id: "tax",
title: "Income Tax Preparation",
description:
"Accurate, timely filing for individuals, families, and small businesses with a focus on maximizing your refund and staying compliant.",
bullets: ["Personal & business returns", "Multistate and amended returns"],
},
{
id: "insurance",
title: "Insurance Services",
description:
"Guidance on selecting health, life, and other insurance coverage that fits your needs and budget.",
bullets: ["Health & ObamaCare guidance", "Coverage tailored to your situation"],
},
{
id: "bookkeeping",
title: "Bookkeeping Services",
description:
"Reliable bookkeeping that keeps your records organized and ready when tax time comes.",
bullets: ["Monthly reports", "Small business friendly"],
},
];
return (
<section
id="services"
className="bg-white py-10 sm:py-12 md:py-16 border-b border-slate-200"
>
<div className="mx-auto max-w-6xl px-4 md:px-6">
<div className="mb-6 max-w-2xl">
<h2 className="text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl">
Services tailored to your financial life
</h2>
<p className="mt-3 text-sm text-slate-600 sm:text-base">
Whether you are filing as an individual, supporting your family, or
running a business, Ghion Financial provides services designed to
meet you where you are.
</p>
</div>
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
{services.map((service) => (
<article
key={service.id}
className="flex flex-col justify-between rounded-2xl border border-slate-200 bg-slate-50/60 p-5 shadow-sm"
>
<div>
<h3 className="text-base font-semibold text-slate-900 sm:text-lg">
{service.title}
</h3>
<p className="mt-2 text-sm text-slate-600">
{service.description}
</p>
<ul className="mt-3 space-y-1.5 text-sm text-slate-700">
{service.bullets.map((item) => (
<li key={item}> {item}</li>
))}
</ul>
</div>
<div className="mt-4 text-sm font-semibold text-sky-800">
Book an appointment to get started
</div>
</article>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,89 @@
import Link from "next/link";
import { offices } from "@/lib/site-config";
export function SiteFooter() {
return (
<footer className="mt-8 bg-slate-900 text-slate-50">
<div className="mx-auto max-w-6xl px-4 pb-8 pt-10 md:px-6 md:pb-10">
<div className="rounded-3xl border border-slate-700 bg-slate-900 px-6 py-8 shadow-[0_18px_0_0_#fbbf24] md:px-10 md:py-10">
<div className="grid gap-8 md:grid-cols-[2fr,1fr,1fr]">
<div>
<div className="flex items-center gap-2">
<span className="flex h-8 w-8 items-center justify-center rounded-full border border-sky-400 bg-sky-100 text-xs font-semibold text-sky-900">
GF
</span>
<span className="text-sm font-semibold tracking-[0.14em] uppercase">
Ghion Financial
</span>
</div>
<p className="mt-3 max-w-sm text-sm text-slate-200">
Empowering individuals, families, and businesses with tax,
insurance, and bookkeeping services that make financial
decisions clearer and less stressful.
</p>
</div>
<div className="space-y-3 text-sm">
<div className="font-semibold text-slate-50">Site map</div>
<div className="flex flex-col gap-1 text-slate-200">
<Link href="/" className="underline-offset-2 hover:underline">
Home
</Link>
<a
href="#services"
className="underline-offset-2 hover:underline"
>
Services
</a>
<a
href="#reviews"
className="underline-offset-2 hover:underline"
>
Reviews
</a>
<a
href="#locations"
className="underline-offset-2 hover:underline"
>
Locations
</a>
<Link
href="/contact"
className="underline-offset-2 hover:underline"
>
Contact Us
</Link>
</div>
</div>
<div className="space-y-3 text-sm">
<div className="font-semibold text-slate-50">Offices</div>
{offices.map((office) => (
<div key={office.id} className="space-y-1 text-slate-200">
<div>{office.label}</div>
<div className="text-xs text-slate-300">
{office.addressLines.map((line) => (
<div key={line}>{line}</div>
))}
</div>
</div>
))}
</div>
</div>
<div className="mt-8 flex flex-col items-start justify-between gap-3 border-t border-slate-700 pt-4 text-xs text-slate-400 md:flex-row md:items-center">
<p>
© {new Date().getFullYear()} Ghion Financial. All rights reserved.
</p>
<div className="flex gap-4">
<Link
href="/privacy"
className="underline-offset-2 hover:underline"
>
Privacy Policy
</Link>
</div>
</div>
</div>
</div>
</footer>
);
}

View File

@ -0,0 +1,111 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import { offices } from "@/lib/site-config";
import { Button } from "@/components/ui/button";
export function SiteHeader() {
const vaOffice = offices.find((o) => o.id === "va");
const [open, setOpen] = useState(false);
return (
<header className="sticky top-0 z-40 border-b border-slate-200 bg-slate-50/90 backdrop-blur">
<div className="mx-auto flex max-w-6xl items-center justify-between rounded-b-3xl bg-white px-4 py-3 shadow-sm md:px-6">
<Link href="/" className="flex items-center gap-2">
<span className="flex h-8 w-8 items-center justify-center rounded-full border border-sky-400 bg-sky-100 text-xs font-semibold text-sky-800">
GF
</span>
</Link>
<nav className="hidden items-center gap-6 text-xs font-medium text-slate-600 md:flex">
<Link href="/" className="relative pb-1 hover:text-slate-900">
<span>Home</span>
</Link>
<Link
href="#services"
className="relative pb-1 hover:text-slate-900"
>
Services
</Link>
<Link
href="#reviews"
className="relative pb-1 hover:text-slate-900"
>
Reviews
</Link>
<Link
href="#locations"
className="relative pb-1 hover:text-slate-900"
>
Locations
</Link>
<Link
href="/contact"
className="relative pb-1 hover:text-slate-900"
>
Contact
</Link>
</nav>
<div className="flex items-center gap-3">
{vaOffice ? (
<a
href={vaOffice.phoneHref}
className="hidden text-[0.7rem] font-medium text-slate-600 md:inline"
>
Call {vaOffice.phone}
</a>
) : null}
<div className="hidden md:block">
<Button
variant="outline"
size="sm"
className="h-9 rounded-full border-sky-400 bg-sky-100 px-4 text-[0.7rem] font-semibold text-slate-900 hover:bg-sky-200"
>
<Link href="/appointment">Book Appointment</Link>
</Button>
</div>
<button
type="button"
className="flex h-9 w-9 items-center justify-center rounded-full border border-slate-300 bg-white text-slate-700 hover:bg-slate-100 md:hidden"
aria-label="Toggle navigation"
onClick={() => setOpen((prev) => !prev)}
>
<span className="block h-0.5 w-4 rounded bg-slate-700" />
<span className="block h-0.5 w-4 rounded bg-slate-700" />
</button>
</div>
</div>
{open && (
<div className="border-t border-slate-200 bg-white md:hidden">
<div className="mx-auto max-w-6xl px-4 py-3 text-sm text-slate-800">
<nav className="flex flex-col gap-3">
<Link href="/" onClick={() => setOpen(false)}>
Home
</Link>
<a href="#services" onClick={() => setOpen(false)}>
Services
</a>
<a href="#reviews" onClick={() => setOpen(false)}>
Reviews
</a>
<a href="#locations" onClick={() => setOpen(false)}>
Locations
</a>
<Link href="/contact" onClick={() => setOpen(false)}>
Contact
</Link>
<Link
href="/appointment"
onClick={() => setOpen(false)}
className="mt-2 inline-flex h-10 items-center justify-center rounded-full border border-sky-400 bg-sky-100 px-4 text-xs font-semibold text-slate-900"
>
Book Appointment
</Link>
</nav>
</div>
</div>
)}
</header>
);
}

75
src/lib/site-config.ts Normal file
View File

@ -0,0 +1,75 @@
export type OfficeKey = "va" | "md";
export type Office = {
id: OfficeKey;
name: string;
label: string;
addressLines: string[];
phone: string;
phoneHref: string;
email: string;
emailHref: string;
mapUrl: string;
};
export type Review = {
id: string;
name: string;
rating: number;
quote: string;
};
export const offices: Office[] = [
{
id: "va",
name: "Virginia office",
label: "Virginia Office",
addressLines: ["3819-D S. George Mason Dr.", "Falls Church, VA 22041"],
phone: "703.931.1050",
phoneHref: "tel:17039311050",
email: "info@ghionfinancial.com",
emailHref: "mailto:info@ghionfinancial.com",
mapUrl:
"https://www.google.com/maps/search/?api=1&query=3819-D+S+George+Mason+Dr+Falls+Church+VA+22041",
},
{
id: "md",
name: "Maryland office",
label: "Maryland Office",
addressLines: ["911 Silver Spring Ave # 100", "Silver Spring MD 20910"],
phone: "240.393.4852",
phoneHref: "tel:12403934852",
email: "md@ghionfinancial.com",
emailHref: "mailto:md@ghionfinancial.com",
mapUrl:
"https://www.google.com/maps/search/?api=1&query=911+Silver+Spring+Ave+%23100+Silver+Spring+MD+20910",
},
];
export const reviews: Review[] = [
{
id: "review-1",
name: "Sara T.",
rating: 5,
quote:
"Ghion Financial made tax season simple and stressfree. They explained every step clearly.",
},
{
id: "review-2",
name: "Michael D.",
rating: 5,
quote:
"Professional, responsive, and detailoriented. I trust them with both my personal and business taxes.",
},
{
id: "review-3",
name: "Liya G.",
rating: 5,
quote:
"Friendly team and excellent service. They helped me find deductions I would have missed on my own.",
},
];
export const googleReviewsUrl =
"https://www.google.com/search?q=Ghion+Financial+reviews";