Real Estate — Commercial / Industrial Landing
An authoritative B2B landing page for a fictional commercial and industrial real-estate brokerage, built with vanilla HTML, CSS, and JavaScript. A steel-and-navy hero with animated deal counters fronts a featured sale-leaseback asset, while asset-class tabs switch a sortable listings table showing cap rate, NOI, price per square foot, and for-sale or for-lease badges. An investment-highlights strip, a six-card brokerage-services grid, and a validated request-an-offering-memorandum form round out the structured, no-nonsense layout.
MCP
الكود
/* ============================================================
Meridian Commercial — Commercial / Industrial landing
Palette override: Steel #3b454d · Navy #1d2b3a · Amber #e0a13c
Inter for both display and body. Authoritative, B2B, structured.
============================================================ */
:root {
/* core palette */
--steel: #3b454d;
--steel-d: #2c343b;
--navy: #1d2b3a;
--navy-d: #14202c;
--navy-700: #25384b;
--amber: #e0a13c;
--amber-d: #c4831f;
--amber-50: #f7e9cf;
/* neutrals */
--paper: #f4f5f6;
--white: #ffffff;
--panel: #fbfbfc;
--ink: #182028;
--ink-2: #3a444d;
--muted: #6c757d;
--muted-2: #97a0a8;
/* feedback */
--ok: #2f9e6f;
--warn: #d08a2a;
--danger: #c4503e;
/* lines */
--line: rgba(29, 43, 58, 0.12);
--line-2: rgba(29, 43, 58, 0.22);
--line-amber: rgba(224, 161, 60, 0.45);
/* radii */
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
/* shadows */
--sh-sm: 0 1px 2px rgba(20, 32, 44, 0.06), 0 1px 1px rgba(20, 32, 44, 0.04);
--sh-md: 0 8px 22px rgba(20, 32, 44, 0.10), 0 2px 6px rgba(20, 32, 44, 0.06);
--sh-lg: 0 26px 60px rgba(20, 32, 44, 0.20), 0 8px 18px rgba(20, 32, 44, 0.10);
--max: 1180px;
}
/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
font-size: 16px;
line-height: 1.55;
color: var(--ink);
background: var(--paper);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { font-family: "Inter", system-ui, sans-serif; line-height: 1.12; margin: 0; letter-spacing: -0.02em; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; cursor: pointer; }
.wrap { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 24px; }
.sr-only, .skip-link {
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:focus {
position: fixed; top: 12px; left: 12px; width: auto; height: auto; clip: auto;
z-index: 200; background: var(--navy); color: #fff; padding: 10px 16px;
border-radius: var(--r-sm); box-shadow: var(--sh-md);
}
/* ---------- buttons ---------- */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 11px 18px; border-radius: var(--r-sm); border: 1px solid transparent;
font-weight: 600; font-size: 0.92rem; letter-spacing: 0.01em;
transition: transform .14s ease, box-shadow .14s ease, background .14s ease, border-color .14s ease, color .14s ease;
white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn--lg { padding: 14px 24px; font-size: 1rem; }
.btn--block { width: 100%; }
.btn--amber { background: var(--amber); color: var(--navy-d); box-shadow: var(--sh-sm); }
.btn--amber:hover { background: var(--amber-d); color: #fff; box-shadow: var(--sh-md); }
.btn--navy { background: var(--navy); color: #fff; }
.btn--navy:hover { background: var(--navy-d); box-shadow: var(--sh-md); }
.btn--outline { background: transparent; color: var(--navy); border-color: var(--line-2); }
.btn--outline:hover { border-color: var(--navy); background: rgba(29,43,58,0.04); }
.btn--ghost { background: transparent; color: var(--ink-2); padding: 9px 12px; }
.btn--ghost:hover { color: var(--navy); background: rgba(29,43,58,0.05); }
:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; border-radius: 4px; }
/* ---------- eyebrow / badges ---------- */
.eyebrow {
text-transform: uppercase; letter-spacing: 0.18em; font-size: 0.72rem;
font-weight: 600; color: var(--muted); margin: 0 0 10px;
}
.eyebrow--amber { color: var(--amber-d); }
.badge {
display: inline-flex; align-items: center; gap: 6px;
font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
padding: 4px 10px; border-radius: 999px; text-transform: uppercase;
border: 1px solid transparent; white-space: nowrap;
}
.badge--sale { background: var(--navy); color: #fff; }
.badge--lease { background: var(--amber-50); color: var(--amber-d); border-color: var(--line-amber); }
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .85; }
/* ---------- photo (simulated imagery) ---------- */
.photo {
position: relative; border-radius: var(--r-md); overflow: hidden;
aspect-ratio: 16 / 10; box-shadow: var(--sh-md);
}
.photo__tag {
position: absolute; top: 14px; left: 14px;
font-size: 0.72rem; font-weight: 600; letter-spacing: 0.04em;
color: #fff; background: rgba(20,32,44,0.55); backdrop-filter: blur(4px);
padding: 6px 12px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.18);
}
.photo--hero {
background:
radial-gradient(120% 80% at 80% 0%, rgba(224,161,60,0.30), transparent 55%),
radial-gradient(90% 90% at 10% 100%, rgba(29,43,58,0.85), transparent 60%),
linear-gradient(160deg, #4a5560 0%, #2c3a48 45%, #19232f 100%);
}
.photo--hero::after {
content: ""; position: absolute; inset: 0;
background:
repeating-linear-gradient(90deg, rgba(255,255,255,0.05) 0 2px, transparent 2px 46px),
repeating-linear-gradient(0deg, rgba(255,255,255,0.04) 0 2px, transparent 2px 38px),
linear-gradient(180deg, transparent 55%, rgba(20,32,44,0.55) 100%);
mix-blend-mode: screen;
}
/* gradient thumbnails per asset class (in table rows) */
.thumb {
width: 56px; height: 44px; border-radius: var(--r-sm); flex: 0 0 auto;
box-shadow: var(--sh-sm); position: relative; overflow: hidden;
}
.thumb::after {
content: ""; position: absolute; inset: 0;
background: repeating-linear-gradient(115deg, rgba(255,255,255,0.10) 0 2px, transparent 2px 12px);
}
.thumb--office { background: linear-gradient(150deg, #5b6772, #2f3b48); }
.thumb--retail { background: linear-gradient(150deg, #e0a13c, #8a5f1d); }
.thumb--industrial { background: linear-gradient(150deg, #3b454d, #1d2b3a); }
.thumb--land { background: linear-gradient(150deg, #6f7a5e, #3c4733); }
/* ============================================================
TOPBAR
============================================================ */
.topbar {
position: sticky; top: 0; z-index: 50;
background: rgba(244,245,246,0.86); backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.topbar__inner { display: flex; align-items: center; gap: 28px; height: 72px; }
.brand { display: inline-flex; align-items: center; gap: 12px; }
.brand__mark { display: inline-flex; gap: 3px; align-items: flex-end; height: 26px; }
.brand__mark span { width: 5px; border-radius: 2px; background: var(--navy); }
.brand__mark span:nth-child(1) { height: 14px; }
.brand__mark span:nth-child(2) { height: 26px; background: var(--amber); }
.brand__mark span:nth-child(3) { height: 19px; }
.brand__text { display: flex; flex-direction: column; line-height: 1; }
.brand__text strong { font-size: 1.02rem; letter-spacing: 0.16em; color: var(--navy); }
.brand__text em { font-style: normal; font-size: 0.64rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin-top: 4px; }
.topnav { display: flex; gap: 26px; margin-left: auto; }
.topnav a { font-size: 0.92rem; font-weight: 500; color: var(--ink-2); position: relative; padding: 6px 0; }
.topnav a::after {
content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 0;
background: var(--amber); transition: width .18s ease;
}
.topnav a:hover { color: var(--navy); }
.topnav a:hover::after { width: 100%; }
.topbar__actions { display: flex; align-items: center; gap: 10px; }
/* ============================================================
HERO
============================================================ */
.hero {
background:
radial-gradient(80% 120% at 100% 0%, rgba(224,161,60,0.10), transparent 60%),
linear-gradient(180deg, #fff 0%, var(--paper) 100%);
border-bottom: 1px solid var(--line);
}
.hero__inner {
display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 56px;
align-items: center; padding: 76px 24px 84px;
}
.hero__copy h1 { font-size: clamp(2.1rem, 4.2vw, 3.35rem); font-weight: 700; color: var(--navy); margin-bottom: 18px; }
.lede { font-size: 1.08rem; color: var(--ink-2); max-width: 52ch; }
.hero__stats { display: flex; gap: 36px; margin: 32px 0 30px; flex-wrap: wrap; }
.stat { display: flex; flex-direction: column; }
.stat__num { font-size: 1.7rem; font-weight: 700; color: var(--navy); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.stat__label { font-size: 0.8rem; color: var(--muted); margin-top: 2px; }
.hero__stats .stat + .stat { padding-left: 36px; border-left: 1px solid var(--line); }
.hero__cta { display: flex; gap: 14px; flex-wrap: wrap; }
/* hero right panel */
.hero__panel { display: grid; gap: 0; }
.hero__panel .photo--hero { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.hero__card {
background: var(--white); border: 1px solid var(--line); border-top: none;
border-radius: 0 0 var(--r-md) var(--r-md); padding: 22px 24px 24px;
box-shadow: var(--sh-lg); margin: 0; position: relative; z-index: 2;
}
.hero__card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.hero__card h2 { font-size: 1.3rem; color: var(--navy); }
.hero__card-sub { font-size: 0.86rem; color: var(--muted); margin: 6px 0 16px; }
.kv { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; margin: 0 0 20px; }
.kv > div { display: flex; flex-direction: column; gap: 2px; }
.kv dt { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
.kv dd { margin: 0; font-size: 1.02rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
/* ============================================================
SECTIONS
============================================================ */
.section { padding: 80px 0; }
.section--alt { background: linear-gradient(180deg, #fff, 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: 32px;
margin-bottom: 32px; flex-wrap: wrap;
}
.section__title { font-size: clamp(1.6rem, 3vw, 2.3rem); font-weight: 700; color: var(--navy); }
.section__desc { color: var(--ink-2); max-width: 40ch; font-size: 0.96rem; }
/* ---------- tabs ---------- */
.tabs {
display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 22px;
padding: 6px; background: var(--white); border: 1px solid var(--line);
border-radius: var(--r-md); width: fit-content; box-shadow: var(--sh-sm);
}
.tab {
border: none; background: transparent; color: var(--ink-2);
padding: 9px 18px; border-radius: var(--r-sm); font-weight: 600; font-size: 0.9rem;
transition: background .15s ease, color .15s ease;
}
.tab:hover { background: rgba(29,43,58,0.05); color: var(--navy); }
.tab.is-active { background: var(--navy); color: #fff; box-shadow: var(--sh-sm); }
/* ---------- listings table ---------- */
.table-card {
background: var(--white); border: 1px solid var(--line);
border-radius: var(--r-md); box-shadow: var(--sh-md); overflow: hidden;
}
.table-scroll { overflow-x: auto; }
.ltable { width: 100%; border-collapse: collapse; min-width: 760px; }
.ltable thead th {
text-align: left; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.07em;
color: var(--muted); font-weight: 700; padding: 16px 18px;
background: var(--panel); border-bottom: 1px solid var(--line); white-space: nowrap;
}
.ltable th.num, .ltable td.num { text-align: right; font-variant-numeric: tabular-nums; }
.ltable th.sortable { padding: 0; }
.ltable th.sortable button {
width: 100%; height: 100%; background: none; border: none; color: inherit; font: inherit;
display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px;
padding: 16px 18px; text-transform: uppercase; letter-spacing: 0.07em;
}
.ltable th.sortable:hover { background: rgba(29,43,58,0.04); color: var(--navy); }
.arrow { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; opacity: .3; }
.ltable th[aria-sort="ascending"] .arrow { border-bottom: 5px solid var(--amber-d); opacity: 1; }
.ltable th[aria-sort="descending"] .arrow { border-top: 5px solid var(--amber-d); opacity: 1; }
.ltable th[aria-sort="ascending"] button, .ltable th[aria-sort="descending"] button { color: var(--navy); }
.ltable tbody td { padding: 14px 18px; border-bottom: 1px solid var(--line); font-size: 0.92rem; color: var(--ink); vertical-align: middle; }
.ltable tbody tr:last-child td { border-bottom: none; }
.ltable tbody tr { transition: background .12s ease; }
.ltable tbody tr:hover { background: rgba(224,161,60,0.06); }
.prop { display: flex; align-items: center; gap: 14px; min-width: 220px; }
.prop__name { font-weight: 700; color: var(--navy); }
.prop__meta { font-size: 0.78rem; color: var(--muted); margin-top: 2px; }
.cell-cap { font-weight: 700; color: var(--amber-d); }
.cell-price { font-weight: 700; }
.row-cta {
font-size: 0.82rem; font-weight: 600; color: var(--navy);
border: 1px solid var(--line-2); background: var(--white);
padding: 7px 12px; border-radius: var(--r-sm); transition: all .14s ease; white-space: nowrap;
}
.row-cta:hover { background: var(--navy); color: #fff; border-color: var(--navy); }
.table-empty { padding: 40px; text-align: center; color: var(--muted); font-size: 0.95rem; }
/* ============================================================
INVESTMENT STRIP
============================================================ */
.strip {
background:
radial-gradient(70% 140% at 0% 0%, rgba(224,161,60,0.18), transparent 60%),
linear-gradient(120deg, var(--navy) 0%, var(--navy-d) 100%);
color: #fff;
}
.strip__inner { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; padding: 48px 24px; }
.strip__item { text-align: center; padding: 6px 12px; position: relative; }
.strip__item + .strip__item::before {
content: ""; position: absolute; left: 0; top: 14%; height: 72%; width: 1px;
background: rgba(255,255,255,0.14);
}
.strip__num { display: block; font-size: 2.1rem; font-weight: 700; color: var(--amber); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.strip__label { display: block; font-size: 0.82rem; color: rgba(255,255,255,0.72); margin-top: 4px; }
/* ============================================================
SERVICES
============================================================ */
.svc-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.svc {
background: var(--white); border: 1px solid var(--line); border-radius: var(--r-md);
padding: 26px 24px; box-shadow: var(--sh-sm);
transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.svc:hover { transform: translateY(-3px); box-shadow: var(--sh-md); border-color: var(--line-amber); }
.svc__icon {
width: 46px; height: 46px; border-radius: var(--r-sm); display: grid; place-items: center;
font-size: 1.4rem; color: var(--amber-d); background: var(--amber-50);
border: 1px solid var(--line-amber); margin-bottom: 16px;
}
.svc h3 { font-size: 1.12rem; color: var(--navy); margin-bottom: 8px; }
.svc p { font-size: 0.9rem; color: var(--ink-2); }
/* ============================================================
CTA
============================================================ */
.cta {
background:
radial-gradient(60% 120% at 100% 0%, rgba(224,161,60,0.12), transparent 60%),
linear-gradient(160deg, var(--steel) 0%, var(--navy) 60%, var(--navy-d) 100%);
color: #fff; padding: 80px 0;
}
.cta__inner { display: grid; grid-template-columns: 1fr 0.95fr; gap: 56px; align-items: center; }
.cta__copy .eyebrow--amber { color: var(--amber); }
.cta__copy h2 { font-size: clamp(1.7rem, 3vw, 2.5rem); font-weight: 700; margin-bottom: 14px; }
.cta__copy > p { color: rgba(255,255,255,0.82); max-width: 46ch; }
.cta__list { list-style: none; margin: 22px 0 0; padding: 0; display: grid; gap: 10px; }
.cta__list li { position: relative; padding-left: 26px; color: rgba(255,255,255,0.9); font-size: 0.94rem; }
.cta__list li::before {
content: "✓"; position: absolute; left: 0; top: 0; color: var(--amber); font-weight: 700;
}
.cta__form {
background: var(--white); color: var(--ink); border-radius: var(--r-lg);
padding: 28px; box-shadow: var(--sh-lg); display: grid; gap: 14px;
}
.field { display: grid; gap: 6px; }
.field label { font-size: 0.82rem; font-weight: 600; color: var(--ink-2); }
.field input, .field select {
width: 100%; padding: 12px 14px; border: 1px solid var(--line-2); border-radius: var(--r-sm);
font: inherit; color: var(--ink); background: var(--white); transition: border-color .14s ease, box-shadow .14s ease;
}
.field input:focus, .field select:focus { outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px rgba(224,161,60,0.22); }
.field input.invalid { border-color: var(--danger); box-shadow: 0 0 0 3px rgba(196,80,62,0.18); }
.cta__fineprint { font-size: 0.74rem; color: var(--muted); text-align: center; margin-top: 2px; }
/* ============================================================
FOOTER
============================================================ */
.footer { background: var(--navy-d); color: rgba(255,255,255,0.78); padding: 56px 0 26px; }
.footer__inner { display: grid; grid-template-columns: 1.2fr 1.8fr; gap: 40px; }
.footer__brand .brand__text strong { color: #fff; }
.footer__brand .brand__text em { color: var(--amber); }
.footer__brand p { margin-top: 14px; font-size: 0.86rem; max-width: 38ch; color: rgba(255,255,255,0.6); }
.footer__cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.footer__cols h4 { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--amber); margin-bottom: 12px; }
.footer__cols a { display: block; font-size: 0.9rem; color: rgba(255,255,255,0.72); padding: 5px 0; transition: color .14s ease; }
.footer__cols a:hover { color: #fff; }
.footer__bottom {
display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
margin-top: 40px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.12);
font-size: 0.78rem; color: rgba(255,255,255,0.5);
}
/* ============================================================
TOAST
============================================================ */
.toast-region { position: fixed; bottom: 24px; right: 24px; z-index: 200; display: grid; gap: 10px; }
.toast {
background: var(--navy-d); color: #fff; padding: 13px 18px; border-radius: var(--r-sm);
box-shadow: var(--sh-lg); font-size: 0.9rem; font-weight: 500;
border-left: 3px solid var(--amber); min-width: 240px; max-width: 340px;
transform: translateY(10px); opacity: 0; transition: transform .22s ease, opacity .22s ease;
}
.toast.show { transform: translateY(0); opacity: 1; }
/* ============================================================
RESPONSIVE
============================================================ */
@media (max-width: 940px) {
.hero__inner, .cta__inner { grid-template-columns: 1fr; gap: 40px; }
.hero__panel { max-width: 480px; }
.svc-grid { grid-template-columns: repeat(2, 1fr); }
.footer__inner { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
.topnav, .topbar__actions .btn--ghost { display: none; }
.strip__inner { grid-template-columns: repeat(2, 1fr); gap: 28px 16px; }
.strip__item:nth-child(3)::before, .strip__item:nth-child(odd)::before { display: none; }
}
@media (max-width: 520px) {
.wrap { padding: 0 18px; }
.hero__inner { padding: 48px 18px 56px; }
.section { padding: 56px 0; }
.hero__stats { gap: 22px; }
.hero__stats .stat + .stat { padding-left: 22px; }
.stat__num { font-size: 1.45rem; }
.hero__cta .btn, .hero__cta { width: 100%; }
.hero__cta .btn { width: 100%; }
.svc-grid { grid-template-columns: 1fr; }
.footer__cols { grid-template-columns: 1fr 1fr; }
.tabs { width: 100%; }
.tab { flex: 1 1 auto; text-align: center; padding: 9px 10px; }
.section__head { flex-direction: column; align-items: flex-start; gap: 14px; }
.toast-region { left: 18px; right: 18px; bottom: 18px; }
.toast { max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after { transition: none !important; animation: none !important; }
}/* ============================================================
Meridian Commercial — landing interactions (vanilla JS)
- Asset-class tab filtering
- Sortable listings table (price / cap / NOI / $-per-SF)
- Animated hero counters
- Demo request-OM form with validation + toast
============================================================ */
(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastRegion = document.getElementById("toast-region");
function toast(msg) {
if (!toastRegion) return;
var el = document.createElement("div");
el.className = "toast";
el.textContent = msg;
toastRegion.appendChild(el);
requestAnimationFrame(function () { el.classList.add("show"); });
setTimeout(function () {
el.classList.remove("show");
setTimeout(function () { el.remove(); }, 280);
}, 3200);
}
/* ---------- listing dataset (fictional) ---------- */
var LISTINGS = [
{ name: "Cascade Logistics Park", meta: "412,000 SF · Reno, NV", cls: "industrial", price: 74500000, cap: 6.35, noi: 4730000, psf: 181, deal: "sale" },
{ name: "Northgate Office Tower", meta: "286,400 SF · Austin, TX", cls: "office", price: 119000000, cap: 5.85, noi: 6961500, psf: 416, deal: "sale" },
{ name: "Harbor Point Retail Center", meta: "98,200 SF · Tampa, FL", cls: "retail", price: 41200000, cap: 6.90, noi: 2842800, psf: 420, deal: "lease" },
{ name: "Ironworks Distribution", meta: "540,000 SF · Columbus, OH", cls: "industrial", price: 88300000, cap: 6.10, noi: 5386300, psf: 164, deal: "sale" },
{ name: "Mesa Crossing Power Center", meta: "212,500 SF · Phoenix, AZ", cls: "retail", price: 63750000, cap: 6.45, noi: 4111875, psf: 300, deal: "sale" },
{ name: "Lakeshore Corporate Plaza", meta: "174,000 SF · Chicago, IL", cls: "office", price: 52000000, cap: 7.20, noi: 3744000, psf: 299, deal: "lease" },
{ name: "Cypress Industrial Land", meta: "38.4 acres · Houston, TX", cls: "land", price: 18900000, cap: 0, noi: 0, psf: 11, deal: "sale" },
{ name: "Brightline Flex Campus", meta: "126,800 SF · Denver, CO", cls: "industrial", price: 33600000, cap: 6.75, noi: 2268000, psf: 265, deal: "lease" },
{ name: "Summit Medical Office", meta: "92,300 SF · Nashville, TN", cls: "office", price: 47800000, cap: 6.05, noi: 2891900, psf: 518, deal: "sale" },
{ name: "Riverbend Development Site", meta: "61.0 acres · Sacramento, CA", cls: "land", price: 27400000, cap: 0, noi: 0, psf: 10, deal: "sale" }
];
var body = document.getElementById("listing-body");
var emptyMsg = document.getElementById("table-empty");
var state = { cls: "all", sortKey: null, sortDir: "desc" };
/* ---------- formatters ---------- */
function fmtMoney(n) {
if (n >= 1000000) return "$" + (n / 1000000).toFixed(n % 1000000 === 0 ? 0 : 1) + "M";
return "$" + n.toLocaleString("en-US");
}
function fmtCap(n) { return n > 0 ? n.toFixed(2) + "%" : "—"; }
function fmtNoi(n) { return n > 0 ? fmtMoney(n) : "—"; }
function fmtPsf(n) { return "$" + n; }
function classLabel(c) { return c.charAt(0).toUpperCase() + c.slice(1); }
/* ---------- render ---------- */
function visibleRows() {
var rows = LISTINGS.filter(function (r) { return state.cls === "all" || r.cls === state.cls; });
if (state.sortKey) {
var dir = state.sortDir === "asc" ? 1 : -1;
rows.sort(function (a, b) { return (a[state.sortKey] - b[state.sortKey]) * dir; });
}
return rows;
}
function render() {
var rows = visibleRows();
body.innerHTML = "";
if (!rows.length) {
emptyMsg.hidden = false;
return;
}
emptyMsg.hidden = true;
rows.forEach(function (r) {
var tr = document.createElement("tr");
var dealBadge = r.deal === "sale"
? '<span class="badge badge--sale">For Sale</span>'
: '<span class="badge badge--lease">For Lease</span>';
tr.innerHTML =
'<td>' +
'<div class="prop">' +
'<span class="thumb thumb--' + r.cls + '" aria-hidden="true"></span>' +
'<span>' +
'<span class="prop__name">' + r.name + '</span>' +
'<span class="prop__meta">' + classLabel(r.cls) + ' · ' + r.meta + '</span>' +
'</span>' +
'</div>' +
'</td>' +
'<td class="num cell-price">' + fmtMoney(r.price) + '</td>' +
'<td class="num cell-cap">' + fmtCap(r.cap) + '</td>' +
'<td class="num">' + fmtNoi(r.noi) + '</td>' +
'<td class="num">' + fmtPsf(r.psf) + '</td>' +
'<td>' + dealBadge + '</td>' +
'<td><button class="row-cta" type="button" data-prop="' + r.name + '">Request OM</button></td>';
body.appendChild(tr);
});
}
/* ---------- tabs ---------- */
var tabs = Array.prototype.slice.call(document.querySelectorAll(".tab"));
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
tabs.forEach(function (t) {
t.classList.remove("is-active");
t.setAttribute("aria-selected", "false");
});
tab.classList.add("is-active");
tab.setAttribute("aria-selected", "true");
state.cls = tab.getAttribute("data-class");
render();
var n = visibleRows().length;
var label = state.cls === "all" ? "all asset classes" : classLabel(state.cls);
toast("Showing " + n + " listing" + (n === 1 ? "" : "s") + " · " + label);
});
});
/* ---------- sorting ---------- */
var headers = Array.prototype.slice.call(document.querySelectorAll("th.sortable"));
headers.forEach(function (th) {
var key = th.getAttribute("data-sort");
th.querySelector("button").addEventListener("click", function () {
if (state.sortKey === key) {
state.sortDir = state.sortDir === "asc" ? "desc" : "asc";
} else {
state.sortKey = key;
state.sortDir = "desc";
}
headers.forEach(function (h) { h.setAttribute("aria-sort", "none"); });
th.setAttribute("aria-sort", state.sortDir === "asc" ? "ascending" : "descending");
render();
var labels = { price: "price", cap: "cap rate", noi: "NOI", psf: "price per SF" };
toast("Sorted by " + labels[key] + " · " + (state.sortDir === "asc" ? "low to high" : "high to low"));
});
});
/* ---------- row CTA (event delegation) ---------- */
body.addEventListener("click", function (e) {
var btn = e.target.closest(".row-cta");
if (!btn) return;
toast("OM requested for " + btn.getAttribute("data-prop") + " — a broker will follow up.");
var form = document.getElementById("om-form");
if (form) form.scrollIntoView({ behavior: "smooth", block: "center" });
});
/* ---------- hero counters ---------- */
function animateCount(el) {
var target = parseFloat(el.getAttribute("data-count"));
var prefix = el.getAttribute("data-prefix") || "";
var suffix = el.getAttribute("data-suffix") || "";
var decimals = (el.getAttribute("data-count").indexOf(".") > -1) ? 1 : 0;
var dur = 1100, start = null;
function step(ts) {
if (!start) start = ts;
var p = Math.min((ts - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
var val = target * eased;
var shown = decimals ? val.toFixed(decimals) : Math.round(val).toLocaleString("en-US");
el.textContent = prefix + shown + suffix;
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
var counters = document.querySelectorAll(".stat__num[data-count]");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(function (entries, obs) {
entries.forEach(function (en) {
if (en.isIntersecting) { animateCount(en.target); obs.unobserve(en.target); }
});
}, { threshold: 0.5 });
counters.forEach(function (c) { io.observe(c); });
} else {
counters.forEach(animateCount);
}
/* ---------- request-OM form ---------- */
var form = document.getElementById("om-form");
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = form.elements["name"];
var email = form.elements["email"];
var ok = true;
[name, email].forEach(function (f) { f.classList.remove("invalid"); });
if (!name.value.trim()) { name.classList.add("invalid"); ok = false; }
var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRe.test(email.value.trim())) { email.classList.add("invalid"); ok = false; }
if (!ok) {
toast("Please add your name and a valid work email.");
var firstBad = form.querySelector(".invalid");
if (firstBad) firstBad.focus();
return;
}
var cls = form.elements["assetClass"].value;
toast("Request received — " + classLabel(cls) + " offering memoranda are on the way.");
form.reset();
});
}
/* ---------- init ---------- */
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Meridian Commercial — Office, Retail & Industrial Investment Sales</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@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#listings">Skip to listings</a>
<!-- ================= HEADER ================= -->
<header class="topbar">
<div class="wrap topbar__inner">
<a class="brand" href="#" aria-label="Meridian Commercial home">
<span class="brand__mark" aria-hidden="true">
<span></span><span></span><span></span>
</span>
<span class="brand__text">
<strong>MERIDIAN</strong>
<em>Commercial · Industrial</em>
</span>
</a>
<nav class="topnav" aria-label="Primary">
<a href="#listings">Properties</a>
<a href="#highlights">Investment</a>
<a href="#services">Services</a>
<a href="#cta">Request OM</a>
</nav>
<div class="topbar__actions">
<a class="btn btn--ghost" href="#cta">Broker login</a>
<a class="btn btn--amber" href="#cta">List an asset</a>
</div>
</div>
</header>
<!-- ================= HERO ================= -->
<section class="hero">
<div class="wrap hero__inner">
<div class="hero__copy">
<p class="eyebrow">Institutional-grade brokerage · Est. 1998</p>
<h1>Commercial & industrial assets, underwritten with precision.</h1>
<p class="lede">
Meridian places office, retail, industrial, and land assets with qualified
principals nationwide. Transparent cap rates, verified NOI, and full offering
memoranda — built for decision-makers.
</p>
<div class="hero__stats" aria-label="Firm performance">
<div class="stat">
<span class="stat__num" data-count="14.2" data-suffix="B" data-prefix="$">$0</span>
<span class="stat__label">Transacted volume</span>
</div>
<div class="stat">
<span class="stat__num" data-count="1840" data-suffix="">0</span>
<span class="stat__label">Assets sold</span>
</div>
<div class="stat">
<span class="stat__num" data-count="38" data-suffix="">0</span>
<span class="stat__label">Markets covered</span>
</div>
</div>
<div class="hero__cta">
<a class="btn btn--amber btn--lg" href="#listings">Browse inventory</a>
<a class="btn btn--outline btn--lg" href="#cta">Speak to a broker</a>
</div>
</div>
<aside class="hero__panel" aria-label="Featured asset">
<div class="photo photo--hero" role="img" aria-label="Aerial view of a steel-and-glass distribution campus at dusk">
<span class="photo__tag">Featured · Sale-leaseback</span>
</div>
<div class="hero__card">
<div class="hero__card-head">
<h2>Cascade Logistics Park</h2>
<span class="badge badge--sale">For Sale</span>
</div>
<p class="hero__card-sub">412,000 SF · Class A Industrial · Reno, NV</p>
<dl class="kv">
<div><dt>Asking</dt><dd>$74,500,000</dd></div>
<div><dt>Cap rate</dt><dd>6.35%</dd></div>
<div><dt>NOI</dt><dd>$4.73M</dd></div>
<div><dt>$ / SF</dt><dd>$181</dd></div>
</dl>
<a class="btn btn--navy btn--block" href="#cta">Request offering memorandum</a>
</div>
</aside>
</div>
</section>
<!-- ================= LISTINGS ================= -->
<section class="section" id="listings">
<div class="wrap">
<div class="section__head">
<div>
<p class="eyebrow eyebrow--amber">Available inventory</p>
<h2 class="section__title">Investment opportunities</h2>
</div>
<p class="section__desc">
Filter by asset class and sort the table by the metrics that drive your
underwriting. All figures are illustrative.
</p>
</div>
<!-- Asset-class tabs -->
<div class="tabs" role="tablist" aria-label="Asset class">
<button class="tab is-active" role="tab" aria-selected="true" data-class="all" id="tab-all" aria-controls="listing-table">All assets</button>
<button class="tab" role="tab" aria-selected="false" data-class="office" id="tab-office" aria-controls="listing-table">Office</button>
<button class="tab" role="tab" aria-selected="false" data-class="retail" id="tab-retail" aria-controls="listing-table">Retail</button>
<button class="tab" role="tab" aria-selected="false" data-class="industrial" id="tab-industrial" aria-controls="listing-table">Industrial</button>
<button class="tab" role="tab" aria-selected="false" data-class="land" id="tab-land" aria-controls="listing-table">Land</button>
</div>
<!-- Listings table -->
<div class="table-card">
<div class="table-scroll">
<table class="ltable" id="listing-table" aria-live="polite">
<caption class="sr-only">Commercial property listings, sortable by column</caption>
<thead>
<tr>
<th scope="col">Property</th>
<th scope="col" class="num sortable" data-sort="price" aria-sort="none">
<button type="button">Price <span class="arrow" aria-hidden="true"></span></button>
</th>
<th scope="col" class="num sortable" data-sort="cap" aria-sort="none">
<button type="button">Cap rate <span class="arrow" aria-hidden="true"></span></button>
</th>
<th scope="col" class="num sortable" data-sort="noi" aria-sort="none">
<button type="button">NOI <span class="arrow" aria-hidden="true"></span></button>
</th>
<th scope="col" class="num sortable" data-sort="psf" aria-sort="none">
<button type="button">$ / SF <span class="arrow" aria-hidden="true"></span></button>
</th>
<th scope="col">Status</th>
<th scope="col"><span class="sr-only">Action</span></th>
</tr>
</thead>
<tbody id="listing-body"><!-- rows injected by script.js --></tbody>
</table>
</div>
<p class="table-empty" id="table-empty" hidden>No assets match this filter.</p>
</div>
</div>
</section>
<!-- ================= INVESTMENT HIGHLIGHTS ================= -->
<section class="strip" id="highlights" aria-label="Investment highlights">
<div class="wrap strip__inner">
<div class="strip__item">
<span class="strip__num">6.1%</span>
<span class="strip__label">Avg. going-in cap rate</span>
</div>
<div class="strip__item">
<span class="strip__num">92%</span>
<span class="strip__label">Portfolio occupancy</span>
</div>
<div class="strip__item">
<span class="strip__num">8.4 yr</span>
<span class="strip__label">Weighted lease term (WALT)</span>
</div>
<div class="strip__item">
<span class="strip__num">$181</span>
<span class="strip__label">Blended price per SF</span>
</div>
</div>
</section>
<!-- ================= BROKERAGE SERVICES ================= -->
<section class="section section--alt" id="services">
<div class="wrap">
<div class="section__head">
<div>
<p class="eyebrow eyebrow--amber">What we do</p>
<h2 class="section__title">Full-service brokerage</h2>
</div>
<p class="section__desc">
An integrated team across capital markets, leasing, and advisory — aligned to
your hold strategy.
</p>
</div>
<div class="svc-grid">
<article class="svc">
<div class="svc__icon" aria-hidden="true">◫</div>
<h3>Investment sales</h3>
<p>Disposition and acquisition of stabilized and value-add assets with
institutional-grade marketing and a vetted buyer pool.</p>
</article>
<article class="svc">
<div class="svc__icon" aria-hidden="true">⌗</div>
<h3>Landlord leasing</h3>
<p>Lease-up strategy, tenant prospecting, and rent benchmarking to push NOI and
shorten downtime between tenancies.</p>
</article>
<article class="svc">
<div class="svc__icon" aria-hidden="true">⊞</div>
<h3>Tenant representation</h3>
<p>Site selection, comp analysis, and negotiation across office, retail, and
industrial requirements nationwide.</p>
</article>
<article class="svc">
<div class="svc__icon" aria-hidden="true">◷</div>
<h3>Capital markets</h3>
<p>Debt placement, recapitalization, and sale-leaseback structuring with our
lending and equity relationships.</p>
</article>
<article class="svc">
<div class="svc__icon" aria-hidden="true">▤</div>
<h3>Valuation & advisory</h3>
<p>Broker opinions of value, underwriting models, and hold-sell analysis to time
your exit with conviction.</p>
</article>
<article class="svc">
<div class="svc__icon" aria-hidden="true">◇</div>
<h3>Industrial & land</h3>
<p>Distribution, manufacturing, and entitled land — from infill cold storage to
speculative big-box development sites.</p>
</article>
</div>
</div>
</section>
<!-- ================= CTA / REQUEST OM ================= -->
<section class="cta" id="cta">
<div class="wrap cta__inner">
<div class="cta__copy">
<p class="eyebrow eyebrow--amber">Confidential</p>
<h2>Request an offering memorandum</h2>
<p>Tell us the asset class and check size you're targeting. A Meridian broker will
return a curated pipeline and full OMs within one business day.</p>
<ul class="cta__list">
<li>Verified rent rolls & T-12 statements</li>
<li>Cap-rate & NOI underwriting support</li>
<li>Off-market and pre-marketing access</li>
</ul>
</div>
<form class="cta__form" id="om-form" novalidate>
<div class="field">
<label for="om-name">Full name</label>
<input id="om-name" name="name" type="text" autocomplete="name" required placeholder="Jordan Avery" />
</div>
<div class="field">
<label for="om-email">Work email</label>
<input id="om-email" name="email" type="email" autocomplete="email" required placeholder="[email protected]" />
</div>
<div class="field">
<label for="om-class">Asset class of interest</label>
<select id="om-class" name="assetClass">
<option value="office">Office</option>
<option value="retail">Retail</option>
<option value="industrial" selected>Industrial</option>
<option value="land">Land</option>
<option value="mixed">Mixed / opportunistic</option>
</select>
</div>
<div class="field">
<label for="om-size">Target check size</label>
<select id="om-size" name="checkSize">
<option>$1M – $5M</option>
<option selected>$5M – $25M</option>
<option>$25M – $75M</option>
<option>$75M+</option>
</select>
</div>
<button class="btn btn--amber btn--block btn--lg" type="submit">Request offering memorandum</button>
<p class="cta__fineprint">By submitting you agree to be contacted by a licensed broker. Demo form — nothing is sent.</p>
</form>
</div>
</section>
<!-- ================= FOOTER ================= -->
<footer class="footer">
<div class="wrap footer__inner">
<div class="footer__brand">
<span class="brand__text">
<strong>MERIDIAN</strong>
<em>Commercial · Industrial</em>
</span>
<p>Institutional commercial real-estate brokerage. Illustrative demo — fictional firm.</p>
</div>
<nav class="footer__cols" aria-label="Footer">
<div>
<h4>Assets</h4>
<a href="#listings">Office</a>
<a href="#listings">Retail</a>
<a href="#listings">Industrial</a>
<a href="#listings">Land</a>
</div>
<div>
<h4>Firm</h4>
<a href="#services">Services</a>
<a href="#highlights">Investment</a>
<a href="#cta">Contact</a>
</div>
<div>
<h4>Legal</h4>
<a href="#">Disclosures</a>
<a href="#">Privacy</a>
<a href="#">Terms</a>
</div>
</nav>
</div>
<div class="wrap footer__bottom">
<span>© 1998–2026 Meridian Commercial (fictional). All rights reserved.</span>
<span>Equal opportunity advisory.</span>
</div>
</footer>
<!-- Toast region -->
<div class="toast-region" id="toast-region" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Commercial / Industrial Landing
An authoritative, B2B-flavored landing page for Meridian, a fictional commercial and industrial real-estate brokerage. The steel-and-navy palette with amber accents leans structured and no-nonsense: a hero pairs animated transacted-volume counters with a featured Class A industrial sale-leaseback card showing asking price, cap rate, NOI, and price per square foot. Simulated architectural “photography” is rendered entirely from layered CSS gradients — warm dusk light over a steel-and-glass distribution campus.
The core of the page is a sortable listings table fed from a fictional dataset of office,
retail, industrial, and land assets. Asset-class tabs above the table filter the rows in
place, and every numeric column — price, cap rate, NOI, and $/SF — is click-to-sort with an
accessible aria-sort state and a toggling ascending/descending arrow. For-sale and
for-lease badges, gradient asset thumbnails, and per-row “Request OM” buttons keep the data
scannable. Below it sit an investment-highlights strip, a brokerage-services grid, and a
request-an-offering-memorandum form with inline validation.
Everything is self-contained vanilla JavaScript with no build step or external libraries —
just one Google Font (Inter), a single stylesheet, and one script. The form validates name
and email before firing a confirmation toast, hero counters animate on scroll via
IntersectionObserver, and a prefers-reduced-motion block disables transitions and
animations for visitors who ask for less movement.
Illustrative UI only — sample listings and data are fictional; not a real real-estate service.