D2C — Fashion / Apparel Landing
A full editorial direct-to-consumer landing page for a fictional limited-run apparel label. White-and-black canvas lifted by a single terracotta accent, a couture serif paired with a clean sans, and an aspirational, minimal mood. Includes a full-bleed hero with a running marquee, a masonry lookbook with hover styling notes, an interactive product configurator with colour swatches and size picker, a fabric-quality story, dark customer reviews, bundle pricing, a drop waitlist, a sticky add-to-cart bar, scroll-reveal, and a responsive mobile nav.
MCP
Code
/* ===== Maison Écru — D2C Fashion Landing ===== */
:root {
--ink: #131210;
--ink-soft: #4a4742;
--ink-mute: #8a857c;
--paper: #fbfaf7;
--paper-2: #f3f0e9;
--line: #e4e0d6;
--accent: #b3411f; /* editorial terracotta */
--accent-deep: #8c2f15;
--white: #ffffff;
--shadow: 0 24px 60px -28px rgba(19, 18, 16, 0.35);
--shadow-sm: 0 8px 24px -14px rgba(19, 18, 16, 0.3);
--serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
--sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--r: 4px;
--maxw: 1200px;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--sans);
font-weight: 400;
color: var(--ink);
background: var(--paper);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
img { max-width: 100%; display: block; }
h1, h2, h3, h4 {
font-family: var(--serif);
font-weight: 500;
line-height: 1.05;
letter-spacing: -0.01em;
margin: 0;
}
h1 em, h2 em { font-style: italic; color: var(--accent); }
a { color: inherit; text-decoration: none; }
.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;
}
.eyebrow {
font-family: var(--sans);
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent);
margin: 0 0 1rem;
}
.eyebrow--light { color: rgba(255,255,255,0.7); }
/* ===== Buttons ===== */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
font-family: var(--sans);
font-size: 0.86rem;
font-weight: 500;
letter-spacing: 0.02em;
padding: 0.95rem 1.7rem;
border: 1px solid transparent;
border-radius: var(--r);
cursor: pointer;
transition: transform 0.2s ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn-dark { background: var(--ink); color: var(--paper); }
.btn-dark:hover { background: var(--accent-deep); box-shadow: var(--shadow-sm); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-ghost:hover { background: var(--ink); color: var(--paper); }
.btn-light { background: var(--paper); color: var(--ink); }
.btn-light:hover { background: var(--accent); color: var(--white); }
.btn-sm { padding: 0.6rem 1.1rem; font-size: 0.8rem; }
.btn-block { width: 100%; }
/* ===== Announcement ===== */
.announce {
background: var(--ink);
color: var(--paper);
text-align: center;
font-size: 0.76rem;
letter-spacing: 0.06em;
padding: 0.55rem 1rem;
}
.announce strong { color: #e7c9b9; font-weight: 600; }
/* ===== Nav ===== */
.nav {
position: sticky;
top: 0;
z-index: 50;
background: rgba(251, 250, 247, 0.82);
backdrop-filter: blur(12px);
border-bottom: 1px solid transparent;
transition: border-color 0.3s ease, background 0.3s ease;
}
.nav.scrolled { border-color: var(--line); }
.nav-inner {
max-width: var(--maxw);
margin: 0 auto;
padding: 1rem 1.5rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.logo {
font-family: var(--serif);
font-size: 1.35rem;
font-weight: 600;
letter-spacing: 0.12em;
}
.logo span { color: var(--accent); font-style: italic; }
.logo--light { color: var(--paper); }
.logo--light span { color: #e7c9b9; }
.nav-links { display: flex; gap: 1.8rem; }
.nav-links a {
font-size: 0.85rem;
font-weight: 500;
color: var(--ink-soft);
position: relative;
padding: 0.2rem 0;
}
.nav-links a::after {
content: "";
position: absolute;
left: 0; bottom: -2px;
width: 0; height: 1px;
background: var(--accent);
transition: width 0.25s ease;
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after { width: 100%; }
.nav-cta { display: flex; align-items: center; gap: 0.8rem; }
.menu-toggle {
display: none;
flex-direction: column;
gap: 5px;
background: none; border: none; cursor: pointer;
padding: 6px;
}
.menu-toggle span {
width: 24px; height: 2px; background: var(--ink);
transition: transform 0.3s ease, opacity 0.3s ease;
}
.menu-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.menu-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
/* ===== Hero ===== */
.hero { padding: 4.5rem 1.5rem 0; }
.hero-grid {
max-width: var(--maxw);
margin: 0 auto;
display: grid;
grid-template-columns: 1.05fr 1fr;
gap: 3.5rem;
align-items: center;
}
.hero-copy h1 {
font-size: clamp(3rem, 7vw, 5.6rem);
margin: 0 0 1.4rem;
}
.lead {
font-size: 1.12rem;
color: var(--ink-soft);
max-width: 30rem;
margin: 0 0 2.2rem;
}
.hero-actions { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 2.6rem; }
.hero-meta {
display: flex; gap: 2.4rem;
list-style: none; padding: 0; margin: 0;
border-top: 1px solid var(--line);
padding-top: 1.4rem;
}
.hero-meta li { font-size: 0.85rem; color: var(--ink-mute); }
.hero-meta strong {
display: block;
font-family: var(--serif);
font-size: 1.9rem;
font-weight: 600;
color: var(--ink);
line-height: 1;
margin-bottom: 0.2rem;
}
.hero-frame { position: relative; }
.hero-photo {
border-radius: var(--r);
box-shadow: var(--shadow);
background-size: cover;
background-position: center;
}
.hero-photo--main {
aspect-ratio: 4 / 5;
background:
linear-gradient(150deg, rgba(179,65,31,0.12), rgba(19,18,16,0.04)),
radial-gradient(120% 90% at 70% 10%, #efe7dc 0%, #d8cdbd 55%, #b9a890 100%);
position: relative;
}
.hero-photo--main::after {
content: "";
position: absolute; inset: 12% 18% 20% 16%;
border-radius: 60% 60% 4% 4% / 24% 24% 4% 4%;
background: linear-gradient(180deg, #e8ddcd, #cdbfa6 70%, #b8a685);
box-shadow: inset 0 -20px 40px rgba(0,0,0,0.08);
}
.hero-photo--accent {
position: absolute;
right: -1.4rem; bottom: -1.8rem;
width: 42%;
aspect-ratio: 1 / 1;
background:
linear-gradient(135deg, var(--accent), var(--accent-deep));
border: 5px solid var(--paper);
}
.hero-tag {
position: absolute;
left: -1rem; top: 1.6rem;
background: var(--paper);
border: 1px solid var(--line);
box-shadow: var(--shadow-sm);
font-size: 0.78rem;
font-weight: 500;
padding: 0.6rem 1rem;
border-radius: var(--r);
}
/* ===== Marquee ===== */
.marquee {
margin-top: 4.5rem;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
overflow: hidden;
padding: 1rem 0;
}
.marquee-track {
display: inline-flex;
gap: 2.4rem;
white-space: nowrap;
animation: scroll 28s linear infinite;
font-size: 0.82rem;
font-weight: 600;
letter-spacing: 0.18em;
color: var(--ink-mute);
}
.marquee-track span:nth-child(even) { color: var(--accent); }
@keyframes scroll { to { transform: translateX(-50%); } }
/* ===== Press ===== */
.press {
max-width: var(--maxw);
margin: 0 auto;
padding: 3.5rem 1.5rem;
text-align: center;
}
.press-label {
font-size: 0.72rem; letter-spacing: 0.2em; text-transform: uppercase;
color: var(--ink-mute); margin: 0 0 1.4rem;
}
.press-logos {
display: flex; flex-wrap: wrap; justify-content: center;
gap: 2.8rem; list-style: none; padding: 0; margin: 0;
}
.press-logos li {
font-family: var(--serif);
font-size: 1.4rem;
font-weight: 500;
letter-spacing: 0.12em;
color: var(--ink-soft);
opacity: 0.6;
transition: opacity 0.25s ease;
}
.press-logos li:hover { opacity: 1; }
/* ===== Section heads ===== */
.section-head { max-width: 38rem; margin: 0 auto 3rem; text-align: center; }
.section-head h2 { font-size: clamp(2.2rem, 4.5vw, 3.4rem); }
.section-sub { color: var(--ink-soft); margin: 1rem 0 0; }
/* ===== Lookbook ===== */
.lookbook { max-width: var(--maxw); margin: 0 auto; padding: 4.5rem 1.5rem; }
.look-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 240px;
gap: 1.2rem;
}
.look { position: relative; margin: 0; overflow: hidden; border-radius: var(--r); cursor: pointer; }
.look--tall { grid-row: span 2; }
.look--wide { grid-column: span 2; }
.look-img {
position: absolute; inset: 0;
transition: transform 0.6s cubic-bezier(.2,.6,.2,1);
}
.look:hover .look-img, .look:focus .look-img { transform: scale(1.06); }
.look-1 { background: radial-gradient(120% 100% at 60% 20%, #ece3d6, #c3b49a 70%, #9c8b6f); }
.look-2 { background: radial-gradient(120% 100% at 40% 30%, #e7ddce, #b39d7e); }
.look-3 { background: radial-gradient(120% 100% at 50% 20%, #ded3c0, #a89a82); }
.look-4 { background: linear-gradient(120deg, #d9cdb8, #b9a888 60%, #8c2f15 180%); }
.look-5 { background: radial-gradient(120% 100% at 60% 30%, #efe9df, #cabfa9); }
.look figcaption {
position: absolute; left: 0; right: 0; bottom: 0;
padding: 1.4rem 1.2rem 1.1rem;
background: linear-gradient(180deg, transparent, rgba(19,18,16,0.78));
color: var(--paper);
transform: translateY(45%);
opacity: 0;
transition: transform 0.4s ease, opacity 0.4s ease;
}
.look:hover figcaption, .look:focus figcaption { transform: translateY(0); opacity: 1; }
.look-name { display: block; font-family: var(--serif); font-size: 1.25rem; }
.look-note { display: block; font-size: 0.82rem; color: rgba(255,255,255,0.78); margin-top: 0.2rem; }
/* ===== Product ===== */
.product { background: var(--paper-2); padding: 5rem 1.5rem; }
.product-grid {
max-width: var(--maxw);
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3.5rem;
align-items: center;
}
.product-photo {
aspect-ratio: 4 / 5;
border-radius: var(--r);
box-shadow: var(--shadow);
transition: background 0.5s ease;
background: radial-gradient(120% 90% at 60% 15%, #efe7da 0%, #d6c8b1 55%, #b6a486 100%);
}
.product-photo[data-color="charcoal"] { background: radial-gradient(120% 90% at 60% 15%, #5a5750, #36332e 60%, #211f1b); }
.product-photo[data-color="rust"] { background: radial-gradient(120% 90% at 60% 15%, #d97b53, var(--accent) 55%, var(--accent-deep)); }
.product-photo[data-color="sage"] { background: radial-gradient(120% 90% at 60% 15%, #c4cab2, #97a07f 60%, #6f7a5a); }
.product-thumbs { display: flex; gap: 0.7rem; margin-top: 0.9rem; }
.product-thumb {
width: 58px; height: 70px;
border-radius: 3px;
border: 1px solid var(--line);
cursor: pointer;
background-size: cover;
transition: outline 0.2s ease, transform 0.2s ease;
outline: 2px solid transparent;
outline-offset: 2px;
}
.product-thumb:hover { transform: translateY(-2px); }
.product-thumb[aria-current="true"] { outline-color: var(--ink); }
.product-info h2 { font-size: clamp(2.2rem, 4vw, 3rem); margin-bottom: 0.6rem; }
.product-price { font-family: var(--serif); font-size: 1.8rem; font-weight: 600; margin: 0 0 1.1rem; }
.product-price-was { font-size: 1.1rem; color: var(--ink-mute); text-decoration: line-through; margin-left: 0.5rem; font-weight: 400; }
.product-desc { color: var(--ink-soft); margin: 0 0 2rem; max-width: 32rem; }
.picker { display: grid; gap: 1.6rem; margin-bottom: 2rem; }
.picker-label { display: block; font-size: 0.8rem; letter-spacing: 0.04em; color: var(--ink-mute); text-transform: uppercase; margin-bottom: 0.7rem; }
.picker-label strong { color: var(--ink); font-weight: 600; text-transform: none; letter-spacing: 0; }
.swatches { display: flex; gap: 0.7rem; }
.swatch {
width: 34px; height: 34px;
border-radius: 50%;
border: 1px solid var(--line);
cursor: pointer;
position: relative;
outline: 2px solid transparent;
outline-offset: 3px;
transition: outline 0.2s ease;
}
.swatch[aria-checked="true"] { outline-color: var(--ink); }
.swatch:focus-visible { outline-color: var(--accent); }
.sizes { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.size {
min-width: 48px;
padding: 0.6rem 0.8rem;
border: 1px solid var(--line);
background: var(--paper);
border-radius: var(--r);
font-family: var(--sans);
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.size:hover { border-color: var(--ink); }
.size[aria-checked="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.size[disabled] { opacity: 0.35; cursor: not-allowed; text-decoration: line-through; }
.product-actions { display: grid; grid-template-columns: 1.6fr 1fr; gap: 0.8rem; margin-bottom: 1.6rem; }
.product-perks { display: flex; gap: 1.4rem; flex-wrap: wrap; list-style: none; padding: 0; margin: 0; font-size: 0.82rem; color: var(--ink-soft); }
/* ===== Fabric ===== */
.fabric { max-width: var(--maxw); margin: 0 auto; padding: 5rem 1.5rem; }
.fabric-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; }
.fabric-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r);
padding: 2.2rem 1.8rem;
transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}
.fabric-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-sm); border-color: var(--accent); }
.fabric-num { font-family: var(--serif); font-size: 2.6rem; color: var(--accent); font-style: italic; line-height: 1; }
.fabric-card h3 { font-size: 1.6rem; margin: 0.8rem 0 0.6rem; }
.fabric-card p { color: var(--ink-soft); font-size: 0.95rem; margin: 0; }
/* ===== Reviews ===== */
.reviews { background: var(--ink); color: var(--paper); padding: 5rem 1.5rem; }
.reviews .eyebrow { color: #e7c9b9; }
.reviews h2 { color: var(--paper); }
.review-grid { max-width: var(--maxw); margin: 0 auto; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; }
.review {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.1);
border-radius: var(--r);
padding: 2rem 1.8rem;
margin: 0;
transition: background 0.3s ease, transform 0.3s ease;
}
.review:hover { background: rgba(255,255,255,0.07); transform: translateY(-4px); }
.review .stars { color: #e7a06f; letter-spacing: 0.15em; margin: 0 0 1rem; }
.review p:not(.stars) { font-family: var(--serif); font-size: 1.25rem; line-height: 1.4; color: var(--paper); margin: 0 0 1.2rem; }
.review footer { font-size: 0.82rem; color: rgba(255,255,255,0.6); }
/* ===== Pricing ===== */
.pricing { max-width: var(--maxw); margin: 0 auto; padding: 5rem 1.5rem; }
.plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; align-items: stretch; }
.plan {
position: relative;
background: var(--white);
border: 1px solid var(--line);
border-radius: 6px;
padding: 2.4rem 1.9rem;
display: flex; flex-direction: column;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.plan:hover { transform: translateY(-5px); box-shadow: var(--shadow-sm); }
.plan--featured { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.plan--featured h3 { color: var(--paper); }
.plan-badge {
position: absolute; top: -0.8rem; left: 50%; transform: translateX(-50%);
background: var(--accent); color: var(--white);
font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase;
padding: 0.35rem 0.9rem; border-radius: 40px; font-weight: 600;
}
.plan h3 { font-size: 1.7rem; }
.plan-price { font-family: var(--serif); font-size: 2.4rem; font-weight: 600; margin: 0.6rem 0 0; }
.plan-was { font-size: 1.1rem; color: var(--ink-mute); text-decoration: line-through; font-weight: 400; margin-left: 0.4rem; }
.plan--featured .plan-was { color: rgba(255,255,255,0.5); }
.plan-desc { color: var(--ink-soft); font-size: 0.9rem; margin: 0.4rem 0 1.4rem; }
.plan--featured .plan-desc { color: rgba(255,255,255,0.7); }
.plan ul { list-style: none; padding: 0; margin: 0 0 1.8rem; display: grid; gap: 0.6rem; }
.plan li { font-size: 0.9rem; padding-left: 1.3rem; position: relative; }
.plan li::before { content: "✓"; position: absolute; left: 0; color: var(--accent); font-weight: 600; }
.plan--featured li::before { color: #e7a06f; }
.plan-cta { margin-top: auto; }
.plan--featured .btn-dark { background: var(--paper); color: var(--ink); }
.plan--featured .btn-dark:hover { background: var(--accent); color: var(--white); }
/* ===== Waitlist ===== */
.waitlist {
background:
radial-gradient(120% 120% at 80% -10%, rgba(179,65,31,0.5), transparent 60%),
var(--ink);
color: var(--paper);
padding: 5rem 1.5rem;
}
.waitlist-inner { max-width: 36rem; margin: 0 auto; text-align: center; }
.waitlist h2 { color: var(--paper); font-size: clamp(2.2rem, 5vw, 3.4rem); }
.waitlist p { color: rgba(255,255,255,0.78); margin: 1rem 0 2rem; }
.waitlist-form { display: flex; gap: 0.7rem; max-width: 28rem; margin: 0 auto; }
.waitlist-form input {
flex: 1;
padding: 0.95rem 1.1rem;
border: 1px solid rgba(255,255,255,0.25);
background: rgba(255,255,255,0.06);
color: var(--paper);
border-radius: var(--r);
font-family: var(--sans);
font-size: 0.9rem;
}
.waitlist-form input::placeholder { color: rgba(255,255,255,0.45); }
.waitlist-form input:focus { outline: none; border-color: #e7a06f; }
.waitlist-form input.invalid { border-color: #e57a4f; background: rgba(229,122,79,0.12); }
.waitlist-fine { font-size: 0.76rem; color: rgba(255,255,255,0.5); margin-top: 1.2rem; }
/* ===== Footer ===== */
.footer { background: #0e0d0b; color: var(--paper); padding: 4rem 1.5rem 2rem; }
.footer-grid {
max-width: var(--maxw);
margin: 0 auto;
display: grid;
grid-template-columns: 1.6fr 1fr 1fr 1fr;
gap: 2.5rem;
padding-bottom: 3rem;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.footer-brand p { color: rgba(255,255,255,0.6); font-size: 0.9rem; max-width: 22rem; margin: 1rem 0 0; }
.footer h4 { font-family: var(--sans); font-size: 0.78rem; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255,255,255,0.5); margin: 0 0 1.1rem; font-weight: 600; }
.footer nav a { display: block; font-size: 0.9rem; color: rgba(255,255,255,0.8); padding: 0.32rem 0; transition: color 0.2s ease; }
.footer nav a:hover { color: #e7a06f; }
.footer-base {
max-width: var(--maxw);
margin: 0 auto;
padding-top: 1.6rem;
display: flex; justify-content: space-between; align-items: center;
flex-wrap: wrap; gap: 1rem;
font-size: 0.8rem; color: rgba(255,255,255,0.5);
}
.footer-social { display: flex; gap: 1.4rem; }
.footer-social a:hover { color: #e7a06f; }
/* ===== Sticky cart ===== */
.sticky-cart {
position: fixed; left: 0; right: 0; bottom: 0;
z-index: 60;
background: rgba(251,250,247,0.96);
backdrop-filter: blur(12px);
border-top: 1px solid var(--line);
transform: translateY(120%);
transition: transform 0.35s cubic-bezier(.2,.7,.2,1);
}
.sticky-cart.show { transform: translateY(0); }
.sticky-cart-inner {
max-width: var(--maxw);
margin: 0 auto;
padding: 0.85rem 1.5rem;
display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.sticky-cart-name { font-family: var(--serif); font-size: 1.15rem; font-weight: 600; display: block; }
.sticky-cart-meta { font-size: 0.8rem; color: var(--ink-mute); }
/* ===== Toast ===== */
.toast {
position: fixed; left: 50%; bottom: 5.5rem;
transform: translate(-50%, 1.4rem);
background: var(--ink); color: var(--paper);
padding: 0.85rem 1.4rem;
border-radius: var(--r);
font-size: 0.86rem;
box-shadow: var(--shadow);
opacity: 0; pointer-events: none;
transition: opacity 0.3s ease, transform 0.3s ease;
z-index: 80;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ===== Reveal ===== */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.in { opacity: 1; transform: none; }
/* ===== Responsive ===== */
@media (max-width: 980px) {
.hero-grid, .product-grid { grid-template-columns: 1fr; gap: 2.5rem; }
.hero-visual { max-width: 26rem; }
.footer-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 720px) {
.nav-links {
position: absolute;
top: 100%; left: 0; right: 0;
flex-direction: column;
gap: 0;
background: var(--paper);
border-bottom: 1px solid var(--line);
padding: 0.5rem 1.5rem 1rem;
transform: translateY(-12px);
opacity: 0; pointer-events: none;
transition: transform 0.25s ease, opacity 0.25s ease;
}
.nav-links.open { transform: none; opacity: 1; pointer-events: auto; }
.nav-links a { padding: 0.8rem 0; border-bottom: 1px solid var(--line); }
.menu-toggle { display: flex; }
.nav-cta .btn-sm { display: none; }
.look-grid { grid-template-columns: 1fr 1fr; grid-auto-rows: 200px; }
.look--wide { grid-column: span 2; }
.fabric-grid, .review-grid, .plans { grid-template-columns: 1fr; }
.hero-meta { gap: 1.6rem; }
}
@media (max-width: 520px) {
.hero { padding-top: 2.8rem; }
.hero-actions { flex-direction: column; align-items: stretch; }
.hero-actions .btn { width: 100%; }
.look-grid { grid-template-columns: 1fr; }
.look--wide, .look--tall { grid-column: auto; grid-row: auto; }
.look figcaption { transform: none; opacity: 1; }
.product-actions { grid-template-columns: 1fr; }
.footer-grid { grid-template-columns: 1fr; }
.waitlist-form { flex-direction: column; }
.footer-base { flex-direction: column; align-items: flex-start; }
.press-logos { gap: 1.6rem; }
}
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
.reveal { opacity: 1; transform: none; }
}/* ===== Maison Écru — D2C Fashion Landing ===== */
(function () {
"use strict";
/* ---- Toast helper ---- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
/* ---- Mobile menu ---- */
var menuToggle = document.getElementById("menuToggle");
var navLinks = document.getElementById("navLinks");
if (menuToggle && navLinks) {
menuToggle.addEventListener("click", function () {
var open = navLinks.classList.toggle("open");
menuToggle.setAttribute("aria-expanded", String(open));
});
navLinks.querySelectorAll("a").forEach(function (a) {
a.addEventListener("click", function () {
navLinks.classList.remove("open");
menuToggle.setAttribute("aria-expanded", "false");
});
});
}
/* ---- Nav shadow on scroll ---- */
var nav = document.getElementById("nav");
function onScroll() {
if (nav) nav.classList.toggle("scrolled", window.scrollY > 8);
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---- Scroll reveal ---- */
var reveals = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
e.target.classList.add("in");
io.unobserve(e.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -8% 0px" }
);
reveals.forEach(function (el) {
io.observe(el);
});
} else {
reveals.forEach(function (el) {
el.classList.add("in");
});
}
/* ---- Product configurator (color + size + price) ---- */
var colors = [
{ id: "ecru", name: "Écru", hex: "#d8cbb3", price: 420 },
{ id: "charcoal", name: "Charcoal", hex: "#36332e", price: 420 },
{ id: "rust", name: "Rust", hex: "#b3411f", price: 440 },
{ id: "sage", name: "Sage", hex: "#97a07f", price: 420 }
];
var sizes = ["XS", "S", "M", "L", "XL"];
var soldOut = { rust: ["XS"], sage: ["XL"] };
var state = { color: colors[0], size: "M" };
var photo = document.getElementById("productPhoto");
var thumbs = document.getElementById("productThumbs");
var colorSwatches = document.getElementById("colorSwatches");
var sizeOptions = document.getElementById("sizeOptions");
var colorNameEl = document.getElementById("colorName");
var sizeNameEl = document.getElementById("sizeName");
var priceValEl = document.getElementById("productPriceVal");
var addPriceEl = document.getElementById("addPrice");
var stickyMeta = document.getElementById("stickyMeta");
function fmt(n) {
return "€" + n;
}
function renderSwatches() {
if (!colorSwatches) return;
colorSwatches.innerHTML = "";
thumbs.innerHTML = "";
colors.forEach(function (c) {
var sw = document.createElement("button");
sw.className = "swatch";
sw.style.background = c.hex;
sw.setAttribute("role", "radio");
sw.setAttribute("aria-label", c.name);
sw.setAttribute("aria-checked", String(c.id === state.color.id));
sw.addEventListener("click", function () {
setColor(c);
});
colorSwatches.appendChild(sw);
var th = document.createElement("button");
th.className = "product-thumb";
th.style.background = "linear-gradient(150deg," + c.hex + ", rgba(0,0,0,0.25))";
th.setAttribute("aria-label", "View " + c.name);
th.setAttribute("aria-current", String(c.id === state.color.id));
th.addEventListener("click", function () {
setColor(c);
});
thumbs.appendChild(th);
});
}
function renderSizes() {
if (!sizeOptions) return;
sizeOptions.innerHTML = "";
var out = soldOut[state.color.id] || [];
sizes.forEach(function (s) {
var btn = document.createElement("button");
btn.className = "size";
btn.textContent = s;
btn.setAttribute("role", "radio");
var isOut = out.indexOf(s) !== -1;
if (isOut) {
btn.disabled = true;
btn.setAttribute("aria-disabled", "true");
btn.title = "Sold out in " + state.color.name;
}
btn.setAttribute("aria-checked", String(s === state.size && !isOut));
btn.addEventListener("click", function () {
if (isOut) return;
state.size = s;
syncUI();
});
sizeOptions.appendChild(btn);
});
}
function setColor(c) {
state.color = c;
// If current size is sold out in new color, bump to first available
var out = soldOut[c.id] || [];
if (out.indexOf(state.size) !== -1) {
var avail = sizes.filter(function (s) {
return out.indexOf(s) === -1;
});
state.size = avail[0] || state.size;
}
if (photo) photo.setAttribute("data-color", c.id);
syncUI();
}
function syncUI() {
if (colorNameEl) colorNameEl.textContent = state.color.name;
if (sizeNameEl) sizeNameEl.textContent = state.size;
if (priceValEl) priceValEl.textContent = fmt(state.color.price);
if (addPriceEl) addPriceEl.textContent = fmt(state.color.price);
if (stickyMeta)
stickyMeta.textContent =
state.color.name + " · " + state.size + " · " + fmt(state.color.price);
// re-mark checked states without full re-render of color
if (colorSwatches) {
colorSwatches.querySelectorAll(".swatch").forEach(function (sw, i) {
sw.setAttribute("aria-checked", String(colors[i].id === state.color.id));
});
thumbs.querySelectorAll(".product-thumb").forEach(function (th, i) {
th.setAttribute("aria-current", String(colors[i].id === state.color.id));
});
}
renderSizes();
}
renderSwatches();
renderSizes();
syncUI();
/* ---- Add to cart ---- */
function addToCart() {
toast(
"Added — Écru Coat · " +
state.color.name +
" · " +
state.size +
" (" +
fmt(state.color.price) +
")"
);
}
var addBtn = document.getElementById("addBtn");
var stickyAdd = document.getElementById("stickyAdd");
if (addBtn) addBtn.addEventListener("click", addToCart);
if (stickyAdd) stickyAdd.addEventListener("click", addToCart);
var sizeGuideBtn = document.getElementById("sizeGuideBtn");
if (sizeGuideBtn)
sizeGuideBtn.addEventListener("click", function () {
toast("Size guide: M fits 96–101cm chest. Runs true to size.");
});
/* ---- Plan CTAs ---- */
document.querySelectorAll(".plan-cta").forEach(function (btn) {
btn.addEventListener("click", function () {
var card = btn.closest(".plan");
var name = card ? card.querySelector("h3").textContent : "Bundle";
toast("Added “" + name + "” to your cart.");
});
});
/* ---- Sticky add-to-cart visibility ---- */
var stickyCart = document.getElementById("stickyCart");
var product = document.getElementById("product");
var pricing = document.getElementById("pricing");
if (stickyCart && product && "IntersectionObserver" in window) {
var shownBelow = false; // past product hero
var pastPricing = false;
var ioProduct = new IntersectionObserver(
function (entries) {
shownBelow = entries[0].boundingClientRect.top < 0 && !entries[0].isIntersecting;
updateSticky();
},
{ threshold: 0 }
);
ioProduct.observe(product);
if (pricing) {
var ioPricing = new IntersectionObserver(
function (entries) {
// hide once user has scrolled past pricing into footer area
pastPricing = entries[0].boundingClientRect.top < 0 && !entries[0].isIntersecting;
updateSticky();
},
{ threshold: 0 }
);
ioPricing.observe(pricing);
}
function updateSticky() {
var show = shownBelow && !pastPricing;
stickyCart.classList.toggle("show", show);
stickyCart.setAttribute("aria-hidden", String(!show));
}
}
/* ---- Waitlist form ---- */
var form = document.getElementById("waitlistForm");
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var input = document.getElementById("email");
var val = (input.value || "").trim();
var ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val);
if (!ok) {
input.classList.add("invalid");
input.focus();
toast("Please enter a valid email.");
return;
}
input.classList.remove("invalid");
input.value = "";
toast("You're on the list — Drop 02 access incoming.");
});
document.getElementById("email").addEventListener("input", function () {
this.classList.remove("invalid");
});
}
/* ---- Dismiss announcement on its own click is not needed; keep simple ---- */
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>MAISON ÉCRU — The Atelier Drop 01</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:ital,wght@0,400;0,500;0,600;1,400&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Announcement -->
<div class="announce" id="announce">
<span>Drop 01 — <strong>The Atelier Collection</strong> ships worldwide · Free returns within 30 days</span>
</div>
<!-- Nav -->
<header class="nav" id="nav">
<div class="nav-inner">
<a href="#top" class="logo" aria-label="Maison Écru home">
MAISON <span>ÉCRU</span>
</a>
<nav class="nav-links" id="navLinks" aria-label="Primary">
<a href="#lookbook">Lookbook</a>
<a href="#product">The Coat</a>
<a href="#fabric">Fabric</a>
<a href="#reviews">Reviews</a>
<a href="#pricing">Pricing</a>
</nav>
<div class="nav-cta">
<a href="#pricing" class="btn btn-dark btn-sm">Shop the drop</a>
<button class="menu-toggle" id="menuToggle" aria-label="Toggle menu" aria-expanded="false" aria-controls="navLinks">
<span></span><span></span><span></span>
</button>
</div>
</div>
</header>
<main id="top">
<!-- Hero -->
<section class="hero" id="hero">
<div class="hero-grid">
<div class="hero-copy reveal">
<p class="eyebrow">Drop 01 — Released 16.06</p>
<h1>Quiet luxury,<br /><em>cut to last.</em></h1>
<p class="lead">An eleven-piece atelier wardrobe in undyed wool, organic cotton and recycled cashmere. Made in limited runs. Worn for decades.</p>
<div class="hero-actions">
<a href="#pricing" class="btn btn-dark">Shop the collection</a>
<a href="#lookbook" class="btn btn-ghost">View the lookbook →</a>
</div>
<ul class="hero-meta">
<li><strong>11</strong> pieces</li>
<li><strong>320</strong> runs only</li>
<li><strong>4.9</strong> ★ from 2,100+</li>
</ul>
</div>
<div class="hero-visual reveal">
<div class="hero-frame">
<div class="hero-photo hero-photo--main" role="img" aria-label="Model wearing the écru wool coat"></div>
<div class="hero-photo hero-photo--accent" role="img" aria-label="Detail of stitched lapel"></div>
<span class="hero-tag">The Écru Coat — €420</span>
</div>
</div>
</div>
<div class="marquee" aria-hidden="true">
<div class="marquee-track">
<span>UNDYED WOOL</span><span>·</span><span>MADE IN PORTUGAL</span><span>·</span><span>CARBON-NEUTRAL SHIPPING</span><span>·</span><span>LIFETIME REPAIRS</span><span>·</span>
<span>UNDYED WOOL</span><span>·</span><span>MADE IN PORTUGAL</span><span>·</span><span>CARBON-NEUTRAL SHIPPING</span><span>·</span><span>LIFETIME REPAIRS</span><span>·</span>
</div>
</div>
</section>
<!-- Social proof / press -->
<section class="press reveal" aria-label="As seen in">
<p class="press-label">As featured in</p>
<ul class="press-logos">
<li>VOGUE</li>
<li>KINFOLK</li>
<li>MONOCLE</li>
<li>THE GENTLEWOMAN</li>
<li>SSENSE</li>
</ul>
</section>
<!-- Lookbook -->
<section class="lookbook" id="lookbook">
<div class="section-head reveal">
<p class="eyebrow">The Lookbook</p>
<h2>Eleven essentials, photographed in daylight.</h2>
<p class="section-sub">Hover any look to see the styling notes. Tap on mobile.</p>
</div>
<div class="look-grid">
<figure class="look look--tall reveal" tabindex="0">
<div class="look-img look-1"></div>
<figcaption><span class="look-name">Look 01 — The Écru Coat</span><span class="look-note">Double-faced wool, raglan sleeve, horn buttons.</span></figcaption>
</figure>
<figure class="look reveal" tabindex="0">
<div class="look-img look-2"></div>
<figcaption><span class="look-name">Look 02 — Cashmere Knit</span><span class="look-note">Recycled cashmere, dropped shoulder.</span></figcaption>
</figure>
<figure class="look reveal" tabindex="0">
<div class="look-img look-3"></div>
<figcaption><span class="look-name">Look 03 — Wide Trouser</span><span class="look-note">Organic cotton twill, pressed pleat.</span></figcaption>
</figure>
<figure class="look look--wide reveal" tabindex="0">
<div class="look-img look-4"></div>
<figcaption><span class="look-name">Look 04 — The Set</span><span class="look-note">Coat over knit, styled with leather mule.</span></figcaption>
</figure>
<figure class="look reveal" tabindex="0">
<div class="look-img look-5"></div>
<figcaption><span class="look-name">Look 05 — Silk Shirt</span><span class="look-note">Sandwashed silk, mother-of-pearl.</span></figcaption>
</figure>
</div>
</section>
<!-- Product highlight with swatch picker -->
<section class="product" id="product">
<div class="product-grid">
<div class="product-media reveal">
<div class="product-photo" id="productPhoto" data-color="ecru" role="img" aria-label="The Écru Coat in selected colour"></div>
<div class="product-thumbs" id="productThumbs"></div>
</div>
<div class="product-info reveal">
<p class="eyebrow">The Hero Piece</p>
<h2>The Écru Coat</h2>
<p class="product-price"><span id="productPriceVal">€420</span> <span class="product-price-was">€520</span></p>
<p class="product-desc">A double-faced wool overcoat with a clean raglan line and hand-finished horn buttons. Unlined, undyed, and built to be re-pressed for a lifetime.</p>
<div class="picker">
<div class="picker-row">
<span class="picker-label">Colour — <strong id="colorName">Écru</strong></span>
<div class="swatches" id="colorSwatches" role="radiogroup" aria-label="Choose colour"></div>
</div>
<div class="picker-row">
<span class="picker-label">Size — <strong id="sizeName">M</strong></span>
<div class="sizes" id="sizeOptions" role="radiogroup" aria-label="Choose size"></div>
</div>
</div>
<div class="product-actions">
<button class="btn btn-dark btn-block" id="addBtn">Add to cart — <span id="addPrice">€420</span></button>
<button class="btn btn-ghost btn-block" id="sizeGuideBtn">Size guide</button>
</div>
<ul class="product-perks">
<li>✓ Free worldwide shipping</li>
<li>✓ 30-day returns</li>
<li>✓ Lifetime repairs</li>
</ul>
</div>
</div>
</section>
<!-- Fabric / quality story -->
<section class="fabric" id="fabric">
<div class="section-head reveal">
<p class="eyebrow">The Fabric Story</p>
<h2>We start with the fibre, not the season.</h2>
</div>
<div class="fabric-grid">
<article class="fabric-card reveal">
<span class="fabric-num">01</span>
<h3>Undyed wool</h3>
<p>Sourced from a single mill in northern Portugal. Left in its natural écru tone — no dye, no bleach, no microplastic shed.</p>
</article>
<article class="fabric-card reveal">
<span class="fabric-num">02</span>
<h3>Slow construction</h3>
<p>Each coat passes through 31 hands over two days. Seams are double-faced, so there is no lining to fray or pucker.</p>
</article>
<article class="fabric-card reveal">
<span class="fabric-num">03</span>
<h3>Made to be mended</h3>
<p>Every garment is registered to you. Send it back any time and our atelier will re-press, re-button or reweave it — free, for life.</p>
</article>
</div>
</section>
<!-- Reviews -->
<section class="reviews" id="reviews">
<div class="section-head reveal">
<p class="eyebrow">Worn & loved</p>
<h2>4.9 from 2,100+ owners.</h2>
</div>
<div class="review-grid">
<blockquote class="review reveal">
<p class="stars" aria-label="5 out of 5">★★★★★</p>
<p>“The cut is impossibly clean. I’ve worn the coat three winters and it still looks like the day it arrived — better, if anything.”</p>
<footer>— Amara K., Copenhagen</footer>
</blockquote>
<blockquote class="review reveal">
<p class="stars" aria-label="5 out of 5">★★★★★</p>
<p>“Bought the knit and the trouser as a set. The undyed wool is softer than anything I own at four times the price.”</p>
<footer>— Téo R., Lisbon</footer>
</blockquote>
<blockquote class="review reveal">
<p class="stars" aria-label="5 out of 5">★★★★★</p>
<p>“They re-buttoned my coat for free after two years. That’s the whole pitch, and they actually deliver on it.”</p>
<footer>— Priya N., London</footer>
</blockquote>
</div>
</section>
<!-- Pricing / bundle -->
<section class="pricing" id="pricing">
<div class="section-head reveal">
<p class="eyebrow">Buy the drop</p>
<h2>One piece, or the whole wardrobe.</h2>
<p class="section-sub">Bundle and save — limited to 320 runs.</p>
</div>
<div class="plans">
<article class="plan reveal">
<h3>The Coat</h3>
<p class="plan-price">€420</p>
<p class="plan-desc">The hero overcoat, on its own.</p>
<ul>
<li>The Écru Coat</li>
<li>Free worldwide shipping</li>
<li>Lifetime repairs</li>
</ul>
<button class="btn btn-ghost btn-block plan-cta">Add the coat</button>
</article>
<article class="plan plan--featured reveal">
<span class="plan-badge">Best value</span>
<h3>The Capsule</h3>
<p class="plan-price">€680 <span class="plan-was">€940</span></p>
<p class="plan-desc">Coat, cashmere knit & wide trouser.</p>
<ul>
<li>3 atelier pieces</li>
<li>Save €260</li>
<li>Priority drop access</li>
<li>Lifetime repairs</li>
</ul>
<button class="btn btn-dark btn-block plan-cta">Shop the capsule</button>
</article>
<article class="plan reveal">
<h3>The Wardrobe</h3>
<p class="plan-price">€1,490</p>
<p class="plan-desc">All eleven pieces of Drop 01.</p>
<ul>
<li>Full collection</li>
<li>Personal atelier fitting</li>
<li>Save €430</li>
<li>Lifetime repairs</li>
</ul>
<button class="btn btn-ghost btn-block plan-cta">Shop the wardrobe</button>
</article>
</div>
</section>
<!-- Waitlist / newsletter -->
<section class="waitlist reveal" id="waitlist">
<div class="waitlist-inner">
<p class="eyebrow eyebrow--light">Drop 02</p>
<h2>Be first through the door.</h2>
<p>Drop 02 is 600 runs of recycled cashmere. Join the list for 24-hour early access.</p>
<form class="waitlist-form" id="waitlistForm" novalidate>
<label class="sr-only" for="email">Email address</label>
<input type="email" id="email" name="email" placeholder="[email protected]" required autocomplete="email" />
<button type="submit" class="btn btn-light">Join the list</button>
</form>
<p class="waitlist-fine">No spam. One email per drop. Unsubscribe anytime.</p>
</div>
</section>
</main>
<!-- Footer -->
<footer class="footer">
<div class="footer-grid">
<div class="footer-brand">
<a href="#top" class="logo logo--light">MAISON <span>ÉCRU</span></a>
<p>Undyed, unlined, made to last. An atelier label in limited runs.</p>
</div>
<nav aria-label="Shop">
<h4>Shop</h4>
<a href="#product">The Coat</a>
<a href="#lookbook">Lookbook</a>
<a href="#pricing">Bundles</a>
<a href="#waitlist">Drop 02</a>
</nav>
<nav aria-label="Atelier">
<h4>Atelier</h4>
<a href="#fabric">Our fabric</a>
<a href="#fabric">Repairs</a>
<a href="#reviews">Reviews</a>
<a href="#top">Sustainability</a>
</nav>
<nav aria-label="Help">
<h4>Help</h4>
<a href="#top">Shipping</a>
<a href="#top">Returns</a>
<a href="#top">Size guide</a>
<a href="#top">Contact</a>
</nav>
</div>
<div class="footer-base">
<span>© 2026 Maison Écru — illustrative concept, not a real store.</span>
<div class="footer-social">
<a href="#top" aria-label="Instagram">Instagram</a>
<a href="#top" aria-label="Pinterest">Pinterest</a>
</div>
</div>
</footer>
<!-- Sticky add-to-cart -->
<div class="sticky-cart" id="stickyCart" aria-hidden="true">
<div class="sticky-cart-inner">
<div class="sticky-cart-info">
<span class="sticky-cart-name">The Écru Coat</span>
<span class="sticky-cart-meta" id="stickyMeta">Écru · M · €420</span>
</div>
<button class="btn btn-dark btn-sm" id="stickyAdd">Add to cart</button>
</div>
</div>
<!-- Toast -->
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Fashion / Apparel Landing
A complete one-page direct-to-consumer landing for Maison Écru, a fictional atelier label that sells apparel in limited “drops”. The identity is deliberately editorial: a white-and-black paper canvas lifted by a single terracotta accent, a Cormorant Garamond couture serif for headlines paired with a clean Inter sans, and an aspirational, quiet-luxury mood throughout. It opens with a sticky blurred nav and a full-bleed hero — oversized headline, dual CTAs, a layered CSS product mock, and a running fabric-credentials marquee.
Below the hero the page tells the full conversion story: a press strip, a masonry lookbook where each look reveals styling notes on hover or focus, an interactive product configurator for the hero coat with colour swatches and a size picker (price, photo tint and the sticky bar all update live, and sold-out size/colour combinations disable themselves), a three-card fabric-and-quality story, dark star-rated reviews, three bundle pricing tiers with a featured “best value” capsule, and a drop-02 waitlist with inline email validation.
Every interaction is vanilla JavaScript with no dependencies: a slide-down mobile nav, smooth in-page scrolling, IntersectionObserver scroll-reveal, the live product configurator, a toast() helper on every add-to-cart and the waitlist submit, and a sticky add-to-cart bar that slides up once you scroll past the product section and tucks away again over the footer. The layout is fully responsive down to ~360px with stacked sections, a single-column lookbook with always-visible captions on small screens, and it respects prefers-reduced-motion.
Illustrative UI only — fictional brand, not a real product.