Wedding — Landing Modern
A modern minimalist wedding landing where oversized serif type meets a single gold accent. A live countdown ticks toward the big day, a sticky nav smooth-scrolls between airy sections, an RSVP form validates inline with meal choices, and an event timeline plus travel cards keep guests informed. Blush and cream tones, thin gold rules, and gentle scroll-reveal animations give it an editorial, romantic calm without clutter.
MCP
Code
:root {
--blush: #e8b7b0;
--blush-50: #fbeeec;
--rose: #c98a86;
--gold: #c9a24b;
--gold-d: #a8862f;
--ink: #3a2b2b;
--ink-2: #6b5555;
--muted: #9a8585;
--bg: #fbf7f4;
--cream: #f4ece4;
--white: #fff;
--line: rgba(58, 43, 43, 0.12);
--r-sm: 8px;
--r-md: 16px;
--r-lg: 26px;
--serif: "Cormorant Garamond", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 78px; }
body {
margin: 0;
font-family: var(--sans);
color: var(--ink);
background: var(--bg);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2, h3 { margin: 0; font-weight: 400; }
p { margin: 0; }
a { color: inherit; }
:focus-visible {
outline: 2px solid var(--gold-d);
outline-offset: 3px;
border-radius: 4px;
}
/* ---------- Nav ---------- */
.nav {
position: sticky;
top: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px clamp(20px, 5vw, 64px);
background: rgba(251, 247, 244, 0.82);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.nav__brand {
font-family: var(--serif);
font-size: 1.5rem;
letter-spacing: 0.14em;
text-decoration: none;
color: var(--ink);
}
.nav__brand span { color: var(--gold-d); }
.nav__links {
display: flex;
align-items: center;
gap: clamp(14px, 2.4vw, 34px);
}
.nav__links a {
position: relative;
text-decoration: none;
font-size: 0.82rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-2);
padding: 6px 2px;
transition: color 0.2s ease;
}
.nav__links a::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: var(--gold-d);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.25s ease;
}
.nav__links a:hover,
.nav__links a.is-active { color: var(--ink); }
.nav__links a:hover::after,
.nav__links a.is-active::after { transform: scaleX(1); }
.nav__cta {
padding: 8px 18px !important;
border: 1px solid var(--gold);
border-radius: 999px;
color: var(--gold-d) !important;
}
.nav__cta::after { display: none; }
.nav__cta:hover { background: var(--gold); color: var(--white) !important; }
/* ---------- Buttons ---------- */
.btn {
display: inline-block;
font-family: var(--sans);
font-size: 0.82rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
text-decoration: none;
cursor: pointer;
border: none;
border-radius: 999px;
padding: 15px 34px;
transition: transform 0.18s ease, box-shadow 0.25s ease, background 0.2s ease;
}
.btn--primary {
background: var(--ink);
color: var(--cream);
box-shadow: 0 10px 24px rgba(58, 43, 43, 0.16);
}
.btn--primary:hover { background: var(--gold-d); transform: translateY(-2px); box-shadow: 0 14px 30px rgba(168, 134, 47, 0.28); }
.btn--primary:active { transform: translateY(0); }
/* ---------- Hero ---------- */
.hero {
min-height: 88vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 60px 20px 80px;
background:
radial-gradient(120% 90% at 50% 0%, var(--blush-50) 0%, transparent 55%),
linear-gradient(180deg, var(--bg) 0%, var(--cream) 100%);
}
.hero__eyebrow {
font-size: 0.78rem;
letter-spacing: 0.34em;
text-transform: uppercase;
color: var(--rose);
margin-bottom: 22px;
}
.hero__title {
font-family: var(--serif);
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: center;
gap: 0 0.35em;
font-size: clamp(3.4rem, 13vw, 9rem);
line-height: 0.95;
letter-spacing: 0.01em;
}
.hero__amp { color: var(--gold-d); font-weight: 400; font-style: italic; }
.hero__rule {
width: 68px;
height: 1px;
background: var(--gold);
margin: 30px 0 20px;
}
.hero__meta {
font-size: 0.86rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--ink-2);
}
.countdown {
display: flex;
gap: clamp(16px, 4vw, 46px);
margin: 44px 0 40px;
}
.count { display: flex; flex-direction: column; align-items: center; min-width: 56px; }
.count strong {
font-family: var(--serif);
font-size: clamp(2rem, 6vw, 3rem);
font-weight: 500;
color: var(--ink);
font-variant-numeric: tabular-nums;
}
.count span {
margin-top: 6px;
font-size: 0.62rem;
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- Sections ---------- */
.section {
max-width: 1040px;
margin: 0 auto;
padding: clamp(70px, 11vw, 130px) clamp(20px, 5vw, 40px);
border-top: 1px solid var(--line);
}
.section__label {
font-size: 0.72rem;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--gold-d);
margin-bottom: 18px;
}
.section__title {
font-family: var(--serif);
font-size: clamp(1.9rem, 4.6vw, 3.1rem);
line-height: 1.08;
max-width: 18ch;
}
/* Story */
.story__grid {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: clamp(28px, 6vw, 64px);
align-items: start;
margin-top: 10px;
}
.story__body { display: grid; gap: 18px; color: var(--ink-2); font-size: 1.02rem; padding-top: 8px; }
/* Details */
.details__grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1px;
background: var(--line);
margin-top: 46px;
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
}
.detail {
background: var(--white);
padding: clamp(26px, 4vw, 40px);
transition: background 0.25s ease;
}
.detail:hover { background: var(--blush-50); }
.detail__badge {
display: inline-block;
font-size: 0.64rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--gold-d);
border: 1px solid var(--gold);
border-radius: 999px;
padding: 4px 12px;
margin-bottom: 18px;
}
.detail h3 { font-family: var(--serif); font-size: 1.7rem; margin-bottom: 8px; }
.detail p { color: var(--ink-2); font-size: 0.96rem; }
/* Timeline */
.tl { list-style: none; margin: 46px 0 0; padding: 0; }
.tl__item {
display: grid;
grid-template-columns: 92px 1fr;
gap: clamp(18px, 4vw, 40px);
padding: 26px 0;
border-top: 1px dotted var(--line);
position: relative;
}
.tl__item:last-child { border-bottom: 1px dotted var(--line); }
.tl__time {
font-family: var(--serif);
font-size: 1.5rem;
color: var(--gold-d);
font-variant-numeric: tabular-nums;
}
.tl__item h3 { font-family: var(--serif); font-size: 1.5rem; margin-bottom: 4px; }
.tl__item p { color: var(--ink-2); font-size: 0.96rem; }
/* Travel */
.travel__grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 46px;
}
.tcard {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 30px 28px;
transition: transform 0.2s ease, box-shadow 0.28s ease;
}
.tcard:hover { transform: translateY(-4px); box-shadow: 0 18px 34px rgba(58, 43, 43, 0.1); }
.tcard h3 { font-family: var(--serif); font-size: 1.55rem; margin-bottom: 10px; }
.tcard p { color: var(--ink-2); font-size: 0.96rem; }
.tcard em { color: var(--gold-d); font-style: italic; }
/* RSVP */
.rsvp { text-align: center; }
.rsvp .section__title { margin: 0 auto; }
.rsvp__deadline { margin-top: 16px; color: var(--ink-2); letter-spacing: 0.04em; }
.form {
max-width: 560px;
margin: 44px auto 0;
text-align: left;
display: grid;
gap: 22px;
}
.field { display: grid; gap: 8px; border: none; padding: 0; margin: 0; }
.field label,
.field legend {
font-size: 0.72rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-2);
font-weight: 600;
padding: 0;
}
.field .opt { color: var(--muted); font-weight: 400; text-transform: none; letter-spacing: 0.02em; }
.field input,
.field select,
.field textarea {
font-family: var(--sans);
font-size: 1rem;
color: var(--ink);
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-sm);
padding: 13px 15px;
width: 100%;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.field textarea { resize: vertical; }
.field input:focus,
.field select:focus,
.field textarea:focus {
border-color: var(--gold);
box-shadow: 0 0 0 3px rgba(201, 162, 75, 0.18);
outline: none;
}
.field input.invalid,
.field select.invalid { border-color: var(--rose); box-shadow: 0 0 0 3px rgba(201, 138, 134, 0.18); }
.field__error { min-height: 0; font-size: 0.8rem; color: var(--rose); font-weight: 500; }
.field__error:empty { display: none; }
.toggle { display: flex; gap: 10px; }
.toggle__btn {
flex: 1;
font-family: var(--sans);
font-size: 0.86rem;
font-weight: 600;
letter-spacing: 0.04em;
color: var(--ink-2);
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-sm);
padding: 13px;
cursor: pointer;
transition: all 0.2s ease;
}
.toggle__btn:hover { border-color: var(--gold); color: var(--ink); }
.toggle__btn.is-active { background: var(--ink); color: var(--cream); border-color: var(--ink); }
.form__submit { justify-self: start; margin-top: 6px; }
.is-hidden { display: none !important; }
/* ---------- Footer ---------- */
.footer {
text-align: center;
padding: 70px 20px 60px;
background: var(--cream);
border-top: 1px solid var(--line);
}
.footer__mono {
font-family: var(--serif);
font-size: 2rem;
letter-spacing: 0.16em;
margin-bottom: 16px;
}
.footer__mono span { color: var(--gold-d); }
.footer p { color: var(--ink-2); font-size: 0.82rem; letter-spacing: 0.14em; text-transform: uppercase; }
.footer__fine { margin-top: 12px; text-transform: none !important; letter-spacing: 0.02em !important; color: var(--muted) !important; }
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 24px);
background: var(--ink);
color: var(--cream);
padding: 14px 24px;
border-radius: 999px;
font-size: 0.88rem;
font-weight: 500;
letter-spacing: 0.02em;
box-shadow: 0 14px 34px rgba(58, 43, 43, 0.28);
opacity: 0;
pointer-events: none;
z-index: 100;
transition: opacity 0.3s ease, transform 0.3s ease;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- Reveal ---------- */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.reveal { opacity: 1; transform: none; transition: none; }
}
/* ---------- Responsive ---------- */
@media (max-width: 820px) {
.story__grid { grid-template-columns: 1fr; }
.travel__grid { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
.nav { padding: 12px 18px; }
.nav__links { gap: 12px; }
.nav__links a:not(.nav__cta) { display: none; }
.hero { min-height: 78vh; }
.countdown { gap: 14px; }
.count { min-width: 46px; }
.details__grid { grid-template-columns: 1fr; }
.tl__item { grid-template-columns: 68px 1fr; gap: 16px; }
.tl__time { font-size: 1.25rem; }
.btn { padding: 14px 26px; }
.form__submit { justify-self: stretch; text-align: center; }
}(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");
}, 3200);
}
/* ---------- Countdown ---------- */
var target = new Date("2026-09-20T16:30:00+02:00").getTime();
var els = {
days: document.getElementById("cd-days"),
hours: document.getElementById("cd-hours"),
mins: document.getElementById("cd-mins"),
secs: document.getElementById("cd-secs")
};
function pad(n) { return String(n).padStart(2, "0"); }
function tick() {
var diff = target - Date.now();
if (diff <= 0) {
els.days.textContent = els.hours.textContent = els.mins.textContent = els.secs.textContent = "00";
clearInterval(cdTimer);
return;
}
var s = Math.floor(diff / 1000);
els.days.textContent = pad(Math.floor(s / 86400));
els.hours.textContent = pad(Math.floor((s % 86400) / 3600));
els.mins.textContent = pad(Math.floor((s % 3600) / 60));
els.secs.textContent = pad(s % 60);
}
tick();
var cdTimer = setInterval(tick, 1000);
/* ---------- Scroll reveal ---------- */
var revealEls = 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.15, rootMargin: "0px 0px -8% 0px" });
revealEls.forEach(function (el) { io.observe(el); });
} else {
revealEls.forEach(function (el) { el.classList.add("in"); });
}
/* ---------- Active nav link on scroll ---------- */
var links = Array.prototype.slice.call(document.querySelectorAll(".nav__links a[href^='#']"));
var sections = links
.map(function (l) { return document.querySelector(l.getAttribute("href")); })
.filter(Boolean);
function syncNav() {
var pos = window.scrollY + 120;
var current = sections[0];
sections.forEach(function (sec) {
if (sec.offsetTop <= pos) current = sec;
});
links.forEach(function (l) {
l.classList.toggle("is-active", current && l.getAttribute("href") === "#" + current.id);
});
}
window.addEventListener("scroll", syncNav, { passive: true });
syncNav();
/* ---------- Attendance toggle ---------- */
var attending = "yes";
var toggleBtns = document.querySelectorAll(".toggle__btn");
var mealField = document.getElementById("meal-field");
var guestsField = document.getElementById("guests-field");
toggleBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
attending = btn.getAttribute("data-attend");
toggleBtns.forEach(function (b) {
var active = b === btn;
b.classList.toggle("is-active", active);
b.setAttribute("aria-pressed", active ? "true" : "false");
});
var hide = attending === "no";
mealField.classList.toggle("is-hidden", hide);
guestsField.classList.toggle("is-hidden", hide);
});
});
/* ---------- Form validation ---------- */
var form = document.getElementById("rsvp-form");
function setError(id, msg) {
var input = document.getElementById(id);
var err = document.getElementById(id + "-error");
if (err) err.textContent = msg || "";
if (input) input.classList.toggle("invalid", !!msg);
return !msg;
}
var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
form.addEventListener("submit", function (e) {
e.preventDefault();
var ok = true;
var name = document.getElementById("name").value.trim();
ok = setError("name", name ? "" : "Please tell us your name.") && ok;
var email = document.getElementById("email").value.trim();
if (!email) ok = setError("email", "We need an email to confirm.") && ok;
else ok = setError("email", emailRe.test(email) ? "" : "That email looks incomplete.") && ok;
if (attending === "yes") {
var meal = document.getElementById("meal").value;
ok = setError("meal", meal ? "" : "Choose a meal so the kitchen can plan.") && ok;
} else {
setError("meal", "");
setError("guests", "");
}
if (!ok) {
toast("Please check the highlighted fields.");
var firstInvalid = form.querySelector(".invalid");
if (firstInvalid) firstInvalid.focus();
return;
}
var first = name.split(" ")[0];
if (attending === "no") {
toast("We'll miss you, " + first + " — thank you for letting us know.");
} else {
toast("Thank you, " + first + "! Your seat is saved. 🥂");
}
form.reset();
// Reset toggle back to default "yes"
toggleBtns.forEach(function (b, i) {
var active = i === 0;
b.classList.toggle("is-active", active);
b.setAttribute("aria-pressed", active ? "true" : "false");
});
attending = "yes";
mealField.classList.remove("is-hidden");
guestsField.classList.remove("is-hidden");
});
// Clear an individual error as the guest corrects it
["name", "email", "meal"].forEach(function (id) {
var el = document.getElementById(id);
if (el) el.addEventListener("input", function () { setError(id, ""); });
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Amara & Elias — 20 September 2026</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="nav" id="top">
<a class="nav__brand" href="#top" aria-label="Amara and Elias, home">A <span>&</span> E</a>
<nav class="nav__links" aria-label="Primary">
<a href="#story">Story</a>
<a href="#details">Details</a>
<a href="#timeline">Timeline</a>
<a href="#travel">Travel</a>
<a href="#rsvp" class="nav__cta">RSVP</a>
</nav>
</header>
<main>
<section class="hero" aria-labelledby="hero-title">
<p class="hero__eyebrow reveal">We're getting married</p>
<h1 class="hero__title" id="hero-title">
<span class="reveal">Amara</span>
<span class="hero__amp reveal">&</span>
<span class="reveal">Elias</span>
</h1>
<div class="hero__rule reveal" aria-hidden="true"></div>
<p class="hero__meta reveal">Saturday, September 20 · 2026 · Portofino, Italy</p>
<div class="countdown reveal" aria-label="Countdown to the ceremony">
<div class="count"><strong id="cd-days">00</strong><span>Days</span></div>
<div class="count"><strong id="cd-hours">00</strong><span>Hours</span></div>
<div class="count"><strong id="cd-mins">00</strong><span>Minutes</span></div>
<div class="count"><strong id="cd-secs">00</strong><span>Seconds</span></div>
</div>
<a href="#rsvp" class="btn btn--primary reveal">Reserve your seat</a>
</section>
<section class="section story" id="story" aria-labelledby="story-title">
<p class="section__label reveal">Our story</p>
<div class="story__grid">
<h2 class="section__title reveal" id="story-title">Two cities, one very long train ride, and a shared umbrella.</h2>
<div class="story__body reveal">
<p>We met on a rainy platform in Milan in 2019 — one umbrella between two strangers heading the same way. Seven years, three apartments and one small dog named Pesto later, we're saying yes on the coast that started it all.</p>
<p>We would be honoured to have you with us for a weekend of good food, salt air and slightly too much dancing.</p>
</div>
</div>
</section>
<section class="section details" id="details" aria-labelledby="details-title">
<p class="section__label reveal">The details</p>
<h2 class="section__title reveal" id="details-title">Everything you need to know.</h2>
<div class="details__grid">
<article class="detail reveal">
<span class="detail__badge">Ceremony</span>
<h3>4:30 in the afternoon</h3>
<p>Chiesa di San Giorgio, overlooking the harbour. Arrive by 4:00 for prosecco.</p>
</article>
<article class="detail reveal">
<span class="detail__badge">Reception</span>
<h3>Dinner & dancing</h3>
<p>Villa Serena terrace from 7:00 onward. Seated dinner, open bar, live quartet.</p>
</article>
<article class="detail reveal">
<span class="detail__badge">Dress code</span>
<h3>Garden formal</h3>
<p>Long dresses and light suits in soft, warm tones. Bring flats for cobblestones.</p>
</article>
<article class="detail reveal">
<span class="detail__badge">Gifts</span>
<h3>Your presence</h3>
<p>Is the only present we need. A honeymoon fund link lives in your invitation.</p>
</article>
</div>
</section>
<section class="section timeline" id="timeline" aria-labelledby="timeline-title">
<p class="section__label reveal">The day</p>
<h2 class="section__title reveal" id="timeline-title">A gentle order of events.</h2>
<ol class="tl">
<li class="tl__item reveal"><span class="tl__time">16:00</span><div><h3>Welcome drinks</h3><p>Prosecco and citrus on the church steps.</p></div></li>
<li class="tl__item reveal"><span class="tl__time">16:30</span><div><h3>Ceremony</h3><p>Vows, rings, happy tears.</p></div></li>
<li class="tl__item reveal"><span class="tl__time">18:00</span><div><h3>Golden hour photos</h3><p>Aperitivo on the harbour while we sneak away.</p></div></li>
<li class="tl__item reveal"><span class="tl__time">19:00</span><div><h3>Dinner</h3><p>Four courses on the terrace at Villa Serena.</p></div></li>
<li class="tl__item reveal"><span class="tl__time">21:30</span><div><h3>First dance & party</h3><p>The quartet plugs in. You should too.</p></div></li>
</ol>
</section>
<section class="section travel" id="travel" aria-labelledby="travel-title">
<p class="section__label reveal">Getting there</p>
<h2 class="section__title reveal" id="travel-title">Travel & stay.</h2>
<div class="travel__grid">
<article class="tcard reveal">
<h3>Fly in</h3>
<p>Genoa (GOA) is 40 minutes away; Milan Malpensa (MXP) a scenic 2.5-hour drive. Shuttles run both mornings.</p>
</article>
<article class="tcard reveal">
<h3>Where to stay</h3>
<p>We've held rooms at Hotel Belmare and Albergo Riva. Mention <em>Amara & Elias</em> for our rate through August 20.</p>
</article>
<article class="tcard reveal">
<h3>Getting around</h3>
<p>The old town is car-free and walkable. A wedding shuttle loops the harbour hotels from 3:30 to midnight.</p>
</article>
</div>
</section>
<section class="section rsvp" id="rsvp" aria-labelledby="rsvp-title">
<p class="section__label reveal">Répondez s'il vous plaît</p>
<h2 class="section__title reveal" id="rsvp-title">Will you celebrate with us?</h2>
<p class="rsvp__deadline reveal">Kindly reply by <strong>August 1, 2026</strong>.</p>
<form class="form reveal" id="rsvp-form" novalidate>
<div class="field">
<label for="name">Full name</label>
<input id="name" name="name" type="text" autocomplete="name" placeholder="Sofia Ricci" />
<p class="field__error" id="name-error" aria-live="polite"></p>
</div>
<div class="field">
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" placeholder="[email protected]" />
<p class="field__error" id="email-error" aria-live="polite"></p>
</div>
<fieldset class="field field--toggle">
<legend>Will you attend?</legend>
<div class="toggle" role="radiogroup" aria-label="Will you attend?">
<button type="button" class="toggle__btn is-active" data-attend="yes" aria-pressed="true">Joyfully yes</button>
<button type="button" class="toggle__btn" data-attend="no" aria-pressed="false">Sadly no</button>
</div>
</fieldset>
<div class="field" id="guests-field">
<label for="guests">Number of guests</label>
<select id="guests" name="guests">
<option value="1">Just me</option>
<option value="2">Two of us</option>
<option value="3">Three</option>
<option value="4">Four</option>
</select>
<p class="field__error" id="guests-error" aria-live="polite"></p>
</div>
<div class="field" id="meal-field">
<label for="meal">Meal preference</label>
<select id="meal" name="meal">
<option value="">Please choose…</option>
<option value="fish">Sea bass</option>
<option value="beef">Braised short rib</option>
<option value="veg">Garden risotto (v)</option>
<option value="kids">Kids' plate</option>
</select>
<p class="field__error" id="meal-error" aria-live="polite"></p>
</div>
<div class="field">
<label for="note">A note for the couple <span class="opt">(optional)</span></label>
<textarea id="note" name="note" rows="3" placeholder="Song requests, allergies, well wishes…"></textarea>
</div>
<button type="submit" class="btn btn--primary form__submit">Send RSVP</button>
</form>
</section>
</main>
<footer class="footer">
<div class="footer__mono">A <span>&</span> E</div>
<p>Amara Bellini & Elias Vance · September 20, 2026 · Portofino</p>
<p class="footer__fine">With love and gratitude — see you on the coast.</p>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A pared-back, editorial take on the wedding celebration landing page for the fictional couple Amara & Elias. The hero pairs an oversized Cormorant Garamond wordmark with a single gold accent line and a live countdown to the ceremony date, framed by a soft blush-to-cream gradient and generous whitespace. A sticky top nav smooth-scrolls between the Story, Details, Timeline, Travel, and RSVP sections, highlighting the active link as you move.
The middle sections stay sparse and confident: a two-column story block, an at-a-glance details grid, and a vertical event timeline with dotted gold connectors. Travel and stay information is grouped into tidy cards with hover lift, and every heading fades and rises into view as it enters the viewport.
Interactions are all vanilla JS: the countdown updates every second, the RSVP form validates name, email, guest count and meal preference inline with accessible error messaging, an attending toggle reveals or hides the meal picker, and a lightweight toast confirms submission. Everything is keyboard-usable with visible focus rings and collapses cleanly to a single column on small screens.