Video — Landing Wedding
A romantic-cinematic wedding film landing with a letterboxed hero reel, a filterable sample films gallery, three tiered packages with a monthly-installments toggle, star-rated couple testimonials in a rotating carousel, and a validated enquire form that opens as a focus-trapped modal with a live availability check and toast confirmations. Fully responsive, keyboard accessible, amber-accented on cinematic black.
MCP
Code
:root {
--bg: #0a0a0b;
--surface: #141416;
--surface-2: #1c1c1f;
--amber: #ffb020;
--amber-d: #e6971a;
--red: #ff4d4d;
--ink: #f4f4f6;
--muted: #8a8a92;
--line: rgba(255, 255, 255, 0.10);
--line-2: rgba(255, 255, 255, 0.18);
--white: #fff;
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
--sans: "Inter", system-ui, sans-serif;
--mono: "JetBrains Mono", ui-monospace, monospace;
--serif: "Playfair Display", Georgia, serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2, h3 { margin: 0; line-height: 1.1; letter-spacing: -0.02em; }
a { color: inherit; text-decoration: none; }
.serif { font-family: var(--serif); font-style: italic; font-weight: 500; color: var(--amber); }
/* focus */
:focus-visible {
outline: 2px solid var(--amber);
outline-offset: 3px;
border-radius: 3px;
}
/* scroll progress */
.scrollbar {
position: fixed;
top: 0; left: 0;
height: 2px;
width: 0%;
background: linear-gradient(90deg, var(--amber-d), var(--amber));
z-index: 60;
box-shadow: 0 0 10px rgba(255, 176, 32, 0.6);
}
/* ===== buttons ===== */
.btn {
font-family: var(--sans);
font-weight: 600;
font-size: 14px;
border: 1px solid transparent;
border-radius: var(--r-md);
padding: 11px 20px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 9px;
transition: transform 0.12s ease, background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}
.btn:active { transform: translateY(1px) scale(0.99); }
.btn-primary {
background: var(--amber);
color: #1a1205;
box-shadow: 0 6px 22px -8px rgba(255, 176, 32, 0.65);
}
.btn-primary:hover { background: var(--amber-d); box-shadow: 0 10px 28px -8px rgba(255, 176, 32, 0.8); }
.btn-ghost {
background: transparent;
color: var(--ink);
border-color: var(--line-2);
}
.btn-ghost:hover { background: var(--surface-2); border-color: var(--amber); }
.btn-lg { padding: 15px 30px; font-size: 15px; }
.btn-block { width: 100%; justify-content: center; padding: 14px; }
.play-ico {
width: 0; height: 0;
border-left: 9px solid #1a1205;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
transition: opacity 0.15s;
}
.is-playing .play-ico {
border-left: 4px solid #1a1205;
border-right: 4px solid #1a1205;
border-top: 0; border-bottom: 0;
height: 12px;
}
/* ===== topbar ===== */
.topbar {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 50;
display: flex;
align-items: center;
gap: 20px;
padding: 14px clamp(16px, 4vw, 44px);
background: rgba(10, 10, 11, 0.55);
backdrop-filter: blur(12px);
border-bottom: 1px solid transparent;
transition: background 0.25s, border-color 0.25s;
}
.topbar.scrolled {
background: rgba(10, 10, 11, 0.88);
border-bottom-color: var(--line);
}
.brand { display: flex; align-items: center; gap: 9px; }
.brand-mark { color: var(--amber); font-size: 20px; }
.brand-name { font-weight: 800; letter-spacing: -0.02em; font-size: 17px; }
.brand-name em { font-style: normal; color: var(--amber); font-weight: 500; }
.nav {
margin-left: auto;
display: flex;
gap: 26px;
font-size: 14px;
font-weight: 500;
color: var(--muted);
}
.nav a { transition: color 0.15s; }
.nav a:hover { color: var(--ink); }
.nav-cta { margin-left: 4px; }
/* ===== hero ===== */
.hero { position: relative; padding-top: 62px; }
.bar { background: #000; height: 46px; }
.bar.thin { height: 26px; }
.hero-media {
position: relative;
min-height: 78vh;
display: flex;
align-items: center;
padding: clamp(28px, 7vw, 80px);
overflow: hidden;
background:
radial-gradient(120% 90% at 80% 15%, rgba(255, 176, 32, 0.16), transparent 55%),
radial-gradient(90% 80% at 15% 90%, rgba(255, 77, 77, 0.10), transparent 60%),
linear-gradient(160deg, #1a140c 0%, #0c0c0e 55%, #0a0a0b 100%);
}
.grain {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.5;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}
.hero-inner { position: relative; max-width: 640px; }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 9px;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--amber);
margin: 0 0 22px;
padding: 7px 14px;
border: 1px solid var(--line-2);
border-radius: 999px;
background: rgba(0, 0, 0, 0.35);
}
.rec {
width: 8px; height: 8px;
border-radius: 50%;
background: var(--red);
box-shadow: 0 0 0 0 rgba(255, 77, 77, 0.6);
animation: pulse 1.8s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 77, 77, 0.6); }
70% { box-shadow: 0 0 0 8px rgba(255, 77, 77, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 77, 77, 0); }
}
.hero-title {
font-size: clamp(38px, 7vw, 74px);
font-weight: 800;
}
.hero-sub {
color: var(--muted);
font-size: clamp(15px, 2vw, 18px);
max-width: 480px;
margin: 22px 0 30px;
}
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; }
.hero-stats {
display: flex;
gap: 34px;
margin: 42px 0 0;
flex-wrap: wrap;
}
.hero-stats div { display: flex; flex-direction: column; }
.hero-stats dt {
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 4px;
}
.hero-stats dd {
margin: 0;
font-size: 26px;
font-weight: 700;
font-family: var(--mono);
color: var(--ink);
}
.hero-stats dd span { color: var(--muted); font-size: 15px; }
.hero-tc {
position: absolute;
bottom: 18px;
right: 22px;
font-family: var(--mono);
font-size: 12px;
color: var(--muted);
letter-spacing: 0.04em;
}
.hero-tc #reelTc { color: var(--amber); }
/* ===== sections ===== */
.section { padding: clamp(56px, 9vw, 110px) clamp(16px, 5vw, 64px); max-width: 1200px; margin: 0 auto; }
.section-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 22px;
flex-wrap: wrap;
margin-bottom: 38px;
}
.section-head.center { flex-direction: column; align-items: center; text-align: center; }
.kicker {
font-size: 12px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--amber);
font-weight: 600;
margin: 0 0 10px;
}
.section-title { font-size: clamp(28px, 4.5vw, 46px); font-weight: 700; }
/* filters */
.filters { display: flex; gap: 8px; flex-wrap: wrap; }
.chip {
font-family: var(--sans);
font-size: 13px;
font-weight: 500;
padding: 8px 16px;
border-radius: 999px;
border: 1px solid var(--line-2);
background: transparent;
color: var(--muted);
cursor: pointer;
transition: all 0.16s;
}
.chip:hover { color: var(--ink); border-color: var(--amber); }
.chip.is-active { background: var(--amber); color: #1a1205; border-color: var(--amber); font-weight: 600; }
/* film grid */
.film-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.film {
position: relative;
border-radius: var(--r-lg);
overflow: hidden;
border: 1px solid var(--line);
background: var(--surface);
cursor: pointer;
transition: transform 0.2s ease, border-color 0.2s, box-shadow 0.2s;
opacity: 0;
transform: translateY(16px);
animation: rise 0.5s forwards;
}
@keyframes rise { to { opacity: 1; transform: none; } }
.film:hover, .film:focus-visible {
transform: translateY(-4px);
border-color: var(--line-2);
box-shadow: 0 20px 40px -20px rgba(0, 0, 0, 0.9);
}
.film-thumb {
position: relative;
aspect-ratio: 16 / 10;
background-size: cover;
background-position: center;
}
.film-thumb::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent 40%, rgba(10, 10, 11, 0.9));
}
.film-play {
position: absolute;
inset: 0;
margin: auto;
width: 56px; height: 56px;
border-radius: 50%;
background: rgba(10, 10, 11, 0.55);
border: 1px solid var(--line-2);
backdrop-filter: blur(4px);
display: grid;
place-items: center;
z-index: 2;
transition: background 0.18s, transform 0.18s;
}
.film:hover .film-play { background: var(--amber); transform: scale(1.06); }
.film-play::before {
content: "";
width: 0; height: 0;
border-left: 13px solid var(--ink);
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
margin-left: 3px;
transition: border-left-color 0.18s;
}
.film:hover .film-play::before { border-left-color: #1a1205; }
.film-badge {
position: absolute;
top: 12px; left: 12px;
z-index: 2;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 999px;
background: rgba(10, 10, 11, 0.7);
border: 1px solid var(--line-2);
color: var(--amber);
}
.film-tc {
position: absolute;
bottom: 12px; right: 12px;
z-index: 2;
font-family: var(--mono);
font-size: 12px;
color: var(--ink);
background: rgba(10, 10, 11, 0.65);
padding: 3px 8px;
border-radius: var(--r-sm);
letter-spacing: 0.03em;
}
.film-info { padding: 16px 18px 18px; }
.film-title { font-size: 18px; font-weight: 700; }
.film-meta {
display: flex;
align-items: center;
gap: 8px;
margin-top: 6px;
color: var(--muted);
font-size: 13px;
}
.film-meta .dot { width: 3px; height: 3px; border-radius: 50%; background: var(--muted); }
/* ===== packages ===== */
.packages { max-width: 1140px; }
.toggle {
display: inline-flex;
padding: 4px;
gap: 4px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 999px;
}
.toggle-opt {
font-family: var(--sans);
font-size: 13px;
font-weight: 600;
color: var(--muted);
border: none;
background: transparent;
padding: 9px 18px;
border-radius: 999px;
cursor: pointer;
transition: color 0.16s, background 0.16s;
}
.toggle-opt.is-active { background: var(--amber); color: #1a1205; }
.toggle-opt .save { font-size: 11px; opacity: 0.75; }
.pkg-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
}
.pkg {
position: relative;
border: 1px solid var(--line);
background: var(--surface);
border-radius: var(--r-lg);
padding: 30px 26px;
display: flex;
flex-direction: column;
transition: transform 0.2s, border-color 0.2s;
}
.pkg:hover { transform: translateY(-4px); border-color: var(--line-2); }
.pkg.featured {
border-color: var(--amber);
background: linear-gradient(180deg, rgba(255, 176, 32, 0.08), var(--surface) 60%);
box-shadow: 0 24px 60px -30px rgba(255, 176, 32, 0.5);
}
.pkg-tag {
position: absolute;
top: -11px; left: 50%;
transform: translateX(-50%);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
background: var(--amber);
color: #1a1205;
padding: 5px 14px;
border-radius: 999px;
}
.pkg-name { font-size: 14px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--amber); }
.pkg-desc { color: var(--muted); font-size: 13px; margin: 8px 0 20px; min-height: 38px; }
.pkg-price { display: flex; align-items: baseline; gap: 4px; }
.pkg-price .cur { font-size: 20px; font-weight: 600; color: var(--muted); }
.pkg-price .num { font-size: 44px; font-weight: 800; font-family: var(--mono); letter-spacing: -0.02em; }
.pkg-price .per { font-size: 13px; color: var(--muted); }
.pkg-sub { font-size: 12px; color: var(--muted); margin: 6px 0 0; min-height: 16px; }
.pkg-feat { list-style: none; padding: 0; margin: 22px 0 26px; display: flex; flex-direction: column; gap: 11px; flex: 1; }
.pkg-feat li { display: flex; gap: 10px; font-size: 14px; color: #d6d6db; }
.pkg-feat li::before {
content: "";
flex: none;
width: 16px; height: 16px;
margin-top: 2px;
border-radius: 50%;
background: rgba(255, 176, 32, 0.16);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23ffb020' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8.5l2.5 2.5L12 5'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
}
.pkg-note { text-align: center; color: var(--muted); font-size: 13px; margin: 30px auto 0; max-width: 620px; }
/* ===== testimonials ===== */
.love { max-width: 900px; }
.carousel { position: relative; display: flex; align-items: center; gap: 12px; }
.car-viewport { overflow: hidden; flex: 1; border-radius: var(--r-lg); }
.car-track { display: flex; transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1); }
.quote {
flex: 0 0 100%;
padding: 40px clamp(24px, 5vw, 56px);
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
text-align: center;
}
.stars { color: var(--amber); font-size: 16px; letter-spacing: 3px; margin-bottom: 18px; }
.quote-text { font-family: var(--serif); font-style: italic; font-size: clamp(19px, 3vw, 26px); line-height: 1.4; color: var(--ink); }
.quote-by { margin-top: 22px; display: flex; align-items: center; justify-content: center; gap: 12px; }
.quote-av {
width: 42px; height: 42px;
border-radius: 50%;
background-size: cover;
background-position: center;
border: 1px solid var(--line-2);
}
.quote-name { font-weight: 700; font-size: 14px; }
.quote-detail { font-size: 12px; color: var(--muted); }
.car-nav {
flex: none;
width: 44px; height: 44px;
border-radius: 50%;
border: 1px solid var(--line-2);
background: var(--surface);
color: var(--ink);
font-size: 22px;
cursor: pointer;
transition: background 0.16s, border-color 0.16s;
}
.car-nav:hover { background: var(--amber); color: #1a1205; border-color: var(--amber); }
.dots { display: flex; justify-content: center; gap: 8px; margin-top: 22px; }
.dot-btn {
width: 8px; height: 8px;
border-radius: 50%;
border: none;
background: var(--line-2);
cursor: pointer;
padding: 0;
transition: background 0.16s, width 0.16s;
}
.dot-btn.is-active { background: var(--amber); width: 22px; border-radius: 4px; }
/* ===== CTA ===== */
.cta { position: relative; margin-top: 40px; }
.cta-inner {
text-align: center;
padding: clamp(56px, 10vw, 120px) 20px;
background:
radial-gradient(90% 120% at 50% 0%, rgba(255, 176, 32, 0.14), transparent 60%),
linear-gradient(180deg, #100c07, #0a0a0b);
}
.cta-title { font-size: clamp(30px, 6vw, 60px); font-weight: 800; }
.cta-sub { color: var(--muted); max-width: 440px; margin: 18px auto 30px; font-size: 16px; }
/* ===== footer ===== */
.footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
padding: 26px clamp(16px, 5vw, 44px);
border-top: 1px solid var(--line);
color: var(--muted);
font-size: 13px;
}
.foot-tc { font-family: var(--mono); font-size: 12px; }
/* ===== modal ===== */
.modal { position: fixed; inset: 0; z-index: 80; display: grid; place-items: center; padding: 20px; }
.modal[hidden] { display: none; }
.modal-scrim { position: absolute; inset: 0; background: rgba(3, 3, 4, 0.78); backdrop-filter: blur(4px); animation: fade 0.2s; }
@keyframes fade { from { opacity: 0; } }
.modal-card {
position: relative;
width: min(520px, 100%);
max-height: 92vh;
overflow-y: auto;
background: var(--surface);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
padding: 30px clamp(22px, 4vw, 34px) 30px;
box-shadow: 0 40px 90px -30px #000;
animation: pop 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes pop { from { opacity: 0; transform: translateY(14px) scale(0.98); } }
.modal-x {
position: absolute;
top: 16px; right: 16px;
width: 34px; height: 34px;
border-radius: 50%;
border: 1px solid var(--line);
background: var(--surface-2);
color: var(--muted);
cursor: pointer;
font-size: 13px;
transition: color 0.15s, border-color 0.15s;
}
.modal-x:hover { color: var(--ink); border-color: var(--amber); }
.modal-title { font-size: 26px; font-weight: 700; margin-bottom: 20px; }
.field { margin-bottom: 16px; display: flex; flex-direction: column; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.field label { font-size: 13px; font-weight: 600; margin-bottom: 7px; }
.field label .opt { color: var(--muted); font-weight: 400; }
.field input, .field select, .field textarea {
font-family: var(--sans);
font-size: 14px;
color: var(--ink);
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 11px 13px;
transition: border-color 0.16s, box-shadow 0.16s;
width: 100%;
}
.field textarea { resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
outline: none;
border-color: var(--amber);
box-shadow: 0 0 0 3px rgba(255, 176, 32, 0.18);
}
.field.invalid input, .field.invalid select { border-color: var(--red); }
.err { color: var(--red); font-size: 12px; margin-top: 6px; min-height: 0; display: none; }
.field.invalid .err { display: block; }
.avail {
font-size: 13px;
font-weight: 500;
padding: 11px 14px;
border-radius: var(--r-md);
margin-bottom: 16px;
border: 1px solid var(--line);
}
.avail.ok { color: #7ee0a0; border-color: rgba(126, 224, 160, 0.4); background: rgba(126, 224, 160, 0.08); }
.avail.busy { color: var(--red); border-color: rgba(255, 77, 77, 0.4); background: rgba(255, 77, 77, 0.08); }
.avail.check { color: var(--amber); }
/* ===== toast ===== */
.toast-wrap {
position: fixed;
bottom: 22px; right: 22px;
z-index: 90;
display: flex;
flex-direction: column;
gap: 10px;
}
.toast {
background: var(--surface-2);
border: 1px solid var(--line-2);
border-left: 3px solid var(--amber);
color: var(--ink);
padding: 13px 18px;
border-radius: var(--r-md);
font-size: 14px;
max-width: 320px;
box-shadow: 0 16px 40px -16px #000;
animation: toastIn 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.toast.out { animation: toastOut 0.3s forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateX(24px); } }
@keyframes toastOut { to { opacity: 0; transform: translateX(24px); } }
/* ===== responsive ===== */
@media (max-width: 520px) {
.nav { display: none; }
.bar { height: 30px; }
.hero-media { min-height: 70vh; padding: 26px 20px; }
.hero-stats { gap: 22px; }
.hero-stats dd { font-size: 21px; }
.section-head { flex-direction: column; align-items: flex-start; }
.filters { width: 100%; }
.film-grid { grid-template-columns: 1fr; }
.field-row { grid-template-columns: 1fr; }
.car-nav { width: 38px; height: 38px; }
.quote { padding: 30px 20px; }
.footer { flex-direction: column; align-items: flex-start; }
.toast-wrap { left: 16px; right: 16px; }
.toast { max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; scroll-behavior: auto; }
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastWrap = document.getElementById("toastWrap");
function toast(msg) {
var t = document.createElement("div");
t.className = "toast";
t.textContent = msg;
toastWrap.appendChild(t);
setTimeout(function () {
t.classList.add("out");
setTimeout(function () { t.remove(); }, 320);
}, 3400);
}
/* ---------- data ---------- */
var films = [
{ title: "Mara & Jules", loc: "Blue Ridge, NC", cat: "cinematic", style: "Cinematic", run: "00:04:12", grad: "linear-gradient(135deg,#7a4a2b,#2a1810)" },
{ title: "Priya & Dev", loc: "Charleston, SC", cat: "documentary", style: "Documentary", run: "00:06:38", grad: "linear-gradient(135deg,#4a5a72,#181d28)" },
{ title: "Elena & Sam", loc: "Dolomites, IT", cat: "elopement", style: "Elopement", run: "00:03:05", grad: "linear-gradient(135deg,#6b7a5a,#1c2216)" },
{ title: "Noa & Theo", loc: "Big Sur, CA", cat: "cinematic", style: "Cinematic", run: "00:05:20", grad: "linear-gradient(135deg,#8a5a4a,#2a1712)" },
{ title: "Rosa & Cai", loc: "Savannah, GA", cat: "documentary", style: "Documentary", run: "00:07:01", grad: "linear-gradient(135deg,#5a4a6b,#1e1826)" },
{ title: "Ivy & Wren", loc: "Isle of Skye, UK", cat: "elopement", style: "Elopement", run: "00:02:48", grad: "linear-gradient(135deg,#4a6b6a,#16211f)" }
];
var packages = [
{
name: "Highlight", once: 2400, desc: "A short, keepsake film for intimate days.",
feat: ["6-hour coverage, one filmmaker", "3–4 min highlight film", "Licensed music + colour grade", "Delivery in 6 weeks"]
},
{
name: "Signature", once: 3900, featured: true, desc: "Our most-booked — full-day story, beautifully told.",
feat: ["10-hour coverage, two filmmakers", "6–8 min feature film", "60-sec teaser for socials", "Ceremony & speeches in full", "Delivery in 5 weeks"]
},
{
name: "Heirloom", once: 6200, desc: "The complete cinematic archive of your weekend.",
feat: ["Two days, two filmmakers", "10-min feature + full ceremony", "Drone + second-angle coverage", "Rehearsal-dinner short film", "Engraved USB + priority edit"]
}
];
var testimonials = [
{ text: "We ugly-cried the second we hit play. Aurelia caught moments we didn't even know happened.", name: "Mara & Jules", detail: "Married May 2027 · Blue Ridge", av: "linear-gradient(135deg,#c98a5a,#5a2f1c)" },
{ text: "Invisible all day, then handed us a film that felt like a memory more than a video.", name: "Priya & Dev", detail: "Married Apr 2027 · Charleston", av: "linear-gradient(135deg,#5a7ac9,#1c2f5a)" },
{ text: "Our elopement was just us and a mountain. The film makes it look like a feature.", name: "Elena & Sam", detail: "Eloped Sep 2026 · Dolomites", av: "linear-gradient(135deg,#7ac95a,#2f5a1c)" },
{ text: "Booking Aurelia was the single best decision of our whole planning process.", name: "Noa & Theo", detail: "Married Jun 2027 · Big Sur", av: "linear-gradient(135deg,#c95a9a,#5a1c3f)" }
];
/* ---------- render films ---------- */
var filmGrid = document.getElementById("filmGrid");
function renderFilms(filter) {
filmGrid.innerHTML = "";
films
.filter(function (f) { return filter === "all" || f.cat === filter; })
.forEach(function (f, i) {
var el = document.createElement("article");
el.className = "film";
el.tabIndex = 0;
el.setAttribute("role", "button");
el.setAttribute("aria-label", "Play film: " + f.title);
el.style.animationDelay = (i * 60) + "ms";
el.innerHTML =
'<div class="film-thumb" style="background-image:' + f.grad + '">' +
'<span class="film-badge">' + f.style + '</span>' +
'<span class="film-play"></span>' +
'<span class="film-tc" data-run="' + f.run + '">' + f.run + '</span>' +
'</div>' +
'<div class="film-info">' +
'<h3 class="film-title">' + f.title + '</h3>' +
'<p class="film-meta"><span>' + f.loc + '</span><span class="dot"></span><span>' + f.run + '</span></p>' +
'</div>';
function open() { toast("▶ Opening “" + f.title + "” — " + f.run); }
el.addEventListener("click", open);
el.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); open(); }
});
// hover scrub timecode
var tc = el.querySelector(".film-tc");
var scrub;
el.addEventListener("mouseenter", function () {
var t = 0;
scrub = setInterval(function () {
t += 7;
var m = Math.floor(t / 60), s = t % 60;
tc.textContent = "00:0" + m + ":" + String(s).padStart(2, "0");
if (t > 240) t = 0;
}, 90);
});
el.addEventListener("mouseleave", function () {
clearInterval(scrub);
tc.textContent = f.run;
});
filmGrid.appendChild(el);
});
}
renderFilms("all");
/* ---------- filters ---------- */
var chips = Array.prototype.slice.call(document.querySelectorAll(".chip"));
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) { c.classList.remove("is-active"); c.setAttribute("aria-pressed", "false"); });
chip.classList.add("is-active");
chip.setAttribute("aria-pressed", "true");
renderFilms(chip.dataset.filter);
});
});
/* ---------- packages ---------- */
var pkgGrid = document.getElementById("pkgGrid");
var currentPlan = "once";
function money(n) { return n.toLocaleString("en-US"); }
function renderPackages() {
pkgGrid.innerHTML = "";
packages.forEach(function (p) {
var el = document.createElement("article");
el.className = "pkg" + (p.featured ? " featured" : "");
var num, per, sub;
if (currentPlan === "installments") {
num = money(Math.round(p.once / 12));
per = "/mo";
sub = "×12 months · $" + money(p.once) + " total, 0% APR";
} else {
num = money(p.once);
per = "one-off";
sub = "Save vs. monthly · locked at booking";
}
el.innerHTML =
(p.featured ? '<span class="pkg-tag">Most booked</span>' : "") +
'<p class="pkg-name">' + p.name + '</p>' +
'<p class="pkg-desc">' + p.desc + '</p>' +
'<div class="pkg-price"><span class="cur">$</span><span class="num">' + num + '</span><span class="per">' + per + '</span></div>' +
'<p class="pkg-sub">' + sub + '</p>' +
'<ul class="pkg-feat">' + p.feat.map(function (f) { return "<li>" + f + "</li>"; }).join("") + '</ul>' +
'<button class="btn ' + (p.featured ? "btn-primary" : "btn-ghost") + ' btn-block" data-pkg="' + p.name + '">Enquire · ' + p.name + '</button>';
el.querySelector("[data-pkg]").addEventListener("click", function () {
openModal(p.name);
});
pkgGrid.appendChild(el);
});
}
renderPackages();
var planBtns = Array.prototype.slice.call(document.querySelectorAll(".toggle-opt"));
planBtns.forEach(function (b) {
b.addEventListener("click", function () {
planBtns.forEach(function (x) { x.classList.remove("is-active"); x.setAttribute("aria-pressed", "false"); });
b.classList.add("is-active");
b.setAttribute("aria-pressed", "true");
currentPlan = b.dataset.plan;
renderPackages();
});
});
/* ---------- testimonials carousel ---------- */
var track = document.getElementById("carTrack");
var dotsWrap = document.getElementById("carDots");
var idx = 0, timer;
testimonials.forEach(function (q, i) {
var el = document.createElement("div");
el.className = "quote";
el.setAttribute("role", "group");
el.setAttribute("aria-roledescription", "slide");
el.setAttribute("aria-label", (i + 1) + " of " + testimonials.length);
el.innerHTML =
'<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>' +
'<p class="quote-text">“' + q.text + '”</p>' +
'<div class="quote-by">' +
'<span class="quote-av" style="background-image:' + q.av + '"></span>' +
'<span><span class="quote-name">' + q.name + '</span><br><span class="quote-detail">' + q.detail + '</span></span>' +
'</div>';
track.appendChild(el);
var d = document.createElement("button");
d.className = "dot-btn" + (i === 0 ? " is-active" : "");
d.setAttribute("role", "tab");
d.setAttribute("aria-label", "Testimonial " + (i + 1));
d.addEventListener("click", function () { go(i); });
dotsWrap.appendChild(d);
});
var dots = Array.prototype.slice.call(dotsWrap.children);
function go(n) {
idx = (n + testimonials.length) % testimonials.length;
track.style.transform = "translateX(-" + (idx * 100) + "%)";
dots.forEach(function (d, i) { d.classList.toggle("is-active", i === idx); d.setAttribute("aria-selected", i === idx); });
restart();
}
function restart() {
clearInterval(timer);
timer = setInterval(function () { go(idx + 1); }, 6000);
}
document.getElementById("carNext").addEventListener("click", function () { go(idx + 1); });
document.getElementById("carPrev").addEventListener("click", function () { go(idx - 1); });
var carousel = document.getElementById("carousel");
carousel.addEventListener("mouseenter", function () { clearInterval(timer); });
carousel.addEventListener("mouseleave", restart);
carousel.setAttribute("tabindex", "0");
carousel.addEventListener("keydown", function (e) {
if (e.key === "ArrowRight") { e.preventDefault(); go(idx + 1); }
if (e.key === "ArrowLeft") { e.preventDefault(); go(idx - 1); }
});
restart();
/* ---------- hero reel play toggle ---------- */
var playBtn = document.getElementById("playReel");
var reelTc = document.getElementById("reelTc");
var playing = false, reelTimer, frame = 0;
playBtn.addEventListener("click", function () {
playing = !playing;
playBtn.classList.toggle("is-playing", playing);
playBtn.setAttribute("aria-pressed", String(playing));
playBtn.querySelector(".play-label").textContent = playing ? "Pause reel" : "Watch the reel";
if (playing) {
toast("▶ Playing showreel · 02:14");
reelTimer = setInterval(function () {
frame++;
var totalF = frame;
var ff = totalF % 24;
var secs = Math.floor(totalF / 24);
var m = Math.floor(secs / 60), s = secs % 60;
reelTc.textContent = "00:0" + m + ":" + String(s).padStart(2, "0") + ":" + String(ff).padStart(2, "0");
}, 42);
} else {
clearInterval(reelTimer);
}
});
/* ---------- scroll progress + topbar ---------- */
var scrollbar = document.getElementById("scrollbar");
var topbar = document.getElementById("topbar");
function onScroll() {
var h = document.documentElement;
var pct = h.scrollTop / (h.scrollHeight - h.clientHeight) * 100;
scrollbar.style.width = pct + "%";
topbar.classList.toggle("scrolled", h.scrollTop > 30);
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---------- modal + form ---------- */
var modal = document.getElementById("enquireModal");
var modalCard = document.getElementById("modalCard");
var form = document.getElementById("enquireForm");
var pkgSelect = document.getElementById("fPkg");
var dateInput = document.getElementById("fDate");
var availLine = document.getElementById("availLine");
var lastFocus = null;
// min date = today
var today = new Date().toISOString().split("T")[0];
dateInput.min = today;
function openModal(preselect) {
lastFocus = document.activeElement;
if (preselect && pkgSelect.querySelector('option[value="' + preselect + '"]')) {
pkgSelect.value = preselect;
}
modal.hidden = false;
document.body.style.overflow = "hidden";
setTimeout(function () { document.getElementById("fName").focus(); }, 40);
document.addEventListener("keydown", onKeydown);
}
function closeModal() {
modal.hidden = true;
document.body.style.overflow = "";
document.removeEventListener("keydown", onKeydown);
if (lastFocus) lastFocus.focus();
}
function onKeydown(e) {
if (e.key === "Escape") { closeModal(); return; }
if (e.key === "Tab") {
var focusables = modalCard.querySelectorAll('button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
focusables = Array.prototype.filter.call(focusables, function (el) { return !el.disabled && el.offsetParent !== null; });
if (!focusables.length) return;
var first = focusables[0], last = focusables[focusables.length - 1];
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
}
}
Array.prototype.forEach.call(document.querySelectorAll("[data-open-enquire]"), function (b) {
b.addEventListener("click", function () { openModal(); });
});
Array.prototype.forEach.call(document.querySelectorAll("[data-close-enquire]"), function (b) {
b.addEventListener("click", closeModal);
});
/* ---- mock availability check ---- */
var checkTimer;
dateInput.addEventListener("change", function () {
if (!dateInput.value) { availLine.hidden = true; return; }
availLine.hidden = false;
availLine.className = "avail check";
availLine.textContent = "Checking availability…";
clearTimeout(checkTimer);
checkTimer = setTimeout(function () {
var d = new Date(dateInput.value);
// deterministic mock: some Saturdays already booked
var busy = (d.getDate() % 7 === 3) || (d.getDay() === 0);
if (busy) {
availLine.className = "avail busy";
availLine.textContent = "That date is on our waitlist — send an enquiry and we'll suggest nearby options.";
} else {
availLine.className = "avail ok";
availLine.textContent = "Good news — " + d.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric" }) + " looks open.";
}
}, 850);
});
/* ---- validation ---- */
function setError(name, msg) {
var field = form.querySelector('[name="' + name + '"]').closest(".field");
var err = field.querySelector(".err");
if (msg) { field.classList.add("invalid"); if (err) err.textContent = msg; }
else { field.classList.remove("invalid"); }
}
["name", "date", "email"].forEach(function (n) {
form.querySelector('[name="' + n + '"]').addEventListener("input", function () { setError(n, ""); });
});
form.addEventListener("submit", function (e) {
e.preventDefault();
var ok = true;
var name = form.name.value.trim();
var date = form.date.value;
var email = form.email.value.trim();
if (name.length < 2) { setError("name", "Please tell us your name."); ok = false; }
if (!date) { setError("date", "Pick your wedding date."); ok = false; }
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { setError("email", "Enter a valid email address."); ok = false; }
if (!ok) { toast("Please fix the highlighted fields."); return; }
var btn = document.getElementById("submitBtn");
btn.disabled = true;
btn.textContent = "Sending…";
setTimeout(function () {
btn.disabled = false;
btn.textContent = "Send enquiry";
closeModal();
form.reset();
availLine.hidden = true;
toast("Enquiry sent — we'll reply to " + email + " within 24h.");
}, 1100);
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Aurelia Films — Wedding Cinema</title>
<meta name="description" content="Cinematic wedding films for couples who want their day remembered like a movie." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&family=Playfair+Display:ital,wght@0,500;0,600;1,500&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="scrollbar" id="scrollbar" aria-hidden="true"></div>
<header class="topbar" id="topbar">
<a class="brand" href="#top" aria-label="Aurelia Films home">
<span class="brand-mark" aria-hidden="true">◐</span>
<span class="brand-name">Aurelia<em>Films</em></span>
</a>
<nav class="nav" aria-label="Primary">
<a href="#films">Films</a>
<a href="#packages">Packages</a>
<a href="#love">Couples</a>
</nav>
<button class="btn btn-ghost nav-cta" data-open-enquire>Enquire</button>
</header>
<main id="top">
<!-- HERO -->
<section class="hero" id="hero" aria-label="Featured wedding film">
<div class="bar bar-top" aria-hidden="true"></div>
<div class="hero-media">
<div class="grain" aria-hidden="true"></div>
<div class="hero-inner">
<p class="eyebrow"><span class="rec" aria-hidden="true"></span> Now booking · Spring & Summer 2027</p>
<h1 class="hero-title">Your day, remembered<br /><span class="serif">like a film.</span></h1>
<p class="hero-sub">Aurelia Films is a two-person crew shooting intimate, unhurried wedding cinema — honest moments, warm light, no stiff posing.</p>
<div class="hero-actions">
<button class="btn btn-primary" id="playReel" aria-pressed="false">
<span class="play-ico" aria-hidden="true"></span>
<span class="play-label">Watch the reel</span>
</button>
<button class="btn btn-ghost" data-open-enquire>Check our date</button>
</div>
<dl class="hero-stats">
<div><dt>Weddings filmed</dt><dd>240+</dd></div>
<div><dt>Avg. rating</dt><dd>4.9<span>/5</span></dd></div>
<div><dt>Delivery</dt><dd>6 wks</dd></div>
</dl>
</div>
<div class="hero-tc" aria-hidden="true"><span id="reelTc">00:00:00:00</span> · REEL_2027.mov</div>
</div>
<div class="bar bar-bottom" aria-hidden="true"></div>
</section>
<!-- FILMS -->
<section class="section" id="films" aria-labelledby="films-h">
<div class="section-head">
<div>
<p class="kicker">Selected work</p>
<h2 id="films-h" class="section-title">Sample films</h2>
</div>
<div class="filters" role="group" aria-label="Filter films by style">
<button class="chip is-active" data-filter="all" aria-pressed="true">All</button>
<button class="chip" data-filter="cinematic" aria-pressed="false">Cinematic</button>
<button class="chip" data-filter="documentary" aria-pressed="false">Documentary</button>
<button class="chip" data-filter="elopement" aria-pressed="false">Elopement</button>
</div>
</div>
<div class="film-grid" id="filmGrid" aria-live="polite"><!-- films injected by JS --></div>
</section>
<!-- PACKAGES -->
<section class="section packages" id="packages" aria-labelledby="pkg-h">
<div class="section-head">
<div>
<p class="kicker">Investment</p>
<h2 id="pkg-h" class="section-title">Wedding packages</h2>
</div>
<div class="toggle" role="group" aria-label="Payment plan">
<button class="toggle-opt is-active" data-plan="once" aria-pressed="true">Pay once</button>
<button class="toggle-opt" data-plan="installments" aria-pressed="false">12 months <span class="save">−0% APR</span></button>
</div>
</div>
<div class="pkg-grid" id="pkgGrid"><!-- packages injected by JS --></div>
<p class="pkg-note">All packages include a pre-wedding call, licensed music, colour grading, and a private online gallery. Travel within 60 mi is included.</p>
</section>
<!-- TESTIMONIALS -->
<section class="section love" id="love" aria-labelledby="love-h">
<div class="section-head center">
<p class="kicker">Kind words</p>
<h2 id="love-h" class="section-title">Couples on Aurelia</h2>
</div>
<div class="carousel" id="carousel">
<button class="car-nav prev" id="carPrev" aria-label="Previous testimonial">‹</button>
<div class="car-viewport">
<div class="car-track" id="carTrack"><!-- quotes injected by JS --></div>
</div>
<button class="car-nav next" id="carNext" aria-label="Next testimonial">›</button>
</div>
<div class="dots" id="carDots" role="tablist" aria-label="Choose testimonial"></div>
</section>
<!-- CTA -->
<section class="cta" aria-labelledby="cta-h">
<div class="bar bar-top thin" aria-hidden="true"></div>
<div class="cta-inner">
<h2 id="cta-h" class="cta-title">Let's talk about <span class="serif">your</span> day.</h2>
<p class="cta-sub">Tell us the date and the vibe. We'll reply within 24 hours with real availability.</p>
<button class="btn btn-primary btn-lg" data-open-enquire>Enquire now</button>
</div>
<div class="bar bar-bottom thin" aria-hidden="true"></div>
</section>
</main>
<footer class="footer">
<span class="brand-name">Aurelia<em>Films</em></span>
<span class="foot-meta">Wedding cinema · Based in Asheville, travels worldwide</span>
<span class="foot-tc">© 2027 · SC:AURELIA//WED</span>
</footer>
<!-- ENQUIRE MODAL -->
<div class="modal" id="enquireModal" hidden>
<div class="modal-scrim" data-close-enquire></div>
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="modal-h" id="modalCard">
<button class="modal-x" data-close-enquire aria-label="Close enquiry form">✕</button>
<p class="kicker">Enquiry</p>
<h3 id="modal-h" class="modal-title">Check your date</h3>
<form id="enquireForm" novalidate>
<div class="field">
<label for="fName">Couple / your name</label>
<input id="fName" name="name" type="text" autocomplete="name" placeholder="Mara & Jules" required />
<span class="err" data-err="name"></span>
</div>
<div class="field-row">
<div class="field">
<label for="fDate">Wedding date</label>
<input id="fDate" name="date" type="date" required />
<span class="err" data-err="date"></span>
</div>
<div class="field">
<label for="fEmail">Email</label>
<input id="fEmail" name="email" type="email" autocomplete="email" placeholder="[email protected]" required />
<span class="err" data-err="email"></span>
</div>
</div>
<div class="field">
<label for="fPkg">Package of interest</label>
<select id="fPkg" name="package">
<option value="Highlight">Highlight — $2,400</option>
<option value="Signature" selected>Signature — $3,900</option>
<option value="Heirloom">Heirloom — $6,200</option>
<option value="Not sure yet">Not sure yet</option>
</select>
</div>
<div class="field">
<label for="fMsg">Tell us about your day <span class="opt">(optional)</span></label>
<textarea id="fMsg" name="message" rows="3" placeholder="Venue, guest count, the mood you're after…"></textarea>
</div>
<div class="avail" id="availLine" hidden></div>
<button class="btn btn-primary btn-block" type="submit" id="submitBtn">Send enquiry</button>
</form>
</div>
</div>
<div class="toast-wrap" id="toastWrap" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>A film-forward landing page for a wedding videographer, built on a cinematic black canvas with letterbox bars, warm amber accents, and JetBrains Mono timecodes. The hero frames a featured reel behind a play control and a soft grain overlay, while a sticky top bar tracks scroll progress with a thin amber line. Below, a filterable gallery of sample films lets visitors switch between styles — Documentary, Cinematic, Elopement — with cards that reveal runtime, location, and a hover-scrub timecode.
The pricing section presents three tiered packages (Highlight, Signature, Heirloom) with a toggle that flips prices between a one-off fee and a twelve-month installment, recalculating figures live. A testimonial carousel rotates real-feeling couple quotes with star ratings, auto-advancing but pausing on hover and steppable by keyboard. The primary enquire CTA opens a focus-trapped modal with a date picker that runs a mock availability check, inline validation, and a toast helper confirming the enquiry.
Every control is keyboard-usable with visible focus rings, the layout collapses gracefully to a single column under 520px, and all interactions are handled in dependency-free vanilla JavaScript.