Real Estate — Brokerage Landing
An editorial marketing landing page for the fictional Harbor & Vale brokerage, pairing a serif hero and listing-search bar with an animated stats strip, a swipeable featured-listings carousel, a why-us feature trio, an agent spotlight grid, client testimonials, and a valuation CTA. Vanilla JS drives the carousel, save-to-shortlist toggles, count-up stat counters, and toast feedback for searches, contact, and CTA submissions across a fully responsive layout.
MCP
Codice
:root {
--ivory: #f7f4ec;
--paper: #fffdf8;
--white: #ffffff;
--green: #1f3d34;
--green-d: #16302a;
--green-700: #26493e;
--green-50: #e8efea;
--brass: #b08d57;
--brass-d: #94733f;
--brass-50: #f3ead9;
--ink: #1c2a25;
--ink-2: #33433d;
--muted: #6b7a72;
--line: rgba(31, 61, 52, 0.12);
--line-2: rgba(31, 61, 52, 0.22);
--ok: #2f9e6f;
--warn: #c98a2b;
--danger: #c4503e;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--shadow-sm: 0 1px 2px rgba(28, 42, 37, 0.05), 0 2px 8px rgba(28, 42, 37, 0.05);
--shadow-md: 0 8px 24px rgba(28, 42, 37, 0.09), 0 2px 6px rgba(28, 42, 37, 0.06);
--shadow-lg: 0 24px 60px rgba(22, 48, 42, 0.18), 0 8px 22px rgba(22, 48, 42, 0.1);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: var(--ivory);
color: var(--ink);
font-family: "Inter", system-ui, sans-serif;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1,
h2,
h3 {
font-family: "Cormorant Garamond", Georgia, serif;
margin: 0;
line-height: 1.05;
letter-spacing: 0.2px;
}
p {
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
img {
max-width: 100%;
display: block;
}
.wrap {
width: 100%;
max-width: 1180px;
margin: 0 auto;
padding: 0 28px;
}
.display {
font-weight: 600;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
.skip-link {
position: absolute;
left: -999px;
top: 8px;
background: var(--green);
color: var(--paper);
padding: 10px 16px;
border-radius: var(--r-sm);
z-index: 100;
}
.skip-link:focus {
left: 12px;
}
:focus-visible {
outline: 2px solid var(--brass);
outline-offset: 2px;
}
/* ===================== BUTTONS / CHIPS ===================== */
.btn {
font-family: inherit;
font-size: 0.95rem;
font-weight: 600;
border: 1px solid transparent;
border-radius: 999px;
padding: 12px 22px;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease,
border-color 0.18s ease, color 0.18s ease;
white-space: nowrap;
}
.btn:active {
transform: translateY(1px);
}
.btn-sm {
padding: 9px 18px;
font-size: 0.88rem;
}
.btn-brass {
background: var(--brass);
color: #1a1305;
box-shadow: 0 6px 16px rgba(176, 141, 87, 0.32);
}
.btn-brass:hover {
background: var(--brass-d);
color: #fff;
box-shadow: 0 10px 22px rgba(148, 115, 63, 0.4);
transform: translateY(-1px);
}
.btn-ghost {
background: transparent;
border-color: var(--line-2);
color: var(--green);
}
.btn-ghost:hover {
background: var(--green);
color: var(--paper);
border-color: var(--green);
}
.chip {
font: inherit;
font-size: 0.82rem;
font-weight: 500;
background: var(--white);
border: 1px solid var(--line);
color: var(--ink-2);
border-radius: 999px;
padding: 6px 13px;
cursor: pointer;
transition: border-color 0.16s, color 0.16s, background 0.16s;
}
.chip:hover {
border-color: var(--brass);
color: var(--brass-d);
background: var(--brass-50);
}
/* ===================== HEADER ===================== */
.site-header {
position: sticky;
top: 0;
z-index: 40;
background: rgba(247, 244, 236, 0.82);
backdrop-filter: saturate(160%) blur(12px);
border-bottom: 1px solid var(--line);
}
.header-inner {
display: flex;
align-items: center;
gap: 24px;
height: 70px;
}
.brand {
display: flex;
align-items: center;
gap: 11px;
font-weight: 700;
}
.brand-mark {
display: grid;
place-items: center;
width: 38px;
height: 38px;
border-radius: 10px;
background: var(--green);
color: var(--brass-50);
font-family: "Cormorant Garamond", serif;
font-weight: 700;
font-size: 0.92rem;
letter-spacing: 0.5px;
box-shadow: inset 0 0 0 1px rgba(176, 141, 87, 0.4);
}
.brand-name {
font-family: "Cormorant Garamond", serif;
font-size: 1.32rem;
font-weight: 600;
color: var(--green-d);
}
.site-nav {
display: flex;
gap: 26px;
margin-left: auto;
font-size: 0.92rem;
font-weight: 500;
color: var(--ink-2);
}
.site-nav a {
position: relative;
padding: 4px 0;
transition: color 0.16s;
}
.site-nav a::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
height: 1.5px;
width: 0;
background: var(--brass);
transition: width 0.22s ease;
}
.site-nav a:hover {
color: var(--green);
}
.site-nav a:hover::after {
width: 100%;
}
.header-inner .btn-ghost {
margin-left: 4px;
}
/* ===================== PHOTO BASE ===================== */
.photo {
position: relative;
border-radius: var(--r-md);
overflow: hidden;
background: var(--green-700);
isolation: isolate;
}
.photo::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 45%, rgba(20, 30, 26, 0.42) 100%);
z-index: 1;
}
.photo-tag {
position: absolute;
left: 12px;
bottom: 12px;
z-index: 2;
font-size: 0.74rem;
font-weight: 600;
letter-spacing: 0.3px;
color: #fff;
background: rgba(22, 35, 30, 0.55);
border: 1px solid rgba(255, 255, 255, 0.18);
padding: 4px 10px;
border-radius: 999px;
backdrop-filter: blur(4px);
}
/* simulated property photography */
.photo--hero {
aspect-ratio: 4 / 5;
background:
radial-gradient(120% 80% at 78% 8%, rgba(255, 233, 196, 0.85), transparent 55%),
radial-gradient(90% 60% at 10% 92%, rgba(35, 70, 60, 0.7), transparent 60%),
linear-gradient(160deg, #d8a86b 0%, #b07a47 28%, #6f5237 52%, #2e4a3f 78%, #1c3a31 100%);
}
.photo--hero::before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(105deg, transparent 38%, rgba(255, 246, 222, 0.16) 40%, transparent 43%),
repeating-linear-gradient(92deg, rgba(20, 30, 25, 0.12) 0 2px, transparent 2px 64px),
repeating-linear-gradient(0deg, rgba(255, 245, 220, 0.05) 0 1px, transparent 1px 40px);
z-index: 1;
}
.photo--l1 {
background:
radial-gradient(100% 70% at 80% 10%, rgba(190, 224, 240, 0.7), transparent 60%),
linear-gradient(155deg, #9fc4d6 0%, #6f99ad 40%, #3f6a72 70%, #274f51 100%);
}
.photo--l2 {
background:
radial-gradient(110% 75% at 75% 6%, rgba(255, 236, 198, 0.8), transparent 55%),
linear-gradient(155deg, #e0b878 0%, #b98e54 38%, #7a6342 68%, #2f4a3f 100%);
}
.photo--l3 {
background:
radial-gradient(100% 70% at 20% 12%, rgba(247, 224, 210, 0.7), transparent 60%),
linear-gradient(150deg, #cf9f86 0%, #a87a63 42%, #6f5747 72%, #3a4239 100%);
}
.photo--l4 {
background:
radial-gradient(100% 65% at 82% 14%, rgba(214, 233, 205, 0.7), transparent 58%),
linear-gradient(155deg, #a8c486 0%, #7fa066 42%, #4f7253 72%, #284b3c 100%);
}
.photo--l5 {
background:
radial-gradient(120% 80% at 70% 4%, rgba(206, 228, 240, 0.75), transparent 55%),
linear-gradient(160deg, #b9d2dd 0%, #7e9aa6 36%, #4b6c70 66%, #233f3a 100%);
}
.photo--l6 {
background:
radial-gradient(100% 70% at 16% 10%, rgba(255, 228, 196, 0.72), transparent 58%),
linear-gradient(150deg, #d8b27e 0%, #a9814f 44%, #6f5a3c 74%, #2c4239 100%);
}
.listing .photo {
aspect-ratio: 4 / 3;
}
.listing .photo::before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(100deg, transparent 46%, rgba(255, 250, 235, 0.14) 49%, transparent 52%),
repeating-linear-gradient(90deg, rgba(15, 25, 22, 0.07) 0 1px, transparent 1px 54px);
z-index: 1;
}
/* agent portraits */
.agent .photo {
aspect-ratio: 1 / 1;
}
.photo--a1 {
background:
radial-gradient(60% 50% at 50% 36%, rgba(255, 236, 210, 0.9), transparent 60%),
linear-gradient(160deg, #c9a37a 0%, #7c6347 50%, #2c463c 100%);
}
.photo--a2 {
background:
radial-gradient(60% 50% at 50% 36%, rgba(228, 210, 240, 0.85), transparent 60%),
linear-gradient(160deg, #8d7fa0 0%, #5a5170 50%, #233f3a 100%);
}
.photo--a3 {
background:
radial-gradient(60% 50% at 50% 36%, rgba(255, 224, 222, 0.85), transparent 60%),
linear-gradient(160deg, #cf8f87 0%, #7d5a55 50%, #2c463c 100%);
}
.photo--a4 {
background:
radial-gradient(60% 50% at 50% 36%, rgba(214, 233, 205, 0.85), transparent 60%),
linear-gradient(160deg, #8aa478 0%, #4f6c50 50%, #233f3a 100%);
}
.agent .photo::before {
content: "";
position: absolute;
left: 50%;
bottom: -8%;
transform: translateX(-50%);
width: 56%;
height: 60%;
border-radius: 50% 50% 12% 12% / 60% 60% 12% 12%;
background: rgba(22, 35, 30, 0.28);
z-index: 1;
}
/* ===================== HERO ===================== */
.hero {
padding: 56px 0 64px;
background:
radial-gradient(80% 60% at 90% -10%, var(--brass-50), transparent 60%),
linear-gradient(180deg, var(--ivory), var(--paper));
}
.hero-grid {
display: grid;
grid-template-columns: 1.05fr 0.95fr;
gap: 56px;
align-items: center;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--brass-d);
margin-bottom: 18px;
}
.eyebrow .dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--brass);
box-shadow: 0 0 0 4px rgba(176, 141, 87, 0.18);
}
.eyebrow--ink {
color: var(--muted);
}
.eyebrow--brass {
color: var(--brass);
}
h1.display {
font-size: clamp(2.7rem, 5.4vw, 4.4rem);
color: var(--green-d);
margin-bottom: 18px;
}
.lede {
font-size: 1.08rem;
color: var(--ink-2);
max-width: 46ch;
margin-bottom: 28px;
}
.search {
display: flex;
align-items: flex-end;
gap: 6px;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 10px;
box-shadow: var(--shadow-md);
max-width: 560px;
}
.search-field {
display: flex;
flex-direction: column;
padding: 6px 12px;
flex: 1 1 auto;
min-width: 0;
border-right: 1px solid var(--line);
}
.search-field--select {
flex: 0 0 auto;
}
.search-field label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 2px;
}
.search-field input,
.search-field select {
border: 0;
background: transparent;
font: inherit;
font-size: 0.95rem;
font-weight: 500;
color: var(--ink);
padding: 2px 0;
width: 100%;
outline: none;
}
.search-field select {
cursor: pointer;
}
.search .btn {
flex: 0 0 auto;
margin-left: 4px;
}
.search-hint {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-top: 18px;
font-size: 0.85rem;
color: var(--muted);
}
.hero-photo {
box-shadow: var(--shadow-lg);
}
.hero-float {
position: absolute;
left: 16px;
bottom: 16px;
right: 16px;
z-index: 2;
display: flex;
flex-direction: column;
gap: 2px;
background: rgba(255, 253, 248, 0.93);
border: 1px solid rgba(176, 141, 87, 0.3);
border-radius: var(--r-md);
padding: 14px 16px;
box-shadow: var(--shadow-md);
backdrop-filter: blur(6px);
}
.hf-label {
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--brass-d);
}
.hf-title {
font-family: "Cormorant Garamond", serif;
font-size: 1.28rem;
font-weight: 600;
color: var(--green-d);
}
.hf-meta {
font-size: 0.82rem;
color: var(--muted);
}
/* ===================== STATS ===================== */
.stats {
background: var(--green);
color: var(--paper);
padding: 0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
padding: 34px 28px;
}
.stat {
text-align: center;
padding: 4px 16px;
border-left: 1px solid rgba(255, 255, 255, 0.1);
}
.stat:first-child {
border-left: 0;
}
.stat-num {
display: block;
font-family: "Cormorant Garamond", serif;
font-size: clamp(2rem, 3.6vw, 2.9rem);
font-weight: 600;
color: #fff;
line-height: 1;
}
.stat-label {
font-size: 0.82rem;
letter-spacing: 0.04em;
color: rgba(232, 239, 234, 0.74);
margin-top: 8px;
display: block;
}
/* ===================== SECTIONS ===================== */
.section {
padding: 72px 0;
}
.section--paper {
background: var(--paper);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
.section-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
margin-bottom: 36px;
}
.section-head--center {
justify-content: center;
text-align: center;
margin-bottom: 44px;
}
.section-title {
font-size: clamp(2rem, 3.6vw, 2.8rem);
color: var(--green-d);
}
.section-title--light {
color: var(--paper);
}
/* carousel */
.carousel-ctrl {
display: flex;
gap: 10px;
}
.round-btn {
width: 44px;
height: 44px;
border-radius: 50%;
border: 1px solid var(--line-2);
background: var(--white);
color: var(--green);
font-size: 1.1rem;
cursor: pointer;
transition: background 0.16s, color 0.16s, border-color 0.16s, transform 0.16s;
}
.round-btn:hover {
background: var(--green);
color: var(--paper);
border-color: var(--green);
}
.round-btn:active {
transform: scale(0.94);
}
.round-btn[disabled] {
opacity: 0.4;
cursor: not-allowed;
}
.carousel {
overflow: hidden;
padding: 6px 0;
}
.track {
display: flex;
gap: 24px;
list-style: none;
margin: 0;
padding: 0 28px;
max-width: 1180px;
margin-inline: auto;
transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
will-change: transform;
}
.card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
}
.listing {
flex: 0 0 calc((100% - 48px) / 3);
min-width: 0;
}
.card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-md);
border-color: var(--line-2);
}
.badge {
position: absolute;
top: 12px;
left: 12px;
z-index: 2;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
padding: 5px 10px;
border-radius: 999px;
color: #fff;
}
.badge-status {
background: rgba(31, 61, 52, 0.92);
}
.badge-new {
background: var(--brass);
color: #1a1305;
}
.badge-pending {
background: var(--warn);
color: #2a1c05;
}
.fav {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
width: 36px;
height: 36px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(22, 35, 30, 0.45);
color: #fff;
font-size: 1.05rem;
line-height: 1;
cursor: pointer;
backdrop-filter: blur(4px);
transition: background 0.16s, transform 0.16s, color 0.16s;
}
.fav:hover {
background: rgba(22, 35, 30, 0.7);
transform: scale(1.08);
}
.fav.is-saved {
background: var(--brass);
color: #1a1305;
border-color: var(--brass);
}
.listing-body {
padding: 18px 20px 22px;
}
.listing-price {
font-family: "Cormorant Garamond", serif;
font-size: 1.6rem;
font-weight: 700;
color: var(--green-d);
line-height: 1;
}
.listing-title {
font-size: 1.32rem;
font-weight: 600;
color: var(--ink);
margin-top: 8px;
}
.listing-addr {
font-size: 0.88rem;
color: var(--muted);
margin-top: 4px;
}
.specs {
display: flex;
gap: 16px;
list-style: none;
margin: 14px 0 0;
padding: 14px 0 0;
border-top: 1px solid var(--line);
font-size: 0.86rem;
font-weight: 500;
color: var(--ink-2);
}
.specs li {
position: relative;
}
.specs li + li::before {
content: "";
position: absolute;
left: -8px;
top: 50%;
width: 3px;
height: 3px;
border-radius: 50%;
background: var(--brass);
transform: translateY(-50%);
}
.dots {
display: flex;
justify-content: center;
gap: 9px;
margin-top: 30px;
}
.dot-btn {
width: 9px;
height: 9px;
border-radius: 50%;
border: 0;
background: var(--line-2);
cursor: pointer;
padding: 0;
transition: width 0.22s, background 0.22s;
}
.dot-btn.is-active {
width: 26px;
border-radius: 999px;
background: var(--brass);
}
/* feature trio */
.feature-trio {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.feature {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 30px 28px;
box-shadow: var(--shadow-sm);
transition: transform 0.2s, box-shadow 0.2s;
}
.feature:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-md);
}
.feature-ic {
display: grid;
place-items: center;
width: 50px;
height: 50px;
border-radius: 14px;
background: var(--green-50);
color: var(--green);
font-size: 1.4rem;
margin-bottom: 18px;
border: 1px solid var(--line);
}
.feature h3 {
font-size: 1.5rem;
font-weight: 600;
color: var(--green-d);
margin-bottom: 8px;
}
.feature p {
font-size: 0.96rem;
color: var(--ink-2);
}
/* agents */
.agent-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}
.agent {
text-align: left;
}
.agent-body {
padding: 18px 20px 22px;
}
.agent-body h3 {
font-size: 1.4rem;
font-weight: 600;
color: var(--green-d);
}
.agent-role {
font-size: 0.86rem;
color: var(--brass-d);
font-weight: 600;
margin-top: 2px;
}
.agent-stat {
font-size: 0.84rem;
color: var(--muted);
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid var(--line);
}
/* testimonials */
.section--green {
background:
radial-gradient(70% 50% at 85% 0%, rgba(176, 141, 87, 0.16), transparent 60%),
var(--green);
}
.quotes {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.quote {
margin: 0;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: var(--r-lg);
padding: 28px 26px;
}
.quote blockquote {
margin: 0 0 18px;
font-family: "Cormorant Garamond", serif;
font-size: 1.32rem;
font-weight: 500;
line-height: 1.34;
color: var(--paper);
}
.quote figcaption {
display: flex;
flex-direction: column;
gap: 2px;
border-top: 1px solid rgba(176, 141, 87, 0.35);
padding-top: 14px;
}
.q-name {
font-weight: 600;
color: #fff;
font-size: 0.95rem;
}
.q-meta {
font-size: 0.82rem;
color: rgba(232, 239, 234, 0.7);
}
/* CTA */
.cta {
padding: 64px 0;
background:
radial-gradient(70% 90% at 10% 10%, var(--brass-50), transparent 60%),
var(--paper);
}
.cta-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 40px;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 44px 48px;
box-shadow: var(--shadow-md);
position: relative;
overflow: hidden;
}
.cta-inner::before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(180deg, var(--brass), var(--green));
}
.cta-title {
font-size: clamp(1.9rem, 3.4vw, 2.6rem);
color: var(--green-d);
margin-bottom: 8px;
}
.cta-copy p {
color: var(--ink-2);
max-width: 42ch;
}
.cta-form {
display: flex;
gap: 8px;
flex: 0 0 auto;
}
.cta-form input {
font: inherit;
font-size: 0.95rem;
padding: 12px 16px;
border: 1px solid var(--line-2);
border-radius: 999px;
background: var(--paper);
min-width: 240px;
outline: none;
transition: border-color 0.16s, box-shadow 0.16s;
}
.cta-form input:focus {
border-color: var(--brass);
box-shadow: 0 0 0 3px rgba(176, 141, 87, 0.18);
}
/* footer */
.site-footer {
background: var(--green-d);
color: rgba(232, 239, 234, 0.8);
padding: 48px 0 36px;
}
.footer-inner {
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: 24px 40px;
align-items: start;
}
.footer-brand {
display: flex;
gap: 14px;
align-items: flex-start;
font-size: 0.88rem;
line-height: 1.5;
}
.footer-brand .brand-mark {
flex: 0 0 auto;
}
.footer-nav {
display: flex;
gap: 24px;
justify-content: flex-end;
flex-wrap: wrap;
font-size: 0.92rem;
font-weight: 500;
}
.footer-nav a:hover {
color: var(--brass);
}
.footer-fine {
grid-column: 1 / -1;
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding-top: 20px;
margin-top: 10px;
font-size: 0.8rem;
color: rgba(232, 239, 234, 0.55);
}
/* toasts */
.toast-host {
position: fixed;
right: 20px;
bottom: 20px;
z-index: 80;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: none;
}
.toast {
display: flex;
align-items: center;
gap: 10px;
background: var(--green-d);
color: var(--paper);
border: 1px solid rgba(176, 141, 87, 0.45);
border-radius: var(--r-md);
padding: 13px 18px;
font-size: 0.9rem;
font-weight: 500;
box-shadow: var(--shadow-lg);
transform: translateY(16px);
opacity: 0;
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s;
max-width: 320px;
}
.toast.is-in {
transform: translateY(0);
opacity: 1;
}
.toast .t-ic {
color: var(--brass);
font-size: 1.05rem;
}
/* ===================== RESPONSIVE ===================== */
@media (max-width: 920px) {
.hero-grid {
grid-template-columns: 1fr;
gap: 36px;
}
.photo--hero {
aspect-ratio: 16 / 10;
}
.agent-grid {
grid-template-columns: repeat(2, 1fr);
}
.listing {
flex-basis: calc((100% - 24px) / 2);
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
gap: 24px 0;
}
.stat:nth-child(3) {
border-left: 0;
}
}
@media (max-width: 760px) {
.feature-trio,
.quotes {
grid-template-columns: 1fr;
}
.cta-inner {
flex-direction: column;
align-items: flex-start;
padding: 32px 28px;
}
.cta-form {
width: 100%;
}
.cta-form input {
flex: 1;
min-width: 0;
}
}
@media (max-width: 520px) {
.wrap {
padding: 0 18px;
}
.site-nav {
display: none;
}
.header-inner {
height: 62px;
}
.hero {
padding: 36px 0 44px;
}
h1.display {
font-size: clamp(2.3rem, 10vw, 2.9rem);
}
.search {
flex-wrap: wrap;
border-radius: var(--r-lg);
}
.search-field {
border-right: 0;
border-bottom: 1px solid var(--line);
flex: 1 1 100%;
}
.search-field--select {
flex: 1 1 calc(50% - 3px);
border-bottom: 0;
}
.search .btn {
flex: 1 1 100%;
margin-left: 0;
margin-top: 4px;
}
.listing {
flex-basis: calc(100% - 0px);
}
.agent-grid {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: 1fr 1fr;
}
.section {
padding: 52px 0;
}
.section-head {
flex-wrap: wrap;
}
.track {
padding: 0 18px;
}
.footer-inner {
grid-template-columns: 1fr;
}
.footer-nav {
justify-content: flex-start;
}
.toast-host {
left: 16px;
right: 16px;
}
.toast {
max-width: none;
}
}
@media (prefers-reduced-motion: reduce) {
* {
scroll-behavior: auto !important;
}
.track,
.toast,
.btn,
.card {
transition-duration: 0.01ms !important;
}
}/* Harbor & Vale — Brokerage Landing
* Vanilla JS: toast helper, animated stats, listings carousel, favourites,
* search + CTA handlers. No external libraries.
*/
(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastHost = document.getElementById("toast-host");
function toast(msg, opts) {
opts = opts || {};
var el = document.createElement("div");
el.className = "toast";
el.setAttribute("role", "status");
var ic = document.createElement("span");
ic.className = "t-ic";
ic.setAttribute("aria-hidden", "true");
ic.textContent = opts.icon || "✓";
var txt = document.createElement("span");
txt.textContent = msg;
el.appendChild(ic);
el.appendChild(txt);
toastHost.appendChild(el);
// force reflow then animate in
void el.offsetWidth;
el.classList.add("is-in");
var life = opts.duration || 3200;
setTimeout(function () {
el.classList.remove("is-in");
setTimeout(function () {
if (el.parentNode) el.parentNode.removeChild(el);
}, 320);
}, life);
}
/* ---------- animated stat counters ---------- */
function formatCount(value, decimals) {
if (decimals > 0) return value.toFixed(decimals);
return Math.round(value).toLocaleString("en-US");
}
function animateStat(el) {
var target = parseFloat(el.getAttribute("data-count"));
var prefix = el.getAttribute("data-prefix") || "";
var suffix = el.getAttribute("data-suffix") || "";
var decimals = target % 1 !== 0 ? 1 : 0;
var start = null;
var duration = 1400;
function step(ts) {
if (start === null) start = ts;
var p = Math.min((ts - start) / duration, 1);
// easeOutCubic
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = prefix + formatCount(target * eased, decimals) + suffix;
if (p < 1) requestAnimationFrame(step);
else el.textContent = prefix + formatCount(target, decimals) + suffix;
}
requestAnimationFrame(step);
}
var statEls = Array.prototype.slice.call(document.querySelectorAll(".stat-num"));
if (statEls.length) {
if ("IntersectionObserver" in window) {
var seen = new WeakSet();
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting && !seen.has(e.target)) {
seen.add(e.target);
animateStat(e.target);
}
});
},
{ threshold: 0.5 }
);
statEls.forEach(function (el) {
io.observe(el);
});
} else {
statEls.forEach(animateStat);
}
}
/* ---------- listings carousel ---------- */
var track = document.querySelector(".js-track");
var prevBtn = document.querySelector(".js-prev");
var nextBtn = document.querySelector(".js-next");
var dotsHost = document.querySelector(".js-dots");
if (track && prevBtn && nextBtn) {
var slides = Array.prototype.slice.call(track.children);
var page = 0;
function perView() {
var w = window.innerWidth;
if (w <= 520) return 1;
if (w <= 920) return 2;
return 3;
}
function pageCount() {
return Math.max(1, Math.ceil(slides.length / perView()));
}
function buildDots() {
if (!dotsHost) return;
dotsHost.innerHTML = "";
var count = pageCount();
for (var i = 0; i < count; i++) {
var b = document.createElement("button");
b.className = "dot-btn";
b.type = "button";
b.setAttribute("role", "tab");
b.setAttribute("aria-label", "Go to listing page " + (i + 1));
(function (idx) {
b.addEventListener("click", function () {
goTo(idx);
});
})(i);
dotsHost.appendChild(b);
}
}
function update() {
var pv = perView();
var slide = slides[0];
var gap = parseFloat(getComputedStyle(track).columnGap || getComputedStyle(track).gap) || 24;
var slideW = slide.getBoundingClientRect().width;
var step = (slideW + gap) * pv;
var maxPage = pageCount() - 1;
if (page > maxPage) page = maxPage;
track.style.transform = "translateX(" + -(step * page) + "px)";
prevBtn.disabled = page === 0;
nextBtn.disabled = page === maxPage;
if (dotsHost) {
var dots = dotsHost.querySelectorAll(".dot-btn");
dots.forEach(function (d, i) {
d.classList.toggle("is-active", i === page);
d.setAttribute("aria-selected", i === page ? "true" : "false");
});
}
}
function goTo(p) {
var maxPage = pageCount() - 1;
page = Math.max(0, Math.min(p, maxPage));
update();
}
nextBtn.addEventListener("click", function () {
goTo(page + 1);
});
prevBtn.addEventListener("click", function () {
goTo(page - 1);
});
// keyboard arrows when carousel region focused/hovered
document.querySelector(".carousel").setAttribute("tabindex", "0");
document.querySelector(".carousel").addEventListener("keydown", function (e) {
if (e.key === "ArrowRight") {
e.preventDefault();
goTo(page + 1);
} else if (e.key === "ArrowLeft") {
e.preventDefault();
goTo(page - 1);
}
});
var resizeTimer;
window.addEventListener("resize", function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
buildDots();
update();
}, 120);
});
buildDots();
update();
}
/* ---------- favourite (save) toggles ---------- */
Array.prototype.slice.call(document.querySelectorAll(".js-fav")).forEach(function (btn) {
btn.addEventListener("click", function () {
var saved = btn.classList.toggle("is-saved");
btn.setAttribute("aria-pressed", saved ? "true" : "false");
btn.textContent = saved ? "♥" : "♡";
var card = btn.closest(".listing");
var title = card ? card.querySelector(".listing-title").textContent : "Listing";
toast(saved ? "Saved " + title + " to your shortlist" : "Removed from shortlist", {
icon: saved ? "♥" : "✕"
});
});
});
/* ---------- hero search ---------- */
var searchForm = document.querySelector(".search");
if (searchForm) {
searchForm.addEventListener("submit", function (e) {
e.preventDefault();
var where = document.getElementById("q").value.trim();
var place = where || "Aldermoor";
toast("Searching " + place + " — 47 homes match your filters", { icon: "⌕" });
});
Array.prototype.slice.call(document.querySelectorAll(".search-hint .chip")).forEach(function (chip) {
chip.addEventListener("click", function () {
toast("Filter applied: " + chip.textContent, { icon: "✧" });
});
});
}
/* ---------- contact + CTA ---------- */
Array.prototype.slice.call(document.querySelectorAll(".js-contact")).forEach(function (b) {
b.addEventListener("click", function () {
toast("An advisor will reach out within one business day.", { icon: "☎" });
});
});
var ctaForm = document.querySelector(".js-cta-form");
if (ctaForm) {
ctaForm.addEventListener("submit", function (e) {
e.preventDefault();
var email = ctaForm.querySelector("input[type=email]").value.trim();
if (!email) return;
toast("Valuation request received for " + email, { icon: "✓" });
ctaForm.reset();
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Harbor & Vale — Brokerage</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===================== HEADER ===================== -->
<header class="site-header">
<div class="wrap header-inner">
<a class="brand" href="#" aria-label="Harbor and Vale home">
<span class="brand-mark" aria-hidden="true">H&V</span>
<span class="brand-name">Harbor & Vale</span>
</a>
<nav class="site-nav" aria-label="Primary">
<a href="#listings">Listings</a>
<a href="#why">Why us</a>
<a href="#agents">Agents</a>
<a href="#voices">Reviews</a>
</nav>
<button class="btn btn-ghost btn-sm js-contact" type="button">Contact</button>
</div>
</header>
<main id="main">
<!-- ===================== HERO ===================== -->
<section class="hero">
<div class="wrap hero-grid">
<div class="hero-copy">
<p class="eyebrow"><span class="dot" aria-hidden="true"></span>Coastal & valley specialists since 1994</p>
<h1 class="display">Find the address<br />you'll never want<br />to leave.</h1>
<p class="lede">
A boutique brokerage pairing concierge service with deep local knowledge across
the harborfront, the foothills, and everywhere worth calling home.
</p>
<form class="search" role="search" aria-label="Search listings">
<div class="search-field">
<label for="q">Where</label>
<input id="q" name="q" type="text" placeholder="Bayfront, Cedar Heights, 94110…" autocomplete="off" />
</div>
<div class="search-field search-field--select">
<label for="type">Type</label>
<select id="type" name="type">
<option>Any property</option>
<option>House</option>
<option>Condo</option>
<option>Townhouse</option>
<option>Estate</option>
</select>
</div>
<div class="search-field search-field--select">
<label for="price">Max price</label>
<select id="price" name="price">
<option>No max</option>
<option>$750k</option>
<option>$1.2M</option>
<option>$2M</option>
<option>$5M+</option>
</select>
</div>
<button class="btn btn-brass" type="submit">Search</button>
</form>
<p class="search-hint">Popular: <button class="chip" type="button">Waterfront</button>
<button class="chip" type="button">New construction</button>
<button class="chip" type="button">Under $1M</button>
</p>
</div>
<figure class="hero-photo photo photo--hero" aria-label="Hillside modern home at golden hour">
<span class="photo-tag">Cedar Heights · $2,395,000</span>
<figcaption class="hero-float">
<span class="hf-label">Just listed</span>
<span class="hf-title">The Ridgeline Residence</span>
<span class="hf-meta">4 bd · 3.5 ba · 3,820 sqft</span>
</figcaption>
</figure>
</div>
</section>
<!-- ===================== STATS ===================== -->
<section class="stats" aria-label="Brokerage results">
<div class="wrap stats-grid">
<div class="stat">
<span class="stat-num" data-count="3120" data-suffix="+">0</span>
<span class="stat-label">Homes sold</span>
</div>
<div class="stat">
<span class="stat-num" data-count="4.8" data-prefix="$" data-suffix="B">0</span>
<span class="stat-label">Sales volume</span>
</div>
<div class="stat">
<span class="stat-num" data-count="62">0</span>
<span class="stat-label">Advisors on staff</span>
</div>
<div class="stat">
<span class="stat-num" data-count="18" data-suffix=" days">0</span>
<span class="stat-label">Avg. days on market</span>
</div>
</div>
</section>
<!-- ===================== LISTINGS CAROUSEL ===================== -->
<section class="section" id="listings">
<div class="wrap section-head">
<div>
<p class="eyebrow eyebrow--ink">Featured listings</p>
<h2 class="display section-title">Handpicked homes, this week</h2>
</div>
<div class="carousel-ctrl">
<button class="round-btn js-prev" type="button" aria-label="Previous listings">←</button>
<button class="round-btn js-next" type="button" aria-label="Next listings">→</button>
</div>
</div>
<div class="carousel" aria-roledescription="carousel" aria-label="Featured listings">
<ul class="track js-track">
<!-- 1 -->
<li class="card listing">
<div class="photo photo--l1">
<span class="badge badge-status">For sale</span>
<button class="fav js-fav" type="button" aria-label="Save listing" aria-pressed="false">♡</button>
<span class="photo-tag">Bayfront</span>
</div>
<div class="listing-body">
<p class="listing-price">$1,640,000</p>
<h3 class="listing-title">28 Tideglass Lane</h3>
<p class="listing-addr">Harborfront District · Aldermoor</p>
<ul class="specs">
<li>3 bd</li><li>2 ba</li><li>2,140 sqft</li>
</ul>
</div>
</li>
<!-- 2 -->
<li class="card listing">
<div class="photo photo--l2">
<span class="badge badge-new">New</span>
<button class="fav js-fav" type="button" aria-label="Save listing" aria-pressed="false">♡</button>
<span class="photo-tag">Cedar Heights</span>
</div>
<div class="listing-body">
<p class="listing-price">$2,395,000</p>
<h3 class="listing-title">The Ridgeline Residence</h3>
<p class="listing-addr">Cedar Heights · Aldermoor</p>
<ul class="specs">
<li>4 bd</li><li>3.5 ba</li><li>3,820 sqft</li>
</ul>
</div>
</li>
<!-- 3 -->
<li class="card listing">
<div class="photo photo--l3">
<span class="badge badge-status">For sale</span>
<button class="fav js-fav" type="button" aria-label="Save listing" aria-pressed="false">♡</button>
<span class="photo-tag">Old Town</span>
</div>
<div class="listing-body">
<p class="listing-price">$884,000</p>
<h3 class="listing-title">7 Marigold Court</h3>
<p class="listing-addr">Old Town · Aldermoor</p>
<ul class="specs">
<li>2 bd</li><li>2 ba</li><li>1,460 sqft</li>
</ul>
</div>
</li>
<!-- 4 -->
<li class="card listing">
<div class="photo photo--l4">
<span class="badge badge-pending">Pending</span>
<button class="fav js-fav" type="button" aria-label="Save listing" aria-pressed="false">♡</button>
<span class="photo-tag">Vale Meadows</span>
</div>
<div class="listing-body">
<p class="listing-price">$1,120,000</p>
<h3 class="listing-title">410 Larkspur Way</h3>
<p class="listing-addr">Vale Meadows · Aldermoor</p>
<ul class="specs">
<li>3 bd</li><li>2.5 ba</li><li>2,310 sqft</li>
</ul>
</div>
</li>
<!-- 5 -->
<li class="card listing">
<div class="photo photo--l5">
<span class="badge badge-new">New</span>
<button class="fav js-fav" type="button" aria-label="Save listing" aria-pressed="false">♡</button>
<span class="photo-tag">The Esplanade</span>
</div>
<div class="listing-body">
<p class="listing-price">$3,750,000</p>
<h3 class="listing-title">The Glasshouse</h3>
<p class="listing-addr">The Esplanade · Aldermoor</p>
<ul class="specs">
<li>5 bd</li><li>4 ba</li><li>5,010 sqft</li>
</ul>
</div>
</li>
<!-- 6 -->
<li class="card listing">
<div class="photo photo--l6">
<span class="badge badge-status">For sale</span>
<button class="fav js-fav" type="button" aria-label="Save listing" aria-pressed="false">♡</button>
<span class="photo-tag">Foothill Row</span>
</div>
<div class="listing-body">
<p class="listing-price">$695,000</p>
<h3 class="listing-title">12 Juniper Mews</h3>
<p class="listing-addr">Foothill Row · Aldermoor</p>
<ul class="specs">
<li>2 bd</li><li>1 ba</li><li>1,180 sqft</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="dots js-dots" role="tablist" aria-label="Listing pages"></div>
</section>
<!-- ===================== WHY US ===================== -->
<section class="section section--paper" id="why">
<div class="wrap">
<div class="section-head section-head--center">
<div>
<p class="eyebrow eyebrow--ink">Why Harbor & Vale</p>
<h2 class="display section-title">Service that earns its referrals</h2>
</div>
</div>
<div class="feature-trio">
<article class="feature">
<span class="feature-ic" aria-hidden="true">♢</span>
<h3>Pricing intelligence</h3>
<p>Twice-weekly comp reviews and a data desk that prices to the neighborhood, not the headline.</p>
</article>
<article class="feature">
<span class="feature-ic" aria-hidden="true">❖</span>
<h3>White-glove staging</h3>
<p>In-house stylists, photography, and a marketing studio included on every listing we represent.</p>
</article>
<article class="feature">
<span class="feature-ic" aria-hidden="true">◆</span>
<h3>One advisor, start to close</h3>
<p>No hand-offs. The advisor you meet is the one who negotiates and stands beside you at signing.</p>
</article>
</div>
</div>
</section>
<!-- ===================== AGENTS ===================== -->
<section class="section" id="agents">
<div class="wrap section-head">
<div>
<p class="eyebrow eyebrow--ink">Agent spotlight</p>
<h2 class="display section-title">The people behind the keys</h2>
</div>
</div>
<div class="wrap agent-grid">
<article class="card agent">
<div class="photo photo--a1" aria-label="Portrait"></div>
<div class="agent-body">
<h3>Mara Lindqvist</h3>
<p class="agent-role">Principal Broker · Harborfront</p>
<p class="agent-stat">$412M sold · 240 closings</p>
</div>
</article>
<article class="card agent">
<div class="photo photo--a2" aria-label="Portrait"></div>
<div class="agent-body">
<h3>Desmond Okafor</h3>
<p class="agent-role">Senior Advisor · Luxury & estates</p>
<p class="agent-stat">$298M sold · 156 closings</p>
</div>
</article>
<article class="card agent">
<div class="photo photo--a3" aria-label="Portrait"></div>
<div class="agent-body">
<h3>Priya Raghunathan</h3>
<p class="agent-role">Advisor · First-time buyers</p>
<p class="agent-stat">$141M sold · 312 closings</p>
</div>
</article>
<article class="card agent">
<div class="photo photo--a4" aria-label="Portrait"></div>
<div class="agent-body">
<h3>Theo Brandt</h3>
<p class="agent-role">Advisor · New construction</p>
<p class="agent-stat">$187M sold · 98 closings</p>
</div>
</article>
</div>
</section>
<!-- ===================== TESTIMONIALS ===================== -->
<section class="section section--green" id="voices">
<div class="wrap">
<div class="section-head section-head--center">
<div>
<p class="eyebrow eyebrow--brass">Client voices</p>
<h2 class="display section-title section-title--light">What it feels like to work with us</h2>
</div>
</div>
<div class="quotes">
<figure class="quote">
<blockquote>"They sold our place above asking in nine days and made the whole thing feel calm. We never once felt managed — we felt looked after."</blockquote>
<figcaption><span class="q-name">The Halloran family</span><span class="q-meta">Sold in Vale Meadows</span></figcaption>
</figure>
<figure class="quote">
<blockquote>"Priya walked two nervous first-timers through every page. We closed under budget and still feel like she's on call for us."</blockquote>
<figcaption><span class="q-name">Anaïs & Lin</span><span class="q-meta">Bought in Foothill Row</span></figcaption>
</figure>
<figure class="quote">
<blockquote>"The staging and photography turned heads. Three competing offers by the first weekend. Worth every point of the fee."</blockquote>
<figcaption><span class="q-name">R. Castellano</span><span class="q-meta">Sold on The Esplanade</span></figcaption>
</figure>
</div>
</div>
</section>
<!-- ===================== CTA ===================== -->
<section class="cta">
<div class="wrap cta-inner">
<div class="cta-copy">
<h2 class="display cta-title">Thinking of making a move?</h2>
<p>Get a free, no-pressure home valuation and a market read for your block — usually back within a day.</p>
</div>
<form class="cta-form js-cta-form">
<label class="sr-only" for="cta-email">Email address</label>
<input id="cta-email" type="email" name="email" placeholder="[email protected]" required />
<button class="btn btn-brass" type="submit">Request valuation</button>
</form>
</div>
</section>
</main>
<!-- ===================== FOOTER ===================== -->
<footer class="site-footer">
<div class="wrap footer-inner">
<div class="footer-brand">
<span class="brand-mark" aria-hidden="true">H&V</span>
<p>Harbor & Vale Realty · 1994<br />A fictional boutique brokerage.</p>
</div>
<nav class="footer-nav" aria-label="Footer">
<a href="#listings">Buy</a>
<a href="#listings">Sell</a>
<a href="#agents">Our advisors</a>
<a href="#why">About</a>
</nav>
<p class="footer-fine">© 1994–2026 Harbor & Vale · Sample UI, no real listings.</p>
</div>
</footer>
<div class="toast-host" id="toast-host" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Brokerage Landing
An editorial marketing landing page for Harbor & Vale, a boutique coastal-and-valley brokerage. A serif display hero anchors a four-field search bar (location, type, max price) and a hero “photo” rendered entirely with layered CSS gradients, complete with a floating just-listed card. Below it, a green stats strip counts up homes sold, sales volume, advisor headcount, and average days on market as it scrolls into view.
The featured-listings carousel pages through six fictional homes — each with a gradient property image, a status badge (For sale / New / Pending), a save-to-shortlist heart, price, address, and bed/bath/sqft specs. Arrow buttons, clickable progress dots, and left/right keyboard arrows all move the track, and the per-view count adapts from three cards to one as the viewport narrows. Further down sit a why-us feature trio, a four-up agent spotlight grid, three client testimonials on a deep-green panel, and a valuation CTA.
Interactions are vanilla JS only: a reusable toast() helper surfaces feedback for searches, popular-filter chips, contact clicks, saved listings, and valuation requests, while an IntersectionObserver triggers the eased count-up animation. The layout is responsive down to ~360px and respects prefers-reduced-motion.
Illustrative UI only — sample listings and data are fictional; not a real real-estate service.