Pages Medium
Real Estate — Light Minimal
Light-mode architectural property site with warm grey palette, Fraunces italic display and Inter body fonts, split hero with parallax image stack, listings grid with filter bar, and dark contact section.
Open in Lab
MCP
gsap scrolltrigger lenis fraunces inter
Targets: JS HTML
Code
/* ── Demo 46: Real Estate — Light Minimal ── */
/* Palette: White / Warm Gray / Charcoal / Soft Terracotta accent */
/* Fonts: Fraunces (display serif italic) + Inter (clean UI/body) */
:root {
--white: #ffffff;
--off-white: #fafaf8;
--light: #f2f0ed;
--mid: #e4e1db;
--stone: #b8b4ad;
--muted: #8a8680;
--charcoal: #2a2825;
--black: #1a1816;
--accent: #c4856a; /* warm terracotta */
--accent-light: #f0e6e0; /* blush tint */
--accent-dark: #9c5c45;
--green: #5a7a65; /* sage */
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--white);
color: var(--charcoal);
line-height: 1.6;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
/* ── Typography Utilities ── */
.serif {
font-family: "Fraunces", Georgia, serif;
}
.serif-italic {
font-family: "Fraunces", Georgia, serif;
font-style: italic;
}
/* ── Navigation ── */
.nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem 3rem;
background: transparent;
transition: background 0.4s ease, padding 0.4s ease, border-bottom 0.4s ease;
}
.nav.scrolled {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(12px);
padding: 1rem 3rem;
border-bottom: 1px solid var(--mid);
}
.nav-brand {
display: flex;
align-items: center;
gap: 0.75rem;
text-decoration: none;
color: var(--charcoal);
}
.nav-mark {
width: 28px;
height: 28px;
background: var(--charcoal);
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
.nav-name {
font-family: "Fraunces", serif;
font-size: 1.1rem;
font-weight: 400;
letter-spacing: 0.05em;
color: var(--charcoal);
}
.nav-links {
display: flex;
align-items: center;
gap: 2.5rem;
list-style: none;
}
.nav-links a {
text-decoration: none;
color: var(--charcoal);
font-size: 0.85rem;
font-weight: 500;
letter-spacing: 0.04em;
transition: color 0.2s;
}
.nav-links a:hover {
color: var(--accent);
}
.nav-cta {
display: inline-block;
padding: 0.65rem 1.5rem;
background: var(--charcoal);
color: var(--white);
text-decoration: none;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
border-radius: 2px;
transition: background 0.2s, transform 0.2s;
}
.nav-cta:hover {
background: var(--accent);
transform: translateY(-1px);
}
/* ── Hero ── */
.hero {
display: grid;
grid-template-columns: 1fr 1fr;
min-height: 100vh;
padding-top: 5rem;
}
.hero-left {
display: flex;
flex-direction: column;
justify-content: center;
padding: 5rem 3rem 5rem 3rem;
background: var(--off-white);
}
.hero-label {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 2rem;
}
.hero-h1 {
font-family: "Fraunces", serif;
font-size: clamp(2.8rem, 5vw, 4.5rem);
font-weight: 300;
line-height: 1.15;
color: var(--black);
margin-bottom: 1.5rem;
}
.hero-h1 .h1-italic {
font-style: italic;
color: var(--accent);
}
.hero-body {
font-size: 1rem;
color: var(--muted);
line-height: 1.8;
max-width: 400px;
margin-bottom: 2.5rem;
}
.hero-btns {
display: flex;
gap: 1rem;
align-items: center;
margin-bottom: 4rem;
}
.btn-dark {
display: inline-block;
padding: 0.85rem 2rem;
background: var(--charcoal);
color: var(--white);
text-decoration: none;
font-size: 0.82rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
border-radius: 2px;
transition: background 0.2s, transform 0.2s;
}
.btn-dark:hover {
background: var(--accent);
transform: translateY(-2px);
}
.btn-line {
display: inline-block;
color: var(--charcoal);
text-decoration: none;
font-size: 0.85rem;
font-weight: 500;
border-bottom: 1px solid var(--charcoal);
padding-bottom: 2px;
transition: color 0.2s, border-color 0.2s;
}
.btn-line:hover {
color: var(--accent);
border-color: var(--accent);
}
.hero-metric-row {
display: flex;
align-items: center;
gap: 2rem;
padding-top: 2rem;
border-top: 1px solid var(--mid);
}
.hero-metric {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.hm-num {
font-family: "Fraunces", serif;
font-size: 2rem;
font-weight: 300;
color: var(--black);
line-height: 1;
}
.hm-label {
font-size: 0.72rem;
font-weight: 500;
color: var(--stone);
letter-spacing: 0.05em;
}
.hero-metric-div {
width: 1px;
height: 40px;
background: var(--mid);
}
/* Hero right — image stack */
.hero-right {
position: relative;
background: var(--light);
overflow: hidden;
}
.hero-img {
position: absolute;
border-radius: 4px;
overflow: hidden;
}
.hero-img--main {
top: 10%;
left: 10%;
right: 15%;
bottom: 25%;
background: linear-gradient(145deg, #d6cec4 0%, #c8bfb2 30%, #b8a99a 60%, #a89486 100%);
}
.hero-img--sub {
bottom: 8%;
right: 8%;
width: 45%;
height: 35%;
background: linear-gradient(135deg, #e8e2d8 0%, #d4c9bc 40%, #c5b8aa 100%);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12);
}
/* ── Section shared ── */
.section-eyebrow {
display: inline-block;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 1rem;
}
/* ── Listings Section ── */
.listings-section {
padding: 6rem 3rem;
background: var(--white);
border-top: 1px solid var(--mid);
}
.listings-header {
display: flex;
align-items: baseline;
justify-content: space-between;
flex-wrap: wrap;
gap: 1.5rem;
margin-bottom: 3rem;
}
.listings-header h2 {
font-family: "Fraunces", serif;
font-size: clamp(1.8rem, 3vw, 2.5rem);
font-weight: 300;
color: var(--black);
}
.filter-bar {
display: flex;
gap: 0.5rem;
}
.filter-btn {
padding: 0.45rem 1rem;
border: 1px solid var(--mid);
background: transparent;
color: var(--muted);
font-family: "Inter", sans-serif;
font-size: 0.78rem;
font-weight: 500;
letter-spacing: 0.04em;
border-radius: 2px;
cursor: pointer;
transition: all 0.2s;
}
.filter-btn:hover,
.filter-btn.active {
background: var(--charcoal);
color: var(--white);
border-color: var(--charcoal);
}
.listings-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto;
gap: 1.5rem;
}
.listing-card--large {
grid-column: 1 / 2;
grid-row: 1 / 3;
}
.listing-card {
background: var(--off-white);
border: 1px solid var(--mid);
border-radius: 4px;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.listing-card:hover {
transform: translateY(-4px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
}
.lc-img {
position: relative;
overflow: hidden;
}
.listing-card--large .lc-img {
height: 320px;
}
.lc-img--1 {
background: linear-gradient(145deg, #b8b0a4 0%, #9a9088 40%, #7c7068 100%);
}
.lc-img--2 {
height: 180px;
background: linear-gradient(135deg, #c0bdb8 0%, #a8a49e 50%, #8a8680 100%);
}
.lc-img--3 {
height: 180px;
background: linear-gradient(125deg, #c8c2ba 0%, #b0aa9e 50%, #948e86 100%);
}
.lc-badge {
position: absolute;
top: 1rem;
left: 1rem;
padding: 0.3rem 0.7rem;
background: var(--accent);
color: var(--white);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
border-radius: 2px;
}
.lc-info {
padding: 1.5rem;
}
.lc-info--sm {
padding: 1.25rem;
}
.lc-type {
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--stone);
margin-bottom: 0.5rem;
}
.lc-info h3 {
font-family: "Fraunces", serif;
font-size: 1.3rem;
font-weight: 300;
color: var(--black);
margin-bottom: 0.6rem;
}
.lc-info--sm h3 {
font-size: 1.1rem;
}
.lc-info p {
font-size: 0.85rem;
color: var(--muted);
line-height: 1.7;
margin-bottom: 1rem;
}
.lc-details {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.lc-details span {
font-size: 0.78rem;
color: var(--stone);
font-weight: 500;
}
.lc-price {
font-family: "Fraunces", serif;
font-size: 1.4rem;
font-weight: 300;
color: var(--charcoal);
}
.lc-info--sm .lc-price {
font-size: 1.2rem;
}
.listings-more {
margin-top: 2.5rem;
text-align: center;
}
/* ── Process Section ── */
.process-section {
padding: 6rem 3rem;
background: var(--off-white);
border-top: 1px solid var(--mid);
}
.process-inner {
max-width: 1100px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5rem;
align-items: start;
}
.process-left h2 {
font-family: "Fraunces", serif;
font-size: clamp(2rem, 3.5vw, 3rem);
font-weight: 300;
line-height: 1.25;
color: var(--black);
margin-bottom: 1.2rem;
}
.process-left h2 em {
font-style: italic;
color: var(--accent);
}
.process-left p {
font-size: 0.95rem;
color: var(--muted);
line-height: 1.8;
}
.process-steps {
display: flex;
flex-direction: column;
gap: 0;
}
.step {
display: flex;
gap: 1.5rem;
padding: 1.5rem 0;
border-bottom: 1px solid var(--mid);
}
.step:first-child {
padding-top: 0;
}
.step-num {
font-family: "Fraunces", serif;
font-size: 0.85rem;
font-weight: 300;
color: var(--accent);
flex-shrink: 0;
width: 28px;
padding-top: 2px;
}
.step-content h4 {
font-size: 0.95rem;
font-weight: 600;
color: var(--charcoal);
margin-bottom: 0.4rem;
letter-spacing: 0.01em;
}
.step-content p {
font-size: 0.85rem;
color: var(--muted);
line-height: 1.7;
}
/* ── About Section ── */
.about-section {
padding: 6rem 3rem;
background: var(--white);
border-top: 1px solid var(--mid);
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5rem;
align-items: center;
max-width: 100%;
}
.about-img-block {
position: relative;
height: 500px;
}
.ab-img {
position: absolute;
border-radius: 4px;
}
.ab-img--main {
top: 0;
left: 0;
right: 20%;
bottom: 20%;
background: linear-gradient(145deg, #d0c8c0 0%, #b8b0a4 50%, #a0988c 100%);
}
.ab-img--secondary {
bottom: 0;
right: 0;
width: 50%;
height: 45%;
background: linear-gradient(135deg, #c4bdb6 0%, #a89e95 100%);
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.1);
}
.about-content {
max-width: 440px;
}
.about-content h2 {
font-family: "Fraunces", serif;
font-size: clamp(2.5rem, 4vw, 3.5rem);
font-weight: 300;
line-height: 1.15;
color: var(--black);
margin-bottom: 1.5rem;
}
.about-content p {
font-size: 0.95rem;
color: var(--muted);
line-height: 1.85;
margin-bottom: 1.2rem;
}
/* ── Contact Section ── */
.contact-section {
padding: 6rem 3rem;
background: var(--charcoal);
border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.contact-inner {
max-width: 1100px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5rem;
align-items: start;
}
.contact-section .section-eyebrow {
color: var(--accent);
}
.contact-left h2 {
font-family: "Fraunces", serif;
font-size: clamp(2rem, 3.5vw, 3rem);
font-weight: 300;
color: var(--white);
line-height: 1.2;
margin-bottom: 2.5rem;
}
.contact-details {
display: flex;
flex-direction: column;
gap: 1.2rem;
}
.cd-item {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.cd-label {
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--stone);
}
.cd-item span:last-child {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
}
.contact-form {
display: flex;
flex-direction: column;
gap: 1.2rem;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.form-field {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-field label {
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--stone);
}
.form-input-mock {
height: 48px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 2px;
transition: border-color 0.2s;
}
.form-input-mock:hover {
border-color: rgba(255, 255, 255, 0.25);
}
.form-input-mock--tall {
height: 120px;
}
/* ── Footer ── */
.footer {
padding: 2rem 3rem;
background: var(--black);
border-top: 1px solid rgba(255, 255, 255, 0.05);
display: flex;
align-items: center;
justify-content: space-between;
}
.footer-brand {
display: flex;
align-items: center;
gap: 0.75rem;
}
.footer-brand .nav-mark {
background: var(--stone);
width: 20px;
height: 20px;
}
.footer-brand span {
font-family: "Fraunces", serif;
font-size: 0.9rem;
font-weight: 300;
color: var(--stone);
}
.footer p {
font-size: 0.78rem;
color: var(--muted);
}
/* ── Responsive ── */
@media (max-width: 1024px) {
.hero {
grid-template-columns: 1fr;
min-height: auto;
}
.hero-right {
height: 50vw;
min-height: 300px;
}
.hero-left {
padding: 6rem 2rem 3rem;
}
.process-inner {
grid-template-columns: 1fr;
gap: 3rem;
}
.about-section {
grid-template-columns: 1fr;
}
.about-img-block {
height: 350px;
}
.contact-inner {
grid-template-columns: 1fr;
gap: 3rem;
}
.listings-grid {
grid-template-columns: 1fr 1fr;
}
.listing-card--large {
grid-column: 1 / 3;
grid-row: auto;
}
}
@media (max-width: 768px) {
.nav {
padding: 1.25rem 1.5rem;
}
.nav.scrolled {
padding: 0.85rem 1.5rem;
}
.nav-links {
display: none;
}
.listings-section,
.process-section,
.about-section,
.contact-section {
padding: 4rem 1.5rem;
}
.footer {
padding: 1.5rem;
flex-direction: column;
gap: 1rem;
text-align: center;
}
.listings-grid {
grid-template-columns: 1fr;
}
.listing-card--large {
grid-column: auto;
}
.form-row {
grid-template-columns: 1fr;
}
.filter-bar {
flex-wrap: wrap;
}
}
/* ── Reduced Motion ── */
html.reduced-motion * {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
html.reduced-motion .listing-card:hover {
transform: none;
}if (!window.MotionPreference) {
const __mql = window.matchMedia("(prefers-reduced-motion: reduce)");
const __listeners = new Set();
const MotionPreference = {
prefersReducedMotion() {
return __mql.matches;
},
setOverride(value) {
const reduced = Boolean(value);
document.documentElement.classList.toggle("reduced-motion", reduced);
window.dispatchEvent(new CustomEvent("motion-preference", { detail: { reduced } }));
for (const listener of __listeners) {
try {
listener({ reduced, override: reduced, systemReduced: __mql.matches });
} catch {}
}
},
onChange(listener) {
__listeners.add(listener);
try {
listener({
reduced: __mql.matches,
override: null,
systemReduced: __mql.matches,
});
} catch {}
return () => __listeners.delete(listener);
},
getState() {
return { reduced: __mql.matches, override: null, systemReduced: __mql.matches };
},
};
window.MotionPreference = MotionPreference;
}
function prefersReducedMotion() {
return window.MotionPreference.prefersReducedMotion();
}
function initDemoShell() {
// No-op shim in imported standalone snippets.
}
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import Lenis from "lenis";
gsap.registerPlugin(ScrollTrigger);
initDemoShell({
title: "Real Estate — Light Minimal",
category: "pages",
tech: ["gsap", "scroll-trigger", "lenis", "fraunces", "inter"],
});
const reduced = prefersReducedMotion();
if (reduced) document.documentElement.classList.add("reduced-motion");
// ── Lenis smooth scroll ───────────────────────────────────────────────────────
const lenis = new Lenis({ lerp: 0.09, smoothWheel: true });
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);
// ── Nav scroll state ──────────────────────────────────────────────────────────
const nav = document.getElementById("nav");
ScrollTrigger.create({
start: "top -60px",
onUpdate: (self) => {
nav.classList.toggle("scrolled", self.scroll() > 60);
},
});
// ── Hero entrance ─────────────────────────────────────────────────────────────
if (!reduced) {
gsap.set(".hero-label", { opacity: 0, y: 12 });
gsap.set(".hero-h1", { opacity: 0, y: 25 });
gsap.set(".hero-body", { opacity: 0, y: 18 });
gsap.set(".hero-btns", { opacity: 0, y: 15 });
gsap.set(".hero-metric-row", { opacity: 0 });
gsap.set(".hero-img--main", { opacity: 0, scale: 1.04 });
gsap.set(".hero-img--sub", { opacity: 0, y: 20 });
gsap
.timeline({ delay: 0.3, defaults: { ease: "expo.out" } })
.to(".hero-label", { opacity: 1, y: 0, duration: 0.7 })
.to(".hero-h1", { opacity: 1, y: 0, duration: 1 }, "-=0.4")
.to(".hero-body", { opacity: 1, y: 0, duration: 0.8 }, "-=0.6")
.to(".hero-btns", { opacity: 1, y: 0, duration: 0.7 }, "-=0.5")
.to(".hero-metric-row", { opacity: 1, duration: 0.6 }, "-=0.4")
.to(".hero-img--main", { opacity: 1, scale: 1, duration: 1.2, ease: "power2.out" }, 0.5)
.to(".hero-img--sub", { opacity: 1, y: 0, duration: 0.9 }, "-=0.5");
}
// ── Parallax on hero images ───────────────────────────────────────────────────
if (!reduced) {
gsap.to(".hero-img--main", {
yPercent: -8,
ease: "none",
scrollTrigger: {
trigger: ".hero",
start: "top top",
end: "bottom top",
scrub: 1.5,
},
});
gsap.to(".hero-img--sub", {
yPercent: -14,
ease: "none",
scrollTrigger: {
trigger: ".hero",
start: "top top",
end: "bottom top",
scrub: 2,
},
});
}
// ── Metric numbers count up ───────────────────────────────────────────────────
const metrics = document.querySelectorAll(".hero-metric .hm-num");
metrics.forEach((el) => {
const target = parseFloat(el.textContent);
if (!reduced) {
ScrollTrigger.create({
trigger: ".hero-metric-row",
start: "top 85%",
end: "top 40%",
onUpdate: (self) => {
const val = target * self.progress;
el.textContent = Number.isInteger(target) ? Math.round(val) : val.toFixed(1);
},
});
}
});
// ── Filter bar interaction ────────────────────────────────────────────────────
document.querySelectorAll(".filter-btn").forEach((btn) => {
btn.addEventListener("click", () => {
document.querySelectorAll(".filter-btn").forEach((b) => b.classList.remove("active"));
btn.classList.add("active");
});
});
// ── Listing cards reveal ──────────────────────────────────────────────────────
document.querySelectorAll(".listing-card").forEach((card, i) => {
if (!reduced) {
gsap.set(card, { opacity: 0, y: 35 });
gsap.to(card, {
opacity: 1,
y: 0,
duration: 0.9,
ease: "expo.out",
delay: i * 0.1,
scrollTrigger: {
trigger: ".listings-grid",
start: "top 75%",
toggleActions: "play none none reverse",
},
});
}
});
// ── Listings header reveal ────────────────────────────────────────────────────
if (!reduced) {
gsap.set(".listings-header h2, .listings-header .section-eyebrow", { opacity: 0, y: 20 });
gsap.to(".listings-header h2, .listings-header .section-eyebrow", {
opacity: 1,
y: 0,
duration: 0.8,
stagger: 0.1,
ease: "expo.out",
scrollTrigger: {
trigger: ".listings-header",
start: "top 80%",
toggleActions: "play none none reverse",
},
});
}
// ── Process steps reveal ──────────────────────────────────────────────────────
if (!reduced) {
// Left column — heading
gsap.set(".process-left > *", { opacity: 0, x: -25 });
gsap.to(".process-left > *", {
opacity: 1,
x: 0,
duration: 0.9,
stagger: 0.12,
ease: "expo.out",
scrollTrigger: {
trigger: ".process-section",
start: "top 70%",
toggleActions: "play none none reverse",
},
});
// Steps — each step line
document.querySelectorAll(".step").forEach((step, i) => {
gsap.set(step, { opacity: 0, x: 20 });
gsap.to(step, {
opacity: 1,
x: 0,
duration: 0.7,
ease: "expo.out",
delay: i * 0.1,
scrollTrigger: {
trigger: ".process-steps",
start: "top 75%",
toggleActions: "play none none reverse",
},
});
});
}
// ── About section reveal ──────────────────────────────────────────────────────
if (!reduced) {
gsap.set(".ab-img--main", { opacity: 0, scale: 1.03 });
gsap.to(".ab-img--main", {
opacity: 1,
scale: 1,
duration: 1.2,
ease: "power2.out",
scrollTrigger: {
trigger: ".about-section",
start: "top 70%",
toggleActions: "play none none reverse",
},
});
gsap.set(".ab-img--secondary", { opacity: 0, y: 25 });
gsap.to(".ab-img--secondary", {
opacity: 1,
y: 0,
duration: 1,
ease: "expo.out",
delay: 0.3,
scrollTrigger: {
trigger: ".about-section",
start: "top 70%",
toggleActions: "play none none reverse",
},
});
gsap.set(".about-content > *", { opacity: 0, y: 22 });
gsap.to(".about-content > *", {
opacity: 1,
y: 0,
duration: 0.8,
stagger: 0.1,
ease: "expo.out",
delay: 0.15,
scrollTrigger: {
trigger: ".about-section",
start: "top 70%",
toggleActions: "play none none reverse",
},
});
}
// ── Contact section reveal ────────────────────────────────────────────────────
if (!reduced) {
gsap.set(".contact-left > *", { opacity: 0, y: 20 });
gsap.to(".contact-left > *", {
opacity: 1,
y: 0,
duration: 0.8,
stagger: 0.1,
ease: "expo.out",
scrollTrigger: {
trigger: ".contact-section",
start: "top 75%",
toggleActions: "play none none reverse",
},
});
gsap.set(".form-field", { opacity: 0, y: 18 });
gsap.to(".form-field", {
opacity: 1,
y: 0,
duration: 0.7,
stagger: 0.08,
ease: "expo.out",
delay: 0.2,
scrollTrigger: {
trigger: ".contact-form",
start: "top 80%",
toggleActions: "play none none reverse",
},
});
}
// ── Subtle section divider line animations ────────────────────────────────────
if (!reduced) {
document.querySelectorAll(".step").forEach((step) => {
step.addEventListener("mouseenter", () => {
gsap.to(step.querySelector(".step-num"), {
color: "#c4856a",
duration: 0.3,
ease: "power2.out",
});
});
step.addEventListener("mouseleave", () => {
gsap.to(step.querySelector(".step-num"), {
color: "#c4856a",
duration: 0.3,
ease: "power2.out",
});
});
});
}
// ── Motion preference toggle ──────────────────────────────────────────────────
window.addEventListener("motion-preference", (e) => {
if (e.detail.reduced) {
gsap.globalTimeline.paused(true);
} else {
gsap.globalTimeline.paused(false);
}
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meridian Property Group — Architecture & Real Estate</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=Inter:wght@300;400;500;600&family=Fraunces:ital,wght@0,300;0,400;1,300;1,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<script type="importmap">{"imports":{"gsap":"https://esm.sh/gsap@3.13.0","gsap/ScrollTrigger":"https://esm.sh/gsap@3.13.0/ScrollTrigger","gsap/SplitText":"https://esm.sh/gsap@3.13.0/SplitText","gsap/Flip":"https://esm.sh/gsap@3.13.0/Flip","gsap/ScrambleTextPlugin":"https://esm.sh/gsap@3.13.0/ScrambleTextPlugin","gsap/TextPlugin":"https://esm.sh/gsap@3.13.0/TextPlugin","gsap/all":"https://esm.sh/gsap@3.13.0/all","gsap/":"https://esm.sh/gsap@3.13.0/","lenis":"https://esm.sh/lenis@1.1.13/dist/lenis.mjs","three":"https://esm.sh/three@0.171.0","three/addons/":"https://esm.sh/three@0.171.0/examples/jsm/"}}</script>
<style>html.lenis,
html.lenis body {
height: auto;
}
.lenis:not(.lenis-autoToggle).lenis-stopped {
overflow: clip;
}
.lenis [data-lenis-prevent],
.lenis [data-lenis-prevent-wheel],
.lenis [data-lenis-prevent-touch] {
overscroll-behavior: contain;
}
.lenis.lenis-smooth iframe {
pointer-events: none;
}
.lenis.lenis-autoToggle {
transition-property: overflow;
transition-duration: 1ms;
transition-behavior: allow-discrete;
}</style>
</head>
<body>
<!-- Nav -->
<nav class="nav" id="nav">
<div class="nav-brand">
<div class="nav-mark" aria-hidden="true"></div>
<span class="nav-name">Meridian</span>
</div>
<ul class="nav-links">
<li><a href="#listings">Properties</a></li>
<li><a href="#process">Process</a></li>
<li><a href="#about">Studio</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
<a href="#contact" class="nav-cta">Schedule a Visit</a>
</nav>
<!-- Hero -->
<section class="hero">
<div class="hero-left">
<div class="hero-label">Residential · Commercial · 2025</div>
<h1 class="hero-h1">
<span>Where<br>architecture</span>
<span class="h1-italic">meets living</span>
</h1>
<p class="hero-body">Meridian curates exceptional properties in London, Copenhagen, and Amsterdam — buildings that endure because they were designed to.</p>
<div class="hero-btns">
<a href="#listings" class="btn-dark">Browse Properties</a>
<a href="#process" class="btn-line">Our Process →</a>
</div>
<div class="hero-metric-row">
<div class="hero-metric"><span class="hm-num">240</span><span class="hm-label">Properties sold</span></div>
<div class="hero-metric-div" aria-hidden="true"></div>
<div class="hero-metric"><span class="hm-num">18</span><span class="hm-label">Years active</span></div>
<div class="hero-metric-div" aria-hidden="true"></div>
<div class="hero-metric"><span class="hm-num">3</span><span class="hm-label">Cities</span></div>
</div>
</div>
<div class="hero-right" aria-hidden="true">
<div class="hero-img hero-img--main"></div>
<div class="hero-img hero-img--sub"></div>
</div>
</section>
<!-- Featured Listings -->
<section class="listings-section" id="listings">
<div class="listings-header">
<span class="section-eyebrow">Available Properties</span>
<h2>Current Listings</h2>
<div class="filter-bar">
<button class="filter-btn active">All</button>
<button class="filter-btn">Residential</button>
<button class="filter-btn">Commercial</button>
<button class="filter-btn">New Development</button>
</div>
</div>
<div class="listings-grid">
<article class="listing-card listing-card--large">
<div class="lc-img lc-img--1" aria-hidden="true">
<div class="lc-badge">New</div>
</div>
<div class="lc-info">
<div class="lc-type">Residential · London</div>
<h3>Bankside Penthouse</h3>
<p>A 320m² top-floor residence with sweeping Thames views, designed by Wilkinson Eyre Architects in 2023.</p>
<div class="lc-details">
<span>320 m²</span>
<span>4 bed · 3 bath</span>
<span>Floor 18</span>
</div>
<div class="lc-price">£ 4,200,000</div>
</div>
</article>
<article class="listing-card">
<div class="lc-img lc-img--2" aria-hidden="true"></div>
<div class="lc-info lc-info--sm">
<div class="lc-type">Commercial · Copenhagen</div>
<h3>Nordhavn Studio</h3>
<p>Open-plan creative studio in the iconic Nordhavn waterfront district.</p>
<div class="lc-price">DKK 8,400,000</div>
</div>
</article>
<article class="listing-card">
<div class="lc-img lc-img--3" aria-hidden="true"></div>
<div class="lc-info lc-info--sm">
<div class="lc-type">Residential · Amsterdam</div>
<h3>Jordaan Canal House</h3>
<p>Restored 17th-century canal house with original wooden beams and modern amenities.</p>
<div class="lc-price">€ 3,100,000</div>
</div>
</article>
</div>
<div class="listings-more">
<a href="#" class="btn-line">View all 24 listings →</a>
</div>
</section>
<!-- Process Section -->
<section class="process-section" id="process">
<div class="process-inner">
<div class="process-left">
<span class="section-eyebrow">How We Work</span>
<h2>A considered <br><em>approach</em></h2>
<p>We don't list properties. We match people to places that will shape the next chapter of their lives. Our process begins with listening.</p>
</div>
<div class="process-steps">
<div class="step">
<div class="step-num">01</div>
<div class="step-content">
<h4>Discovery Consultation</h4>
<p>A conversation about how you live, work, and what a building needs to do for you.</p>
</div>
</div>
<div class="step">
<div class="step-num">02</div>
<div class="step-content">
<h4>Curated Selection</h4>
<p>We present only 3–5 properties that match your criteria. Quality over quantity, always.</p>
</div>
</div>
<div class="step">
<div class="step-num">03</div>
<div class="step-content">
<h4>Architectural Review</h4>
<p>Our in-house architects assess structural integrity, renovation potential, and long-term value.</p>
</div>
</div>
<div class="step">
<div class="step-num">04</div>
<div class="step-content">
<h4>Transaction & Beyond</h4>
<p>Full legal and financial coordination. We remain your point of contact after keys are handed over.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Studio / About -->
<section class="about-section" id="about">
<div class="about-img-block" aria-hidden="true">
<div class="ab-img ab-img--main"></div>
<div class="ab-img ab-img--secondary"></div>
</div>
<div class="about-content">
<span class="section-eyebrow">The Studio</span>
<h2>Designed<br>from the<br>inside out</h2>
<p>Meridian was founded by Elena Marsh and Thomas Velde — an architect and a property lawyer who saw an industry focused on volume over value. We believe every transaction should leave both buyer and city better than before.</p>
<p>Our studio employs 12 specialists across architecture, finance, heritage conservation, and interior strategy.</p>
<a href="#contact" class="btn-dark">Meet the Team →</a>
</div>
</section>
<!-- Contact / CTA -->
<section class="contact-section" id="contact">
<div class="contact-inner">
<div class="contact-left">
<span class="section-eyebrow">Get in Touch</span>
<h2>Let's find<br>your property.</h2>
<div class="contact-details">
<div class="cd-item">
<span class="cd-label">London Office</span>
<span>14 Bermondsey Street, SE1</span>
</div>
<div class="cd-item">
<span class="cd-label">Email</span>
<span>hello@meridianprop.co</span>
</div>
<div class="cd-item">
<span class="cd-label">Hours</span>
<span>Mon–Fri · 09:00–18:00</span>
</div>
</div>
</div>
<div class="contact-form">
<div class="form-row">
<div class="form-field">
<label>Full name</label>
<div class="form-input-mock"></div>
</div>
<div class="form-field">
<label>Email</label>
<div class="form-input-mock"></div>
</div>
</div>
<div class="form-field">
<label>What are you looking for?</label>
<div class="form-input-mock form-input-mock--tall"></div>
</div>
<a href="#" class="btn-dark">Send Message →</a>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="footer-brand">
<div class="nav-mark" aria-hidden="true"></div>
<span>Meridian Property Group</span>
</div>
<p>© 2025 Meridian Property Group. London · Copenhagen · Amsterdam.</p>
</footer>
<script type="module" src="script.js"></script>
</body>
</html>Real Estate — Light Minimal
Light-mode architectural property site with warm grey palette, Fraunces italic display and Inter body fonts, split hero with parallax image stack, listings grid with filter bar, and dark contact section.
Source
- Repository:
libs-genclaude - Original demo id:
46-real-estate
Notes
Light-mode architectural property site with warm grey palette, Fraunces italic display and Inter body fonts, split hero with parallax image stack, listings grid with filter bar, and dark contact section.