feat(02-01): scaffold marketing site nav, hero, listings, footer

- Add scroll-behavior: smooth and scroll-margin-top to globals.css
- Create SiteNav with sticky navy bar, desktop links, mobile hamburger
- Create HeroSection with split-panel layout and next/image
- Create ListingsPlaceholder with brand navy background and gold CTA
- Create SiteFooter with license number and TODO verify comment
- Update page.tsx to compose all section components
- Install lucide-react for icons

[Rule 1 - Bug] Fixed event handlers in server components — used CSS hover classes instead
This commit is contained in:
Chandler Copeland
2026-03-19 14:59:56 -06:00
parent 39f233dbb4
commit c26a0b1b62
6 changed files with 430 additions and 7 deletions

View File

@@ -0,0 +1,111 @@
import Image from 'next/image';
export default function HeroSection() {
return (
<section id="hero" className="hero-section">
{/* Left half — photo */}
<div className="hero-photo">
<Image
src="/red.jpg"
alt="Teressa Copeland"
fill
style={{ objectFit: 'cover', objectPosition: 'center top' }}
priority
/>
</div>
{/* Right half — copy */}
<div className="hero-copy">
{/* PLACEHOLDER HEADLINE — replace before launch */}
<h1 className="hero-headline">
Your Trusted Utah Real Estate Partner
</h1>
{/* PLACEHOLDER SUBHEADING — replace before launch */}
<p className="hero-subheading">
Helping Utah families find the home they deserve
</p>
{/* PLACEHOLDER BIO — replace before launch */}
<p className="hero-bio">
With years of experience navigating the Utah real estate market,
Teressa Copeland brings deep local knowledge and a genuine commitment
to every client she serves. Whether you&apos;re buying your first home
in the Salt Lake Valley or selling a property along the Wasatch Front,
Teressa guides you through every step with patience, transparency, and
skill. Her clients don&apos;t just find houses they find the right
home.
</p>
<a href="#contact" className="hero-cta">
Get in Touch
</a>
</div>
<style>{`
.hero-section {
display: flex;
min-height: 600px;
background-color: #FAF9F7;
}
.hero-photo {
flex: 1;
position: relative;
min-height: 500px;
}
.hero-copy {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
padding: 4rem 3rem;
background-color: #FAF9F7;
}
.hero-headline {
font-size: clamp(1.75rem, 3vw, 2.5rem);
font-weight: 700;
color: #1B2B4B;
line-height: 1.2;
margin-bottom: 1rem;
}
.hero-subheading {
font-size: 1.125rem;
color: #1B2B4B;
opacity: 0.75;
margin-bottom: 1.5rem;
font-style: italic;
}
.hero-bio {
font-size: 1rem;
color: #1B2B4B;
line-height: 1.75;
margin-bottom: 2rem;
max-width: 480px;
}
.hero-cta {
display: inline-block;
background-color: #C9A84C;
color: #1B2B4B;
font-weight: 600;
padding: 0.75rem 2rem;
border-radius: 4px;
text-decoration: none;
font-size: 1rem;
align-self: flex-start;
transition: opacity 0.2s;
}
.hero-cta:hover {
opacity: 0.85;
}
@media (max-width: 767px) {
.hero-section {
flex-direction: column;
}
.hero-photo {
min-height: 300px;
}
}
`}</style>
</section>
);
}

View File

@@ -0,0 +1,68 @@
import { Home } from 'lucide-react';
export default function ListingsPlaceholder() {
return (
<section id="listings" className="listings-section">
<div className="listings-inner">
<Home size={48} color="#C9A84C" />
<h2 className="listings-heading">Listings Coming Soon</h2>
<p className="listings-desc">
Teressa is actively curating listings in your area. Check back soon or
reach out directly.
</p>
<a href="#contact" className="listings-cta">
Contact Teressa
</a>
</div>
<style>{`
.listings-section {
background-color: #1B2B4B;
color: #FAF9F7;
padding: 5rem 2rem;
text-align: center;
}
.listings-inner {
max-width: 600px;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: 1.25rem;
}
.listings-heading {
font-size: clamp(1.5rem, 3vw, 2rem);
font-weight: 700;
color: #FAF9F7;
margin: 0;
}
.listings-desc {
font-size: 1rem;
color: #FAF9F7;
opacity: 0.8;
line-height: 1.7;
margin: 0;
}
.listings-cta {
display: inline-block;
border: 2px solid #C9A84C;
color: #C9A84C;
font-weight: 600;
padding: 0.6rem 1.75rem;
border-radius: 4px;
text-decoration: none;
font-size: 0.95rem;
margin-top: 0.5rem;
transition: background-color 0.2s, color 0.2s;
}
.listings-cta:hover {
background-color: #C9A84C;
color: #1B2B4B;
}
`}</style>
</section>
);
}

View File

