Wedding — Landing Destination
An editorial blush-and-gold destination wedding landing for Amara and Julian in Positano — hero save-the-date with a live countdown, a travel-info strip, an interactive venue map with pinned points of interest, a two-day itinerary timeline, curated accommodation blocks with room-rate badges, and an RSVP form that captures headcount, arrival dates, dietary notes and shuttle needs. Romantic serif type, thin gold rules and airy spacing throughout.
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;
--shadow: 0 18px 50px rgba(58, 43, 43, 0.10);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--sans);
color: var(--ink);
background: var(--bg);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
h1, h2, h3 { margin: 0; font-family: var(--serif); font-weight: 500; letter-spacing: 0.2px; }
a { color: inherit; }
.amp { font-family: var(--serif); color: var(--gold); font-style: italic; }
::selection { background: var(--blush); color: var(--ink); }
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 24px);
background: var(--ink);
color: var(--cream);
padding: 12px 20px;
border-radius: 999px;
font-size: 14px;
font-weight: 500;
box-shadow: var(--shadow);
opacity: 0;
pointer-events: none;
transition: opacity 0.35s ease, transform 0.35s ease;
z-index: 100;
max-width: 90vw;
text-align: center;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- Top bar ---------- */
.topbar {
position: sticky;
top: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px clamp(18px, 5vw, 56px);
background: rgba(251, 247, 244, 0.82);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.brand {
font-family: var(--serif);
font-size: 24px;
text-decoration: none;
letter-spacing: 2px;
color: var(--ink);
}
.nav { display: flex; align-items: center; gap: clamp(12px, 2.4vw, 30px); }
.nav a {
text-decoration: none;
font-size: 13px;
font-weight: 500;
letter-spacing: 0.6px;
text-transform: uppercase;
color: var(--ink-2);
transition: color 0.2s ease;
}
.nav a:hover { color: var(--gold-d); }
.nav-cta {
border: 1px solid var(--gold);
color: var(--gold-d) !important;
padding: 7px 16px;
border-radius: 999px;
}
.nav-cta:hover { background: var(--gold); color: var(--white) !important; }
/* ---------- Buttons ---------- */
.btn {
font-family: var(--sans);
font-size: 14px;
font-weight: 600;
border-radius: 999px;
cursor: pointer;
border: 1px solid transparent;
padding: 13px 26px;
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, color 0.18s ease;
}
.btn:focus-visible { outline: 3px solid rgba(201, 162, 75, 0.55); outline-offset: 2px; }
.btn-primary { background: var(--gold); color: var(--white); box-shadow: 0 10px 24px rgba(168, 134, 47, 0.28); }
.btn-primary:hover { background: var(--gold-d); transform: translateY(-2px); }
.btn-primary:active { transform: translateY(0); }
.btn-ghost {
background: transparent;
border-color: var(--line);
color: var(--ink);
font-weight: 500;
padding: 10px 16px;
font-size: 13px;
}
.btn-ghost:hover { border-color: var(--gold); color: var(--gold-d); background: var(--blush-50); }
.btn-ghost.copied { border-color: var(--gold); color: var(--gold-d); background: var(--blush-50); }
/* ---------- Hero ---------- */
.hero {
text-align: center;
padding: clamp(56px, 9vw, 110px) 20px clamp(48px, 7vw, 84px);
background:
radial-gradient(120% 90% at 50% -10%, var(--blush-50) 0%, rgba(251, 247, 244, 0) 60%),
linear-gradient(180deg, #fdf5f2 0%, var(--bg) 100%);
border-bottom: 1px solid var(--line);
}
.eyebrow, .kicker {
font-size: 12px;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--rose);
font-weight: 600;
margin: 0 0 14px;
}
.hero h1 {
font-size: clamp(52px, 12vw, 118px);
line-height: 0.98;
letter-spacing: 1px;
}
.hero-sub { font-family: var(--serif); font-size: clamp(20px, 3.4vw, 30px); font-style: italic; color: var(--ink-2); margin: 8px 0 20px; }
.hero-meta {
display: inline-flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
justify-content: center;
font-size: 13px;
letter-spacing: 1.6px;
text-transform: uppercase;
color: var(--ink-2);
padding: 12px 0;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
margin-bottom: 30px;
}
.hero-meta .dot { color: var(--gold); }
.countdown {
display: inline-flex;
gap: clamp(10px, 3vw, 26px);
margin-bottom: 34px;
}
.cd-cell {
min-width: 76px;
padding: 16px 10px;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
box-shadow: var(--shadow);
}
.cd-cell strong {
display: block;
font-family: var(--serif);
font-size: clamp(30px, 6vw, 44px);
font-weight: 600;
color: var(--ink);
font-variant-numeric: tabular-nums;
line-height: 1;
}
.cd-cell span {
display: block;
margin-top: 8px;
font-size: 11px;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--muted);
}
.hero-btn { margin-top: 6px; }
/* ---------- Section shell ---------- */
.section {
max-width: 1080px;
margin: 0 auto;
padding: clamp(52px, 8vw, 96px) clamp(18px, 5vw, 40px);
}
.section.shade {
max-width: none;
background: linear-gradient(180deg, var(--cream) 0%, #f7efe8 100%);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
.section.shade > * { max-width: 1080px; margin-left: auto; margin-right: auto; }
.section h2 {
font-size: clamp(32px, 5.4vw, 52px);
margin-bottom: 8px;
}
.section-lede { color: var(--ink-2); max-width: 60ch; margin: 4px 0 30px; font-size: 16px; }
/* ---------- Travel info ---------- */
.info-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 18px;
margin-top: 26px;
}
.info-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 26px 22px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.info-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.info-ico {
display: inline-grid;
place-items: center;
width: 46px;
height: 46px;
border-radius: 50%;
background: var(--blush-50);
color: var(--gold-d);
font-size: 20px;
margin-bottom: 14px;
}
.info-card h3 { font-size: 22px; margin-bottom: 4px; }
.info-card p { margin: 2px 0; font-weight: 600; color: var(--ink); }
.info-note { font-weight: 400 !important; color: var(--muted) !important; font-size: 13px; }
/* ---------- Venue map ---------- */
.venue { display: grid; grid-template-columns: 1.35fr 1fr; gap: 26px; margin-top: 26px; }
.map {
position: relative;
border-radius: var(--r-lg);
overflow: hidden;
min-height: 340px;
border: 1px solid var(--line);
box-shadow: var(--shadow);
background: linear-gradient(180deg, #dfeef0 0%, #cfe6ea 100%);
}
.map-water {
position: absolute;
inset: 0;
background:
repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.35) 0 2px, transparent 2px 26px);
opacity: 0.5;
}
.map-coast {
position: absolute;
left: -6%;
top: -8%;
width: 78%;
height: 116%;
background: linear-gradient(135deg, #f0e2d4 0%, #e7d3bf 60%, #dcc4ac 100%);
border-radius: 46% 54% 60% 40% / 40% 42% 58% 60%;
transform: rotate(-6deg);
box-shadow: inset -20px 0 40px rgba(168, 134, 47, 0.12);
}
.pin {
position: absolute;
transform: translate(-50%, -100%);
width: 34px;
height: 34px;
border-radius: 50% 50% 50% 6px;
background: var(--rose);
border: 2px solid var(--white);
color: var(--white);
cursor: pointer;
display: grid;
place-items: center;
rotate: 45deg;
box-shadow: 0 6px 14px rgba(58, 43, 43, 0.3);
transition: transform 0.2s ease, background 0.2s ease;
z-index: 2;
}
.pin span { rotate: -45deg; font-size: 13px; font-weight: 700; font-family: var(--sans); }
.pin:hover { transform: translate(-50%, -108%) scale(1.08); }
.pin.is-active { background: var(--gold); z-index: 3; }
.pin.is-active::after {
content: "";
position: absolute;
inset: -10px;
border-radius: 50%;
border: 2px solid rgba(201, 162, 75, 0.5);
rotate: -45deg;
animation: ping 1.6s ease-out infinite;
}
.pin:focus-visible { outline: 3px solid rgba(201, 162, 75, 0.7); outline-offset: 3px; }
@keyframes ping { 0% { transform: scale(0.8); opacity: 0.9; } 100% { transform: scale(1.5); opacity: 0; } }
.map-detail {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 28px;
align-self: start;
}
.badge {
display: inline-block;
font-size: 11px;
letter-spacing: 1.4px;
text-transform: uppercase;
font-weight: 700;
color: var(--rose);
background: var(--blush-50);
border: 1px solid rgba(201, 138, 134, 0.35);
padding: 4px 11px;
border-radius: 999px;
margin-bottom: 12px;
}
.badge-gold { color: var(--gold-d); background: rgba(201, 162, 75, 0.14); border-color: rgba(201, 162, 75, 0.4); }
.map-detail h3 { font-size: 27px; margin-bottom: 6px; }
.md-time { color: var(--gold-d); font-weight: 600; font-size: 14px; margin: 0 0 12px; }
.map-detail p { color: var(--ink-2); }
.md-walk { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); font-size: 14px; color: var(--muted); }
/* ---------- Itinerary ---------- */
.itin-tabs { display: inline-flex; gap: 6px; padding: 5px; background: var(--white); border: 1px solid var(--line); border-radius: 999px; margin: 6px 0 34px; }
.itin-tab {
border: none;
background: transparent;
font-family: var(--sans);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.4px;
color: var(--ink-2);
padding: 9px 18px;
border-radius: 999px;
cursor: pointer;
transition: background 0.2s ease, color 0.2s ease;
}
.itin-tab:hover { color: var(--gold-d); }
.itin-tab.is-active { background: var(--gold); color: var(--white); }
.itin-tab:focus-visible { outline: 3px solid rgba(201, 162, 75, 0.55); outline-offset: 2px; }
.timeline { list-style: none; margin: 0; padding: 0 0 0 8px; position: relative; }
.timeline.is-hidden { display: none; }
.timeline::before {
content: "";
position: absolute;
left: 92px;
top: 8px;
bottom: 8px;
width: 1px;
background: linear-gradient(180deg, var(--gold), rgba(201, 162, 75, 0.15));
}
.timeline li { display: grid; grid-template-columns: 96px 1fr; gap: 22px; padding: 14px 0; position: relative; }
.timeline li::before {
content: "";
position: absolute;
left: 88px;
top: 22px;
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--gold);
border: 2px solid var(--bg);
}
.tl-time { font-family: var(--serif); font-size: 19px; font-weight: 600; color: var(--gold-d); text-align: right; padding-right: 4px; }
.timeline h3 { font-size: 21px; margin-bottom: 2px; }
.timeline p { margin: 0; color: var(--ink-2); }
/* ---------- Accommodation ---------- */
.stay-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; margin-top: 26px; }
.stay-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
display: flex;
flex-direction: column;
}
.stay-card:hover { transform: translateY(-5px); box-shadow: var(--shadow); }
.stay-img { height: 150px; }
.img-1 { background: linear-gradient(135deg, #e8b7b0 0%, #c98a86 100%); }
.img-2 { background: linear-gradient(135deg, #cfe6ea 0%, #a8b8c0 100%); }
.img-3 { background: linear-gradient(135deg, #f4ece4 0%, #d8c4a8 100%); }
.stay-body { padding: 20px 22px 24px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.stay-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.stay-top h3 { font-size: 24px; }
.stay-rate { font-family: var(--serif); font-size: 28px; font-weight: 600; color: var(--ink); margin: 2px 0; }
.stay-rate span { font-family: var(--sans); font-size: 13px; font-weight: 500; color: var(--muted); }
.stay-note { font-size: 13px; color: var(--ink-2); margin: 0 0 6px; }
.copy-code { margin-top: auto; align-self: flex-start; font-variant-numeric: tabular-nums; }
/* ---------- RSVP ---------- */
.rsvp {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px 22px;
margin-top: 26px;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: clamp(24px, 4vw, 40px);
box-shadow: var(--shadow);
}
.field { display: flex; flex-direction: column; gap: 7px; margin: 0; border: 0; padding: 0; min-width: 0; }
.span-2 { grid-column: 1 / -1; }
.field label, .field legend {
font-size: 13px;
font-weight: 600;
letter-spacing: 0.3px;
color: var(--ink);
padding: 0;
}
.req { color: var(--rose); }
.field input, .field select, .field textarea {
font-family: var(--sans);
font-size: 15px;
color: var(--ink);
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: var(--r-sm);
background: var(--bg);
transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
width: 100%;
}
.field textarea { resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
outline: none;
border-color: var(--gold);
background: var(--white);
box-shadow: 0 0 0 3px rgba(201, 162, 75, 0.18);
}
.field input.invalid { border-color: var(--rose); box-shadow: 0 0 0 3px rgba(201, 138, 134, 0.18); }
.err { color: var(--rose); font-size: 12px; min-height: 14px; margin: 0; font-weight: 500; }
.choice-row { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 4px; }
.choice {
display: inline-flex;
align-items: center;
gap: 9px;
padding: 11px 18px;
border: 1px solid var(--line);
border-radius: 999px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
background: var(--bg);
transition: border-color 0.18s ease, background 0.18s ease;
}
.choice:hover { border-color: var(--rose); }
.choice input { accent-color: var(--gold-d); width: 16px; height: 16px; }
.choice:has(input:checked) { border-color: var(--gold); background: var(--blush-50); }
.choice:has(input:focus-visible) { outline: 3px solid rgba(201, 162, 75, 0.5); outline-offset: 2px; }
.rsvp-actions { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 4px; }
.rsvp-hint { font-size: 13px; color: var(--muted); margin: 0; }
.guests-only { transition: opacity 0.25s ease; }
.rsvp.declined .guests-only { display: none; }
/* ---------- Footer ---------- */
.footer {
text-align: center;
padding: clamp(44px, 7vw, 76px) 20px;
background: linear-gradient(180deg, var(--bg) 0%, #f4e9e3 100%);
border-top: 1px solid var(--line);
color: var(--ink-2);
}
.foot-mono { font-family: var(--serif); font-size: 30px; letter-spacing: 4px; color: var(--ink); margin: 0 0 12px; }
.footer p { margin: 4px 0; font-size: 14px; letter-spacing: 0.4px; }
.foot-fine { color: var(--muted); font-size: 13px; margin-top: 10px; }
/* ---------- Responsive ---------- */
@media (max-width: 880px) {
.info-grid { grid-template-columns: repeat(2, 1fr); }
.venue { grid-template-columns: 1fr; }
.stay-grid { grid-template-columns: 1fr; max-width: 440px; }
}
@media (max-width: 520px) {
.nav a:not(.nav-cta) { display: none; }
.hero-meta { flex-direction: column; gap: 4px; }
.hero-meta .dot { display: none; }
.countdown { gap: 8px; }
.cd-cell { min-width: 0; flex: 1; padding: 13px 6px; }
.info-grid { grid-template-columns: 1fr 1fr; gap: 12px; }
.info-card { padding: 20px 16px; }
.rsvp { grid-template-columns: 1fr; }
.timeline::before { left: 68px; }
.timeline li { grid-template-columns: 68px 1fr; gap: 14px; }
.timeline li::before { left: 64px; }
.tl-time { font-size: 16px; }
.itin-tab { padding: 8px 12px; font-size: 12px; }
.section.shade { border-radius: 0; }
}(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-12T16:30:00+02:00").getTime();
var cd = {
days: document.querySelector('[data-cd="days"]'),
hours: document.querySelector('[data-cd="hours"]'),
mins: document.querySelector('[data-cd="mins"]'),
secs: document.querySelector('[data-cd="secs"]')
};
function pad(n) { return (n < 10 ? "0" : "") + n; }
function tick() {
var diff = target - Date.now();
if (diff <= 0) {
cd.days.textContent = cd.hours.textContent = cd.mins.textContent = cd.secs.textContent = "00";
return;
}
var s = Math.floor(diff / 1000);
cd.days.textContent = Math.floor(s / 86400);
cd.hours.textContent = pad(Math.floor((s % 86400) / 3600));
cd.mins.textContent = pad(Math.floor((s % 3600) / 60));
cd.secs.textContent = pad(s % 60);
}
tick();
setInterval(tick, 1000);
/* ---------- Venue map ---------- */
var pinData = {
ceremony: {
badge: "Stop 1",
title: "Chiesa di Santa Croce",
time: "Ceremony · 4:30 PM",
text: "A candle-lit clifftop chapel with sweeping views over the Tyrrhenian Sea. Guests are seated by 4:15 PM.",
walk: "🚶 4 min walk from the main piazza"
},
reception: {
badge: "Stop 2",
title: "Villa Tre Ville",
time: "Reception · 7:00 PM",
text: "Dinner and dancing on a terraced garden overlooking the coast, once the retreat of composer Franco Zeffirelli.",
walk: "🚶 8 min walk · shuttle available at 6:45 PM"
},
welcome: {
badge: "Stop 3",
title: "Ristorante Il Fornillo",
time: "Welcome dinner · Fri 7:30 PM",
text: "A relaxed seafront trattoria for our Friday evening welcome feast. Family-style seafood and local wines.",
walk: "🚶 5 min walk from Hotel Marincanto"
},
view: {
badge: "Stop 4",
title: "Belvedere di Positano",
time: "Golden hour · anytime",
text: "The best panorama in town for photos before the ceremony. A short climb, but worth every step.",
walk: "🚶 10 min uphill walk from the beach"
}
};
var pins = Array.prototype.slice.call(document.querySelectorAll(".pin"));
var md = {
badge: document.getElementById("mdBadge"),
title: document.getElementById("mdTitle"),
time: document.getElementById("mdTime"),
text: document.getElementById("mdText"),
walk: document.getElementById("mdWalk")
};
function selectPin(pin) {
var key = pin.getAttribute("data-pin");
var d = pinData[key];
if (!d) return;
pins.forEach(function (p) { p.classList.remove("is-active"); });
pin.classList.add("is-active");
md.badge.textContent = d.badge;
md.title.textContent = d.title;
md.time.textContent = d.time;
md.text.textContent = d.text;
md.walk.textContent = d.walk;
}
pins.forEach(function (pin) {
pin.addEventListener("click", function () { selectPin(pin); });
});
if (pins[0]) pins[0].classList.add("is-active");
/* ---------- Itinerary tabs ---------- */
var tabs = Array.prototype.slice.call(document.querySelectorAll(".itin-tab"));
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
var day = tab.getAttribute("data-day");
tabs.forEach(function (t) {
var active = t === tab;
t.classList.toggle("is-active", active);
t.setAttribute("aria-selected", active ? "true" : "false");
});
["fri", "sat"].forEach(function (d) {
var panel = document.getElementById("tl-" + d);
if (!panel) return;
var show = d === day;
panel.classList.toggle("is-hidden", !show);
if (show) { panel.removeAttribute("hidden"); } else { panel.setAttribute("hidden", ""); }
});
});
});
/* ---------- Copy booking codes ---------- */
Array.prototype.slice.call(document.querySelectorAll(".copy-code")).forEach(function (btn) {
btn.addEventListener("click", function () {
var code = btn.getAttribute("data-code");
function done() {
btn.classList.add("copied");
toast("Copied booking code " + code);
setTimeout(function () { btn.classList.remove("copied"); }, 1400);
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(code).then(done, done);
} else {
done();
}
});
});
/* ---------- RSVP form ---------- */
var form = document.getElementById("rsvpForm");
var attendRadios = Array.prototype.slice.call(form.querySelectorAll('input[name="attend"]'));
function syncAttend() {
var declined = form.querySelector('input[name="attend"]:checked').value === "no";
form.classList.toggle("declined", declined);
}
attendRadios.forEach(function (r) { r.addEventListener("change", syncAttend); });
syncAttend();
function setError(name, msg) {
var input = form.querySelector('[name="' + name + '"]');
var errEl = form.querySelector('[data-err="' + name + '"]');
if (input) input.classList.toggle("invalid", !!msg);
if (errEl) errEl.textContent = msg || "";
}
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = form.fullName.value.trim();
var email = form.email.value.trim();
var attending = form.querySelector('input[name="attend"]:checked').value === "yes";
var ok = true;
if (!name) { setError("fullName", "Please tell us your name."); ok = false; }
else { setError("fullName", ""); }
if (!email) { setError("email", "An email helps us reach you."); ok = false; }
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { setError("email", "That email looks off."); ok = false; }
else { setError("email", ""); }
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) {
toast("Thank you, " + first + ". We will miss you dearly.");
} else {
var guests = form.guests.value;
var shuttle = form.shuttle.value;
var shuttleMsg = shuttle === "none" ? "no shuttle" : shuttle === "both" ? "round-trip shuttle" : "arrival shuttle";
toast("See you in Positano, " + first + "! Party of " + guests + " · " + shuttleMsg + ".");
}
form.querySelectorAll(".invalid").forEach(function (el) { el.classList.remove("invalid"); });
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Amara & Julian — Positano, Italy</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>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<header class="topbar">
<a class="brand" href="#top">A <span class="amp">&</span> J</a>
<nav class="nav" aria-label="Primary">
<a href="#travel">Travel</a>
<a href="#venue">Venue</a>
<a href="#itinerary">Itinerary</a>
<a href="#stay">Stay</a>
<a href="#rsvp" class="nav-cta">RSVP</a>
</nav>
</header>
<main id="top">
<!-- HERO -->
<section class="hero" aria-labelledby="hero-title">
<p class="eyebrow">Together with their families</p>
<h1 id="hero-title">Amara <span class="amp">&</span> Julian</h1>
<p class="hero-sub">are getting married on the Amalfi Coast</p>
<div class="hero-meta">
<span>Saturday, 12 September 2026</span>
<span class="dot" aria-hidden="true">•</span>
<span>Positano, Italy</span>
</div>
<div class="countdown" id="countdown" aria-label="Countdown to the ceremony">
<div class="cd-cell"><strong data-cd="days">--</strong><span>Days</span></div>
<div class="cd-cell"><strong data-cd="hours">--</strong><span>Hours</span></div>
<div class="cd-cell"><strong data-cd="mins">--</strong><span>Minutes</span></div>
<div class="cd-cell"><strong data-cd="secs">--</strong><span>Seconds</span></div>
</div>
<a href="#rsvp" class="btn btn-primary hero-btn">Reply to our invitation</a>
</section>
<!-- TRAVEL INFO -->
<section class="section" id="travel" aria-labelledby="travel-title">
<p class="kicker">Getting there</p>
<h2 id="travel-title">Travel & essentials</h2>
<div class="info-grid">
<article class="info-card">
<span class="info-ico" aria-hidden="true">✈</span>
<h3>Nearest airport</h3>
<p>Naples International (NAP)</p>
<p class="info-note">~90 min drive to Positano</p>
</article>
<article class="info-card">
<span class="info-ico" aria-hidden="true">🚛</span>
<h3>Transfers</h3>
<p>Private shuttle from NAP</p>
<p class="info-note">Sat & Sun, hourly — free for guests</p>
</article>
<article class="info-card">
<span class="info-ico" aria-hidden="true">☀</span>
<h3>Weather</h3>
<p>Warm & breezy, ~26°C</p>
<p class="info-note">Light layers for evening</p>
</article>
<article class="info-card">
<span class="info-ico" aria-hidden="true">👜</span>
<h3>Dress code</h3>
<p>Coastal formal</p>
<p class="info-note">Block heels advised on cobblestones</p>
</article>
</div>
</section>
<!-- VENUE MAP -->
<section class="section shade" id="venue" aria-labelledby="venue-title">
<p class="kicker">Wayfinding</p>
<h2 id="venue-title">The venue map</h2>
<p class="section-lede">Tap a pin to see the details. Everything sits within a short walk along the cliffside.</p>
<div class="venue">
<div class="map" id="map" role="group" aria-label="Interactive venue map">
<div class="map-water"></div>
<div class="map-coast"></div>
<button class="pin" data-pin="ceremony" style="left:28%;top:38%" aria-label="Ceremony pin"><span>1</span></button>
<button class="pin" data-pin="reception" style="left:58%;top:52%" aria-label="Reception pin"><span>2</span></button>
<button class="pin" data-pin="welcome" style="left:44%;top:70%" aria-label="Welcome dinner pin"><span>3</span></button>
<button class="pin" data-pin="view" style="left:74%;top:30%" aria-label="Viewpoint pin"><span>4</span></button>
</div>
<aside class="map-detail" id="mapDetail" aria-live="polite">
<span class="badge" id="mdBadge">Stop 1</span>
<h3 id="mdTitle">Chiesa di Santa Croce</h3>
<p class="md-time" id="mdTime">Ceremony · 4:30 PM</p>
<p id="mdText">A candle-lit clifftop chapel with sweeping views over the Tyrrhenian Sea. Guests are seated by 4:15 PM.</p>
<p class="md-walk" id="mdWalk">🚶 4 min walk from the main piazza</p>
</aside>
</div>
</section>
<!-- ITINERARY -->
<section class="section" id="itinerary" aria-labelledby="itin-title">
<p class="kicker">Two days by the sea</p>
<h2 id="itin-title">The itinerary</h2>
<div class="itin-tabs" role="tablist" aria-label="Itinerary days">
<button class="itin-tab is-active" role="tab" aria-selected="true" id="tab-fri" data-day="fri">Fri 11 Sep · Welcome</button>
<button class="itin-tab" role="tab" aria-selected="false" id="tab-sat" data-day="sat">Sat 12 Sep · Wedding</button>
</div>
<ol class="timeline" id="tl-fri" role="tabpanel" aria-labelledby="tab-fri">
<li><span class="tl-time">5:00 PM</span><div><h3>Arrival aperitivo</h3><p>Spritz & canapés on the terrace of Hotel Marincanto.</p></div></li>
<li><span class="tl-time">7:30 PM</span><div><h3>Welcome dinner</h3><p>Family-style seafood feast at Ristorante Il Fornillo.</p></div></li>
<li><span class="tl-time">10:00 PM</span><div><h3>Sunset stroll</h3><p>Optional walk down to Spiaggia Grande for a nightcap.</p></div></li>
</ol>
<ol class="timeline is-hidden" id="tl-sat" role="tabpanel" aria-labelledby="tab-sat" hidden>
<li><span class="tl-time">4:30 PM</span><div><h3>Ceremony</h3><p>Vows at Chiesa di Santa Croce. Please be seated by 4:15 PM.</p></div></li>
<li><span class="tl-time">5:30 PM</span><div><h3>Cocktail hour</h3><p>Lemon gardens, live guitar & limoncello tasting.</p></div></li>
<li><span class="tl-time">7:00 PM</span><div><h3>Reception & dinner</h3><p>Five courses under the stars at Villa Tre Ville.</p></div></li>
<li><span class="tl-time">10:00 PM</span><div><h3>Dancing</h3><p>First dance, then the floor is yours until late.</p></div></li>
</ol>
</section>
<!-- ACCOMMODATION -->
<section class="section shade" id="stay" aria-labelledby="stay-title">
<p class="kicker">Where to rest</p>
<h2 id="stay-title">Accommodation blocks</h2>
<p class="section-lede">We’ve held rooms at three hotels. Mention the booking code for the group rate.</p>
<div class="stay-grid">
<article class="stay-card">
<div class="stay-img img-1" aria-hidden="true"></div>
<div class="stay-body">
<div class="stay-top">
<h3>Hotel Marincanto</h3>
<span class="badge badge-gold">Host hotel</span>
</div>
<p class="stay-rate">€340 <span>/ night</span></p>
<p class="stay-note">🚶 2 min to ceremony · sea-view rooms</p>
<button class="btn btn-ghost copy-code" data-code="AJ-MARIN26">Copy code · AJ-MARIN26</button>
</div>
</article>
<article class="stay-card">
<div class="stay-img img-2" aria-hidden="true"></div>
<div class="stay-body">
<div class="stay-top">
<h3>Palazzo Talamo</h3>
<span class="badge">Boutique</span>
</div>
<p class="stay-rate">€260 <span>/ night</span></p>
<p class="stay-note">🚶 6 min to reception · rooftop pool</p>
<button class="btn btn-ghost copy-code" data-code="AJ-TALAMO26">Copy code · AJ-TALAMO26</button>
</div>
</article>
<article class="stay-card">
<div class="stay-img img-3" aria-hidden="true"></div>
<div class="stay-body">
<div class="stay-top">
<h3>Villa Fornillo</h3>
<span class="badge">Best value</span>
</div>
<p class="stay-rate">€185 <span>/ night</span></p>
<p class="stay-note">🚶 9 min · garden apartments for families</p>
<button class="btn btn-ghost copy-code" data-code="AJ-FORNIL26">Copy code · AJ-FORNIL26</button>
</div>
</article>
</div>
</section>
<!-- RSVP -->
<section class="section" id="rsvp" aria-labelledby="rsvp-title">
<p class="kicker">Kindly reply by 1 August</p>
<h2 id="rsvp-title">RSVP & travel details</h2>
<form class="rsvp" id="rsvpForm" novalidate>
<div class="field">
<label for="fullName">Full name <span class="req">*</span></label>
<input id="fullName" name="fullName" type="text" autocomplete="name" required placeholder="Amara Whitfield" />
<p class="err" data-err="fullName"></p>
</div>
<div class="field">
<label for="email">Email <span class="req">*</span></label>
<input id="email" name="email" type="email" autocomplete="email" required placeholder="[email protected]" />
<p class="err" data-err="email"></p>
</div>
<fieldset class="field span-2">
<legend>Will you join us? <span class="req">*</span></legend>
<div class="choice-row">
<label class="choice"><input type="radio" name="attend" value="yes" checked /> <span>Joyfully accept</span></label>
<label class="choice"><input type="radio" name="attend" value="no" /> <span>Regretfully decline</span></label>
</div>
</fieldset>
<div class="field guests-only">
<label for="guests">Number of guests</label>
<select id="guests" name="guests">
<option value="1">Just me</option>
<option value="2" selected>2 — plus one</option>
<option value="3">3</option>
<option value="4">4 — family</option>
</select>
</div>
<div class="field guests-only">
<label for="shuttle">Airport shuttle</label>
<select id="shuttle" name="shuttle">
<option value="none">No, arranging my own</option>
<option value="arrival" selected>Yes — arrival only</option>
<option value="both">Yes — arrival & return</option>
</select>
</div>
<div class="field guests-only">
<label for="arrive">Arrival date</label>
<input id="arrive" name="arrive" type="date" value="2026-09-11" />
</div>
<div class="field guests-only">
<label for="depart">Departure date</label>
<input id="depart" name="depart" type="date" value="2026-09-13" />
</div>
<div class="field span-2 guests-only">
<label for="diet">Dietary notes</label>
<textarea id="diet" name="diet" rows="3" placeholder="Allergies, vegetarian, vegan…"></textarea>
</div>
<div class="rsvp-actions span-2">
<button type="submit" class="btn btn-primary">Send RSVP</button>
<p class="rsvp-hint">A confirmation will appear once you reply.</p>
</div>
</form>
</section>
</main>
<footer class="footer">
<p class="foot-mono">A <span class="amp">&</span> J</p>
<p>Amara & Julian · 12 September 2026 · Positano, Italy</p>
<p class="foot-fine">Questions? Reach our planner at [email protected]</p>
</footer>
<script src="script.js"></script>
</body>
</html>A romantic, editorial landing page for a destination wedding on the Amalfi Coast. The hero pairs a Cormorant Garamond monogram with a live countdown that ticks down to the ceremony date, set over an airy blush backdrop with thin gold rules. A travel-info strip summarizes the nearest airport, transfer time and dress code so guests can plan at a glance.
The middle of the page is built for wayfinding: an interactive venue map with selectable pins (ceremony, reception, welcome dinner, viewpoint) that update a details panel, and a two-day itinerary rendered as a gold-accented timeline. Accommodation blocks are presented as cards with nightly rates, walking distance and a copyable booking code, each with hover lift and focus states.
The RSVP section closes the page with a fully keyboard-usable form that captures attendance, guest count, arrival and departure dates, dietary preferences and airport-shuttle needs. Submitting validates the required fields, then confirms with a toast and a personalized summary. Everything is vanilla JS, WCAG-AA contrast, and responsive down to ~360px.