@@ -0,0 +1,74 @@
const navLinks = [
{ label: 'Home', href: '#hero' },
{ label: 'About', href: '#about' },
{ label: 'Listings', href: '#listings' },
{ label: 'Contact', href: '#contact' },
];
export default function SiteFooter() {
return (
<footer
style={{
backgroundColor: '#1B2B4B',
color: '#FAF9F7',
padding: '2rem 1.5rem',
}}
>
<div
style={{
maxWidth: '1200px',
margin: '0 auto',
display: 'flex',
flexDirection: 'column',
gap: '0.75rem',
alignItems: 'center',
textAlign: 'center',
}}
>
{/* Row 1: Name / Wordmark */}
<p style={{ fontWeight: 'bold', fontSize: '1rem', margin: 0 }}>
Teressa Copeland Homes
</p>
{/* Row 2: License number */}
<p style={{ fontSize: '0.875rem', opacity: 0.75, margin: 0 }}>
{/* TODO: Verify license number before launch — last two chars may be "00" (zeros) or "OO" (letters O) */}
Utah Real Estate License: 14196185-SA00
</p>
{/* Row 3: Copyright */}
<p style={{ fontSize: '0.8rem', opacity: 0.6, margin: 0 }}>
&copy; {new Date().getFullYear()} Teressa Copeland Homes. All rights
reserved.
</p>
{/* Row 4: Nav links */}
<nav
style={{
display: 'flex',
gap: '1.5rem',
flexWrap: 'wrap',
justifyContent: 'center',
marginTop: '0.5rem',
}}
>
{navLinks.map((link) => (
<a
key={link.href}
href={link.href}
style={{
color: '#FAF9F7',
textDecoration: 'none',
fontSize: '0.875rem',
opacity: 0.75,
transition: 'opacity 0.2s',
}}
>
{link.label}
</a>
))}
</nav>
</div>
</footer>
);
}

View File

@@ -0,0 +1,153 @@
'use client';
import { useState } from 'react';
import { Menu, X } from 'lucide-react';
const navLinks = [
{ label: 'Home', href: '#hero' },
{ label: 'About', href: '#about' },
{ label: 'Listings', href: '#listings' },
{ label: 'Contact', href: '#contact' },
];
export default function SiteNav() {
const [isOpen, setIsOpen] = useState(false);
function handleMobileLinkClick() {
setIsOpen(false);
}
return (
<nav
style={{
position: 'sticky',
top: 0,
zIndex: 50,
backgroundColor: '#1B2B4B',
color: '#FAF9F7',
}}
>
<div
style={{
maxWidth: '1200px',
margin: '0 auto',
padding: '0 1.5rem',
height: '72px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
{/* Wordmark */}
<a
href="#hero"
style={{
fontWeight: 'bold',
fontSize: '1.125rem',
color: '#FAF9F7',
textDecoration: 'none',
letterSpacing: '0.01em',
}}
>
Teressa Copeland
</a>
{/* Desktop nav links — hidden on mobile */}
<ul
style={{
display: 'flex',
gap: '2rem',
listStyle: 'none',
margin: 0,
padding: 0,
}}
className="hidden-mobile-nav"
>
{navLinks.map((link) => (
<li key={link.href}>
<a
href={link.href}
style={{
color: '#FAF9F7',
textDecoration: 'none',
fontSize: '0.95rem',
opacity: 0.9,
transition: 'opacity 0.2s',
}}
onMouseEnter={(e) =>
((e.currentTarget as HTMLAnchorElement).style.opacity = '1')
}
onMouseLeave={(e) =>
((e.currentTarget as HTMLAnchorElement).style.opacity = '0.9')
}
>
{link.label}
</a>
</li>
))}
</ul>
{/* Mobile hamburger button */}
<button
onClick={() => setIsOpen((prev) => !prev)}
aria-label={isOpen ? 'Close menu' : 'Open menu'}
style={{
background: 'none',
border: 'none',
color: '#FAF9F7',
cursor: 'pointer',
padding: '0.25rem',
display: 'none',
}}
className="mobile-menu-btn"
>
{isOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
{/* Mobile dropdown drawer */}
{isOpen && (
<div
style={{
backgroundColor: '#1B2B4B',
borderTop: '1px solid rgba(250,249,247,0.15)',
padding: '1rem 1.5rem',
}}
className="mobile-nav-drawer"
>
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
{navLinks.map((link) => (
<li key={link.href} style={{ marginBottom: '0.75rem' }}>
<a
href={link.href}
onClick={handleMobileLinkClick}
style={{
color: '#FAF9F7',
textDecoration: 'none',
fontSize: '1rem',
display: 'block',
padding: '0.25rem 0',
}}
>
{link.label}
</a>
</li>
))}
</ul>
</div>
)}
<style>{`
@media (min-width: 768px) {
.hidden-mobile-nav { display: flex !important; }
.mobile-menu-btn { display: none !important; }
.mobile-nav-drawer { display: none !important; }
}
@media (max-width: 767px) {
.hidden-mobile-nav { display: none !important; }
.mobile-menu-btn { display: block !important; }
}
`}</style>
</nav>
);
}

View File

@@ -24,3 +24,11 @@ body {
color: var(--foreground); color: var(--foreground);
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
} }
html {
scroll-behavior: smooth;
}
section[id] {
scroll-margin-top: 72px;
}

View File

@@ -1,11 +1,20 @@
import SiteNav from './_components/SiteNav';
import HeroSection from './_components/HeroSection';
import ListingsPlaceholder from './_components/ListingsPlaceholder';
import { ContactSection } from './_components/ContactSection';
import SiteFooter from './_components/SiteFooter';
export default function HomePage() { export default function HomePage() {
return ( return (
<main className="min-h-screen flex items-center justify-center"> <>
<div className="text-center"> <SiteNav />
<h1 className="text-4xl font-bold text-gray-900">Teressa Copeland Homes</h1> <main>
<p className="mt-4 text-gray-600">Real estate expertise for Utah home buyers and sellers.</p> <HeroSection />
<p className="mt-2 text-sm text-gray-400">Marketing site coming in Phase 2.</p> {/* TestimonialsSection — added in Task 2 */}
</div> <ListingsPlaceholder />
</main> <ContactSection />
</main>
<SiteFooter />
</>
); );
} }