Video — Landing Commercial
A cinematic dark landing page for Vantablack Films, a fictional commercial and brand-film studio, built on a near-black canvas with amber accents, JetBrains Mono timecodes, and letterbox bars framing the hero. Sections cover a client logo wall, a filterable case-reel grid with hover-play overlays, a count-up ROI and engagement band, a three-step process, a rotating testimonial, and a quote-request form. Vanilla JS drives the reel lightbox, category filters, count-up stats, and inline form validation.
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.1);
--line-2: rgba(255, 255, 255, 0.18);
--white: #fff;
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
--mono: "JetBrains Mono", ui-monospace, monospace;
--sans: "Inter", system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--sans);
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
h1, h2, h3 { margin: 0; line-height: 1.08; letter-spacing: -0.02em; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
.wrap { width: min(1140px, 92vw); margin: 0 auto; }
.tc {
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.08em;
color: var(--amber);
}
/* Focus */
:focus-visible {
outline: 2px solid var(--amber);
outline-offset: 3px;
border-radius: 3px;
}
/* ---------- Nav ---------- */
.nav {
position: sticky;
top: 0;
z-index: 50;
background: rgba(10, 10, 11, 0);
border-bottom: 1px solid transparent;
transition: background 0.3s ease, border-color 0.3s ease, backdrop-filter 0.3s ease;
}
.nav.is-stuck {
background: rgba(10, 10, 11, 0.82);
backdrop-filter: blur(12px);
border-bottom-color: var(--line);
}
.nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 68px;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-mark {
width: 30px; height: 30px;
border-radius: 7px;
background: var(--surface-2);
border: 1px solid var(--line-2);
display: grid; place-items: center;
}
.rec {
width: 10px; height: 10px;
border-radius: 50%;
background: var(--red);
box-shadow: 0 0 0 3px rgba(255, 77, 77, 0.18);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.35; }
}
.brand-name {
font-weight: 800;
font-size: 15px;
letter-spacing: 0.14em;
display: flex;
flex-direction: column;
line-height: 1;
}
.brand-sub {
font-family: var(--mono);
font-size: 9px;
letter-spacing: 0.42em;
color: var(--muted);
margin-top: 2px;
}
.nav-links { display: flex; align-items: center; gap: 26px; }
.nav-links a {
font-size: 14px;
font-weight: 500;
color: var(--muted);
transition: color 0.2s ease;
}
.nav-links a:hover { color: var(--ink); }
.nav-cta {
padding: 9px 16px;
border-radius: var(--r-sm);
background: var(--amber);
color: #14100a !important;
font-weight: 700 !important;
}
.nav-cta:hover { background: var(--amber-d); }
.hamburger {
display: none;
flex-direction: column;
gap: 5px;
background: none;
border: 0;
cursor: pointer;
padding: 8px;
}
.hamburger span {
width: 22px; height: 2px;
background: var(--ink);
border-radius: 2px;
transition: transform 0.28s ease, opacity 0.2s ease;
}
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
/* ---------- Buttons ---------- */
.btn {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 13px 22px;
border-radius: var(--r-sm);
font-weight: 700;
font-size: 15px;
border: 1px solid transparent;
cursor: pointer;
transition: transform 0.15s ease, background 0.2s ease, border-color 0.2s ease;
}
.btn:active { transform: translateY(1px) scale(0.99); }
.btn-primary {
background: var(--amber);
color: #14100a;
}
.btn-primary:hover { background: var(--amber-d); box-shadow: 0 8px 30px rgba(255, 176, 32, 0.22); }
.btn-ghost {
background: transparent;
color: var(--ink);
border-color: var(--line-2);
}
.btn-ghost:hover { border-color: var(--amber); color: var(--amber); }
.btn-block { width: 100%; justify-content: center; margin-top: 6px; }
.play {
width: 0; height: 0;
border-style: solid;
border-width: 6px 0 6px 10px;
border-color: transparent transparent transparent currentColor;
display: inline-block;
}
.play-lg { border-width: 12px 0 12px 20px; }
/* ---------- Letterbox bars ---------- */
.bar {
position: absolute;
left: 0; right: 0;
height: clamp(28px, 6vw, 60px);
background: #000;
z-index: 2;
pointer-events: none;
}
.bar-top { top: 0; }
.bar-bottom { bottom: 0; }
/* ---------- Hero ---------- */
.hero {
position: relative;
min-height: min(78vh, 720px);
display: flex;
align-items: center;
overflow: hidden;
border-bottom: 1px solid var(--line);
}
.hero-bg {
position: absolute;
inset: 0;
background:
radial-gradient(120% 90% at 80% 15%, rgba(255, 176, 32, 0.16), transparent 55%),
radial-gradient(90% 80% at 12% 85%, rgba(255, 77, 77, 0.12), transparent 55%),
linear-gradient(180deg, #101014, #0a0a0b 70%);
}
.hero-bg::after {
content: "";
position: absolute;
inset: 0;
background-image: linear-gradient(var(--line) 1px, transparent 1px),
linear-gradient(90deg, var(--line) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(70% 70% at 50% 40%, #000, transparent 80%);
opacity: 0.5;
}
.hero-inner {
position: relative;
z-index: 3;
padding: clamp(70px, 12vw, 120px) 0;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 9px;
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--amber);
padding: 7px 14px;
border: 1px solid var(--line-2);
border-radius: 999px;
background: rgba(255, 176, 32, 0.06);
}
.eyebrow .dot {
width: 7px; height: 7px;
border-radius: 50%;
background: var(--red);
animation: pulse 2s ease-in-out infinite;
}
.hero-title {
font-size: clamp(40px, 7.5vw, 82px);
font-weight: 800;
margin: 22px 0 18px;
max-width: 14ch;
}
.hero-title em {
font-style: normal;
background: linear-gradient(100deg, var(--amber), var(--red));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.hero-lede {
font-size: clamp(16px, 2vw, 19px);
color: var(--muted);
max-width: 52ch;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 14px;
margin: 30px 0 26px;
}
.hero-meta {
display: flex;
align-items: center;
gap: 16px;
font-size: 13px;
color: var(--muted);
}
.hero-meta .sep { width: 1px; height: 16px; background: var(--line-2); }
/* ---------- Client marquee ---------- */
.clients {
border-bottom: 1px solid var(--line);
background: var(--surface);
padding: 22px 0;
overflow: hidden;
-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee-track {
display: flex;
gap: 56px;
width: max-content;
animation: scroll 34s linear infinite;
}
.marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-track span {
font-weight: 700;
font-size: 18px;
letter-spacing: 0.06em;
color: var(--muted);
white-space: nowrap;
transition: color 0.2s ease;
}
.marquee-track span:hover { color: var(--ink); }
@keyframes scroll {
to { transform: translateX(-50%); }
}
/* ---------- Sections ---------- */
.section { padding: clamp(64px, 9vw, 104px) 0; }
.section-head { max-width: 620px; margin-bottom: 40px; }
.kicker {
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--amber);
margin-bottom: 12px;
}
.section-head h2 { font-size: clamp(28px, 4.4vw, 44px); font-weight: 800; }
.section-lede { margin-top: 14px; color: var(--muted); font-size: 17px; }
/* ---------- Filters ---------- */
.filters { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 28px; }
.chip {
padding: 9px 18px;
border-radius: 999px;
border: 1px solid var(--line-2);
background: transparent;
color: var(--muted);
font-family: var(--sans);
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.chip:hover { color: var(--ink); border-color: var(--amber); }
.chip.is-active {
background: var(--amber);
color: #14100a;
border-color: var(--amber);
}
/* ---------- Reel grid ---------- */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
}
.card {
position: relative;
border-radius: var(--r-lg);
overflow: hidden;
border: 1px solid var(--line);
background: var(--surface);
cursor: pointer;
aspect-ratio: 16 / 10;
transition: transform 0.25s ease, border-color 0.25s ease;
animation: cardIn 0.4s ease both;
}
@keyframes cardIn {
from { opacity: 0; transform: translateY(14px); }
}
.card:hover { transform: translateY(-4px); border-color: var(--line-2); }
.card-poster {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
transition: transform 0.5s ease;
}
.card:hover .card-poster { transform: scale(1.06); }
.card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(10, 10, 11, 0.05) 30%, rgba(10, 10, 11, 0.9));
}
.card-overlay {
position: absolute;
inset: 0;
z-index: 2;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 16px;
}
.card-badge {
position: absolute;
top: 14px; left: 14px;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 5px 10px;
border-radius: 999px;
background: rgba(0, 0, 0, 0.55);
border: 1px solid var(--line-2);
color: var(--amber);
}
.card-play {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%) scale(0.8);
width: 54px; height: 54px;
border-radius: 50%;
background: rgba(255, 176, 32, 0.92);
color: #14100a;
display: grid;
place-items: center;
opacity: 0;
transition: opacity 0.25s ease, transform 0.25s ease;
}
.card:hover .card-play { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.card-title { font-size: 17px; font-weight: 700; }
.card-sub {
display: flex;
align-items: center;
gap: 8px;
margin-top: 4px;
font-size: 13px;
color: var(--muted);
}
.card-runtime { font-family: var(--mono); font-size: 11px; color: var(--amber); }
.card.is-hidden { display: none; }
/* ---------- Outcomes ---------- */
.outcomes { background: var(--surface); border-block: 1px solid var(--line); }
.stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 18px;
}
.stat {
padding: 30px 24px;
border: 1px solid var(--line);
border-radius: var(--r-lg);
background: var(--surface-2);
}
.stat-num {
display: block;
font-size: clamp(32px, 4.4vw, 48px);
font-weight: 800;
font-family: var(--mono);
color: var(--amber);
letter-spacing: -0.02em;
}
.stat-label { color: var(--muted); font-size: 14px; margin-top: 6px; display: block; }
/* ---------- Process ---------- */
.steps {
list-style: none;
padding: 0; margin: 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
counter-reset: step;
}
.step {
padding: 28px 24px;
border: 1px solid var(--line);
border-radius: var(--r-lg);
background: var(--surface);
transition: border-color 0.25s ease, transform 0.25s ease;
}
.step:hover { border-color: var(--amber); transform: translateY(-3px); }
.step-tc { display: inline-block; margin-bottom: 14px; }
.step h3 { font-size: 20px; font-weight: 700; margin-bottom: 8px; }
.step p { color: var(--muted); font-size: 15px; }
/* ---------- Voices ---------- */
.voices { background: var(--surface); border-block: 1px solid var(--line); }
.quote-box {
max-width: 780px;
padding: 44px;
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
background: var(--surface-2);
position: relative;
}
.quote-box::before {
content: "“";
position: absolute;
top: 6px; left: 24px;
font-size: 90px;
color: rgba(255, 176, 32, 0.22);
font-family: Georgia, serif;
line-height: 1;
}
.quote-box blockquote {
margin: 10px 0 22px;
font-size: clamp(19px, 2.6vw, 26px);
font-weight: 500;
line-height: 1.35;
transition: opacity 0.3s ease;
}
.quote-by { display: flex; flex-direction: column; gap: 2px; }
.quote-by strong { font-weight: 700; }
.quote-by span { color: var(--muted); font-size: 14px; }
.dots { display: flex; gap: 9px; margin-top: 22px; }
.dots button {
width: 9px; height: 9px;
border-radius: 50%;
border: 0;
background: var(--line-2);
cursor: pointer;
padding: 0;
transition: background 0.2s ease, transform 0.2s ease;
}
.dots button.is-active { background: var(--amber); transform: scale(1.25); }
/* ---------- Quote CTA ---------- */
.quote-inner {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 48px;
align-items: start;
}
.quote-copy h2 { font-size: clamp(28px, 4vw, 42px); font-weight: 800; margin-top: 12px; }
.assurances {
list-style: none;
padding: 0;
margin: 26px 0 0;
display: grid;
gap: 12px;
}
.assurances li {
position: relative;
padding-left: 26px;
color: var(--muted);
}
.assurances li::before {
content: "";
position: absolute;
left: 0; top: 7px;
width: 8px; height: 8px;
border-radius: 50%;
background: var(--amber);
box-shadow: 0 0 0 4px rgba(255, 176, 32, 0.16);
}
.quote-form {
padding: 30px;
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
background: var(--surface);
display: grid;
gap: 16px;
}
.field { display: grid; gap: 6px; }
.field label { font-size: 13px; font-weight: 600; letter-spacing: 0.02em; }
.field .opt { color: var(--muted); font-weight: 400; }
.field input,
.field select,
.field textarea {
width: 100%;
padding: 12px 14px;
border-radius: var(--r-sm);
border: 1px solid var(--line-2);
background: var(--surface-2);
color: var(--ink);
font-family: var(--sans);
font-size: 15px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.field textarea { resize: vertical; }
.field input::placeholder,
.field textarea::placeholder { color: #56565e; }
.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.14);
}
.field.is-invalid input,
.field.is-invalid select {
border-color: var(--red);
box-shadow: 0 0 0 3px rgba(255, 77, 77, 0.14);
}
.err { color: var(--red); font-size: 12.5px; min-height: 0; }
/* ---------- Footer ---------- */
.footer { border-top: 1px solid var(--line); padding: 40px 0; background: var(--surface); }
.footer-inner {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 16px;
}
.foot-note { color: var(--muted); font-size: 13px; }
/* ---------- Lightbox ---------- */
.lightbox {
position: fixed;
inset: 0;
z-index: 100;
display: grid;
place-items: center;
padding: 20px;
}
.lightbox[hidden] { display: none; }
.lightbox-backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.78);
backdrop-filter: blur(4px);
animation: fade 0.25s ease;
}
@keyframes fade { from { opacity: 0; } }
.lightbox-panel {
position: relative;
width: min(760px, 100%);
background: var(--surface);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
overflow: hidden;
z-index: 2;
animation: pop 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
@keyframes pop { from { opacity: 0; transform: scale(0.94) translateY(12px); } }
.lightbox-close {
position: absolute;
top: 12px; right: 14px;
z-index: 5;
width: 36px; height: 36px;
border-radius: 50%;
border: 1px solid var(--line-2);
background: rgba(0, 0, 0, 0.6);
color: var(--ink);
font-size: 22px;
line-height: 1;
cursor: pointer;
transition: background 0.2s ease;
}
.lightbox-close:hover { background: var(--red); }
.lightbox-frame { position: relative; }
.lightbox-poster {
aspect-ratio: 16 / 9;
background-size: cover;
background-position: center;
display: grid;
place-items: center;
}
.lightbox-poster .play-lg { color: rgba(255, 255, 255, 0.92); filter: drop-shadow(0 4px 16px rgba(0,0,0,0.5)); }
.lightbox-meta { padding: 22px 26px 26px; }
.lightbox-meta h3 { font-size: 24px; font-weight: 800; margin: 8px 0; }
.lightbox-meta p { color: var(--muted); }
.lightbox-result {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--line);
display: flex;
align-items: baseline;
gap: 12px;
}
.lightbox-result span {
font-family: var(--mono);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
}
.lightbox-result strong { color: var(--amber); font-size: 16px; }
/* ---------- Toast ---------- */
.toast-wrap {
position: fixed;
bottom: 24px; left: 50%;
transform: translateX(-50%);
z-index: 200;
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
}
.toast {
padding: 12px 20px;
border-radius: var(--r-md);
background: var(--surface-2);
border: 1px solid var(--line-2);
color: var(--ink);
font-size: 14px;
font-weight: 500;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
gap: 10px;
animation: toastIn 0.3s ease;
}
.toast::before {
content: "";
width: 8px; height: 8px;
border-radius: 50%;
background: var(--amber);
}
.toast.is-out { animation: toastOut 0.3s ease forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateY(16px); } }
@keyframes toastOut { to { opacity: 0; transform: translateY(16px); } }
/* ---------- Reveal ---------- */
.reveal {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.reveal.is-in { opacity: 1; transform: none; }
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.grid { grid-template-columns: repeat(2, 1fr); }
.stats { grid-template-columns: repeat(2, 1fr); }
.steps { grid-template-columns: 1fr; }
.quote-inner { grid-template-columns: 1fr; gap: 32px; }
}
@media (max-width: 520px) {
.nav-links {
position: fixed;
top: 68px; left: 0; right: 0;
flex-direction: column;
align-items: stretch;
gap: 4px;
padding: 14px 6vw 22px;
background: rgba(10, 10, 11, 0.97);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--line);
transform: translateY(-12px);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease;
}
.nav-links.is-open { opacity: 1; transform: none; pointer-events: auto; }
.nav-links a { padding: 12px 4px; font-size: 16px; }
.nav-cta { text-align: center; margin-top: 6px; }
.hamburger { display: flex; }
.grid { grid-template-columns: 1fr; }
.stats { grid-template-columns: 1fr; }
.hero { min-height: 0; }
.hero-inner { padding: 80px 0 64px; }
.hero-actions { flex-direction: column; align-items: stretch; }
.btn { justify-content: center; }
.quote-box { padding: 30px 22px; }
.quote-form { padding: 22px; }
.footer-inner { flex-direction: column; align-items: flex-start; }
}
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; }
.marquee-track { animation: none; }
.reveal { opacity: 1; transform: none; transition: none; }
html { scroll-behavior: auto; }
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastWrap = document.getElementById("toastWrap");
function toast(msg) {
if (!toastWrap) return;
var el = document.createElement("div");
el.className = "toast";
el.textContent = msg;
toastWrap.appendChild(el);
setTimeout(function () {
el.classList.add("is-out");
setTimeout(function () { el.remove(); }, 320);
}, 2600);
}
/* ---------- Reel data ---------- */
var reels = [
{
id: "hero",
title: "Northwind — Winter Launch",
cat: "brand",
catLabel: "Brand film",
runtime: "01:48:00",
poster: "linear-gradient(135deg,#2a1a10,#0a0a0b)",
desc: "A 90-second brand film introducing Northwind's cold-weather line, cut for cinema and a 6s bumper.",
result: "+41% branded search in 30 days"
},
{
id: "pulse",
title: "Pulse Athletic — Sprint",
cat: "tvc",
catLabel: "TVC",
runtime: "00:30:00",
poster: "linear-gradient(135deg,#3a1414,#0a0a0b)",
desc: "National 30-second TV commercial built around a single unbroken sprint and a one-line payoff.",
result: "2.4x ROAS across broadcast"
},
{
id: "halcyon",
title: "Halcyon — Product Reveal",
cat: "product",
catLabel: "Product",
runtime: "00:52:00",
poster: "linear-gradient(135deg,#12242a,#0a0a0b)",
desc: "Macro-lens product film for Halcyon's flagship device, engineered for silent-autoplay feeds.",
result: "3.1x add-to-cart vs. control"
},
{
id: "oktane",
title: "Oktane — 6s Bumpers",
cat: "social",
catLabel: "Social",
runtime: "00:06:00",
poster: "linear-gradient(135deg,#2a2410,#0a0a0b)",
desc: "A set of nine six-second vertical bumpers, each testing a different hook against watch-through.",
result: "88% avg. completion rate"
},
{
id: "meridian",
title: "Meridian — Founders",
cat: "brand",
catLabel: "Brand film",
runtime: "02:10:00",
poster: "linear-gradient(135deg,#1c1030,#0a0a0b)",
desc: "A documentary-style founder film for Meridian's Series B, used across web, sales, and hiring.",
result: "+19% qualified pipeline"
},
{
id: "foxglove",
title: "Foxglove — Unboxing",
cat: "product",
catLabel: "Product",
runtime: "00:44:00",
poster: "linear-gradient(135deg,#102a1c,#0a0a0b)",
desc: "Tactile unboxing film for Foxglove skincare, colour-graded for warmth and premium feel.",
result: "+27% repeat purchase"
},
{
id: "vesper",
title: "Vesper — City Nights",
cat: "social",
catLabel: "Social",
runtime: "00:15:00",
poster: "linear-gradient(135deg,#241030,#0a0a0b)",
desc: "Vertical 15-second nightlife spots for Vesper, shot handheld for authenticity in-feed.",
result: "5.2M organic views"
},
{
id: "bright",
title: "Bright & Co — Anthem",
cat: "tvc",
catLabel: "TVC",
runtime: "01:00:00",
poster: "linear-gradient(135deg,#2a1a2a,#0a0a0b)",
desc: "A 60-second brand anthem for Bright & Co, delivered in broadcast, OLV, and cutdown specs.",
result: "+33% ad-recall lift"
},
{
id: "okt2",
title: "Oktane — Launch Teaser",
cat: "brand",
catLabel: "Brand film",
runtime: "00:24:00",
poster: "linear-gradient(135deg,#102030,#0a0a0b)",
desc: "A teaser cut preceding Oktane's product film, seeded two weeks ahead of the reveal.",
result: "62k waitlist signups"
}
];
var reelMap = {};
reels.forEach(function (r) { reelMap[r.id] = r; });
/* ---------- Render grid ---------- */
var grid = document.getElementById("reelGrid");
function render(list) {
grid.innerHTML = "";
list.forEach(function (r, i) {
var card = document.createElement("article");
card.className = "card";
card.dataset.cat = r.cat;
card.dataset.reel = r.id;
card.style.animationDelay = (i * 0.04) + "s";
card.setAttribute("tabindex", "0");
card.setAttribute("role", "button");
card.setAttribute("aria-label", "Play reel: " + r.title);
card.innerHTML =
'<div class="card-poster" style="background-image:' + r.poster + '"></div>' +
'<span class="card-badge">' + r.catLabel + '</span>' +
'<span class="card-play"><span class="play" aria-hidden="true"></span></span>' +
'<div class="card-overlay">' +
'<span class="card-title">' + r.title + '</span>' +
'<span class="card-sub"><span class="card-runtime">' + r.runtime + '</span> · brand campaign</span>' +
'</div>';
grid.appendChild(card);
});
}
render(reels);
/* ---------- Filters ---------- */
var chips = document.querySelectorAll(".chip");
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) {
c.classList.remove("is-active");
c.setAttribute("aria-selected", "false");
});
chip.classList.add("is-active");
chip.setAttribute("aria-selected", "true");
var f = chip.dataset.filter;
var list = f === "all" ? reels : reels.filter(function (r) { return r.cat === f; });
render(list);
});
});
/* ---------- Lightbox ---------- */
var lightbox = document.getElementById("lightbox");
var lbPoster = document.getElementById("lbPoster");
var lbTitle = document.getElementById("lbTitle");
var lbDesc = document.getElementById("lbDesc");
var lbResult = document.getElementById("lbResult");
var lbRuntime = document.getElementById("lbRuntime");
var lastFocus = null;
function openReel(id) {
var r = reelMap[id];
if (!r) return;
lastFocus = document.activeElement;
lbPoster.style.backgroundImage = r.poster;
lbTitle.textContent = r.title;
lbDesc.textContent = r.desc;
lbResult.textContent = r.result;
lbRuntime.textContent = r.runtime;
lightbox.hidden = false;
document.body.style.overflow = "hidden";
var closeBtn = lightbox.querySelector(".lightbox-close");
if (closeBtn) closeBtn.focus();
}
function closeReel() {
lightbox.hidden = true;
document.body.style.overflow = "";
if (lastFocus && lastFocus.focus) lastFocus.focus();
}
// Grid card clicks (delegated) + keyboard
grid.addEventListener("click", function (e) {
var card = e.target.closest(".card");
if (card) openReel(card.dataset.reel);
});
grid.addEventListener("keydown", function (e) {
var card = e.target.closest(".card");
if (card && (e.key === "Enter" || e.key === " ")) {
e.preventDefault();
openReel(card.dataset.reel);
}
});
// Hero "watch the reel"
document.querySelectorAll("[data-reel]").forEach(function (btn) {
if (btn.closest("#reelGrid")) return;
btn.addEventListener("click", function () { openReel(btn.dataset.reel); });
});
lightbox.addEventListener("click", function (e) {
if (e.target.hasAttribute("data-close")) closeReel();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && !lightbox.hidden) closeReel();
});
/* ---------- Sticky nav shadow ---------- */
var nav = document.getElementById("nav");
function onScroll() {
if (window.scrollY > 12) nav.classList.add("is-stuck");
else nav.classList.remove("is-stuck");
}
onScroll();
window.addEventListener("scroll", onScroll, { passive: true });
/* ---------- Mobile menu ---------- */
var hamburger = document.getElementById("hamburger");
var navLinks = document.getElementById("navLinks");
hamburger.addEventListener("click", function () {
var open = navLinks.classList.toggle("is-open");
hamburger.setAttribute("aria-expanded", open ? "true" : "false");
});
navLinks.addEventListener("click", function (e) {
if (e.target.tagName === "A") {
navLinks.classList.remove("is-open");
hamburger.setAttribute("aria-expanded", "false");
}
});
/* ---------- Count-up stats ---------- */
var counted = false;
function runCounts() {
if (counted) return;
counted = true;
document.querySelectorAll(".stat-num").forEach(function (el) {
var target = parseFloat(el.dataset.count);
var suffix = el.dataset.suffix || "";
var decimals = parseInt(el.dataset.decimals || "0", 10);
var start = null;
var dur = 1500;
function step(ts) {
if (!start) start = ts;
var p = Math.min((ts - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
var val = target * eased;
el.textContent = (decimals ? val.toFixed(decimals) : Math.round(val)) + suffix;
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
});
}
/* ---------- Reveal + stats via IntersectionObserver ---------- */
var statsSection = document.getElementById("stats");
if ("IntersectionObserver" in window) {
var revObs = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) {
en.target.classList.add("is-in");
revObs.unobserve(en.target);
}
});
}, { threshold: 0.12 });
document.querySelectorAll(".reveal").forEach(function (el) { revObs.observe(el); });
var statObs = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { runCounts(); statObs.disconnect(); }
});
}, { threshold: 0.4 });
if (statsSection) statObs.observe(statsSection);
} else {
document.querySelectorAll(".reveal").forEach(function (el) { el.classList.add("is-in"); });
runCounts();
}
/* ---------- Testimonials ---------- */
var quotes = [
{
text: "The launch film paid for itself in eleven days. Vantablack treated our conversion rate like the star of the shoot — and it showed.",
name: "Dana Reyes",
role: "VP Growth, Pulse Athletic"
},
{
text: "We briefed a number, not a look. They came back with a film that hit both — and a performance readout that made the next round easy to sign off.",
name: "Marcus Feld",
role: "CMO, Halcyon"
},
{
text: "Fastest brief-to-broadcast we've ever run. Eighteen days from kickoff to a national spot that beat our recall benchmark by a third.",
name: "Priya Anand",
role: "Brand Director, Bright & Co"
}
];
var qText = document.getElementById("quoteText");
var qName = document.getElementById("quoteName");
var qRole = document.getElementById("quoteRole");
var dotsWrap = document.getElementById("dots");
var qIndex = 0;
var qTimer;
quotes.forEach(function (_, i) {
var d = document.createElement("button");
d.setAttribute("role", "tab");
d.setAttribute("aria-label", "Testimonial " + (i + 1));
if (i === 0) d.classList.add("is-active");
d.addEventListener("click", function () { showQuote(i, true); });
dotsWrap.appendChild(d);
});
function showQuote(i, manual) {
qIndex = i;
var q = quotes[i];
qText.style.opacity = "0";
setTimeout(function () {
qText.textContent = q.text;
qName.textContent = q.name;
qRole.textContent = q.role;
qText.style.opacity = "1";
}, 200);
Array.prototype.forEach.call(dotsWrap.children, function (d, di) {
d.classList.toggle("is-active", di === i);
});
if (manual) restartQuoteTimer();
}
function nextQuote() { showQuote((qIndex + 1) % quotes.length, false); }
function restartQuoteTimer() {
clearInterval(qTimer);
qTimer = setInterval(nextQuote, 5500);
}
restartQuoteTimer();
/* ---------- Quote form ---------- */
var form = document.getElementById("quoteForm");
function setErr(id, msg) {
var field = document.getElementById(id).closest(".field");
var err = document.querySelector('[data-err="' + id + '"]');
if (msg) {
field.classList.add("is-invalid");
err.textContent = msg;
} else {
field.classList.remove("is-invalid");
err.textContent = "";
}
}
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = document.getElementById("qName");
var email = document.getElementById("qEmail");
var budget = document.getElementById("qBudget");
var ok = true;
if (!name.value.trim()) { setErr("qName", "Please add your name."); ok = false; }
else setErr("qName", "");
var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email.value.trim()) { setErr("qEmail", "We need an email to reply."); ok = false; }
else if (!emailRe.test(email.value.trim())) { setErr("qEmail", "That email looks off."); ok = false; }
else setErr("qEmail", "");
if (!budget.value) { setErr("qBudget", "Pick a budget range."); ok = false; }
else setErr("qBudget", "");
if (ok) {
form.reset();
toast("Brief received — we'll quote you within a day.");
} else {
toast("Please fix the highlighted fields.");
}
});
// Clear error on input
["qName", "qEmail", "qBudget"].forEach(function (id) {
var el = document.getElementById(id);
var ev = el.tagName === "SELECT" ? "change" : "input";
el.addEventListener(ev, function () { setErr(id, ""); });
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Vantablack Films — Commercials & Brand Film</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Nav -->
<header class="nav" id="nav">
<div class="wrap nav-inner">
<a class="brand" href="#top" aria-label="Vantablack Films home">
<span class="brand-mark" aria-hidden="true">
<span class="rec"></span>
</span>
<span class="brand-name">VANTABLACK<span class="brand-sub">FILMS</span></span>
</a>
<nav class="nav-links" id="navLinks" aria-label="Primary">
<a href="#work">Work</a>
<a href="#outcomes">Outcomes</a>
<a href="#process">Process</a>
<a href="#voices">Voices</a>
<a class="nav-cta" href="#quote">Get a quote</a>
</nav>
<button class="hamburger" id="hamburger" aria-label="Toggle menu" aria-expanded="false" aria-controls="navLinks">
<span></span><span></span><span></span>
</button>
</div>
</header>
<main id="top">
<!-- Hero -->
<section class="hero" id="hero">
<div class="bar bar-top" aria-hidden="true"></div>
<div class="hero-bg" aria-hidden="true"></div>
<div class="wrap hero-inner">
<p class="eyebrow"><span class="dot"></span> Commercial & brand film studio</p>
<h1 class="hero-title">Films that sell,<br /><em>not just scroll.</em></h1>
<p class="hero-lede">
Vantablack Films crafts commercials and brand stories engineered around one thing —
the number that matters to you. Cinematic craft, measurable lift.
</p>
<div class="hero-actions">
<button class="btn btn-primary" data-reel="hero">
<span class="play" aria-hidden="true"></span> Watch the reel
</button>
<a class="btn btn-ghost" href="#quote">Request a quote</a>
</div>
<div class="hero-meta">
<span class="tc" aria-hidden="true">02:14:07</span>
<span class="sep"></span>
<span>Trusted by 40+ brands · 6 industry awards</span>
</div>
</div>
<div class="bar bar-bottom" aria-hidden="true"></div>
</section>
<!-- Client logo wall -->
<section class="clients" aria-label="Selected clients">
<div class="marquee" id="marquee">
<div class="marquee-track">
<span>NORTHWIND</span><span>Halcyon</span><span>PULSE ATHLETIC</span>
<span>Meridian</span><span>Foxglove</span><span>OKTANE</span>
<span>Bright & Co</span><span>VESPER</span>
<span aria-hidden="true">NORTHWIND</span><span aria-hidden="true">Halcyon</span>
<span aria-hidden="true">PULSE ATHLETIC</span><span aria-hidden="true">Meridian</span>
<span aria-hidden="true">Foxglove</span><span aria-hidden="true">OKTANE</span>
<span aria-hidden="true">Bright & Co</span><span aria-hidden="true">VESPER</span>
</div>
</div>
</section>
<!-- Work / case reels -->
<section class="section" id="work">
<div class="wrap">
<div class="section-head reveal">
<p class="kicker">Selected work</p>
<h2>Case reels with receipts</h2>
<p class="section-lede">
Every cut below shipped for a real campaign brief. Filter by what you need —
each reel opens with the result it delivered.
</p>
</div>
<div class="filters reveal" role="tablist" aria-label="Filter reels by category">
<button class="chip is-active" data-filter="all" role="tab" aria-selected="true">All</button>
<button class="chip" data-filter="brand" role="tab" aria-selected="false">Brand film</button>
<button class="chip" data-filter="product" role="tab" aria-selected="false">Product</button>
<button class="chip" data-filter="social" role="tab" aria-selected="false">Social</button>
<button class="chip" data-filter="tvc" role="tab" aria-selected="false">TVC</button>
</div>
<div class="grid" id="reelGrid">
<!-- cards injected by script.js -->
</div>
</div>
</section>
<!-- Outcomes band -->
<section class="section outcomes" id="outcomes">
<div class="wrap">
<div class="section-head reveal">
<p class="kicker">The angle</p>
<h2>We optimize for the outcome, not the applause</h2>
</div>
<div class="stats" id="stats">
<div class="stat reveal">
<span class="stat-num" data-count="312" data-suffix="%">0%</span>
<span class="stat-label">Avg. ROI on paid campaigns</span>
</div>
<div class="stat reveal">
<span class="stat-num" data-count="4.7" data-suffix="x" data-decimals="1">0x</span>
<span class="stat-label">Median engagement lift</span>
</div>
<div class="stat reveal">
<span class="stat-num" data-count="128" data-suffix="M">0M</span>
<span class="stat-label">Views delivered in 2025</span>
</div>
<div class="stat reveal">
<span class="stat-num" data-count="18" data-suffix=" days">0</span>
<span class="stat-label">Avg. brief to delivery</span>
</div>
</div>
</div>
</section>
<!-- Process -->
<section class="section" id="process">
<div class="wrap">
<div class="section-head reveal">
<p class="kicker">How we work</p>
<h2>Three acts, one number</h2>
</div>
<ol class="steps">
<li class="step reveal">
<span class="tc step-tc">ACT 01</span>
<h3>Brief & angle</h3>
<p>We start from your KPI, not a moodboard. One workshop pins the metric, the audience, and the single idea the film has to land.</p>
</li>
<li class="step reveal">
<span class="tc step-tc">ACT 02</span>
<h3>Shoot & craft</h3>
<p>A tight crew, cinema glass, and an edit room that tests hooks against real watch-through data before the grade is locked.</p>
</li>
<li class="step reveal">
<span class="tc step-tc">ACT 03</span>
<h3>Ship & measure</h3>
<p>Cutdowns for every placement, delivered on spec, with a two-week performance readout so the next film starts ahead.</p>
</li>
</ol>
</div>
</section>
<!-- Testimonials -->
<section class="section voices" id="voices">
<div class="wrap">
<div class="section-head reveal">
<p class="kicker">Client voices</p>
<h2>What the brief-side says</h2>
</div>
<div class="quote-box reveal">
<blockquote id="quoteText">
The launch film paid for itself in eleven days. Vantablack treated our conversion
rate like the star of the shoot — and it showed.
</blockquote>
<div class="quote-by">
<strong id="quoteName">Dana Reyes</strong>
<span id="quoteRole">VP Growth, Pulse Athletic</span>
</div>
<div class="dots" id="dots" role="tablist" aria-label="Choose testimonial"></div>
</div>
</div>
</section>
<!-- Quote CTA -->
<section class="section quote" id="quote">
<div class="wrap quote-inner">
<div class="quote-copy reveal">
<p class="kicker">Start a project</p>
<h2>Tell us the number.<br />We'll build the film.</h2>
<p class="section-lede">
Ballpark quote back within one business day. No decks, no discovery-call runaround.
</p>
<ul class="assurances">
<li>Fixed-scope quote in 24h</li>
<li>Kickoff in under 2 weeks</li>
<li>Performance readout included</li>
</ul>
</div>
<form class="quote-form reveal" id="quoteForm" novalidate>
<div class="field">
<label for="qName">Name</label>
<input id="qName" name="name" type="text" autocomplete="name" placeholder="Alex Marlow" />
<span class="err" data-err="qName"></span>
</div>
<div class="field">
<label for="qEmail">Work email</label>
<input id="qEmail" name="email" type="email" autocomplete="email" placeholder="[email protected]" />
<span class="err" data-err="qEmail"></span>
</div>
<div class="field">
<label for="qBudget">Budget range</label>
<select id="qBudget" name="budget">
<option value="">Select a range…</option>
<option value="a">Under $25k</option>
<option value="b">$25k – $75k</option>
<option value="c">$75k – $150k</option>
<option value="d">$150k+</option>
</select>
<span class="err" data-err="qBudget"></span>
</div>
<div class="field">
<label for="qBrief">The brief <span class="opt">(optional)</span></label>
<textarea id="qBrief" name="brief" rows="3" placeholder="What are we selling, and to whom?"></textarea>
</div>
<button class="btn btn-primary btn-block" type="submit">Send the brief</button>
</form>
</div>
</section>
</main>
<footer class="footer">
<div class="wrap footer-inner">
<div class="brand">
<span class="brand-mark" aria-hidden="true"><span class="rec"></span></span>
<span class="brand-name">VANTABLACK<span class="brand-sub">FILMS</span></span>
</div>
<p class="foot-note">Commercials & brand film · Est. 2018 · Fictional studio for demo purposes.</p>
<p class="tc foot-tc" aria-hidden="true">© 2026 · ALL FRAMES RESERVED</p>
</div>
</footer>
<!-- Reel lightbox -->
<div class="lightbox" id="lightbox" hidden>
<div class="lightbox-backdrop" data-close></div>
<div class="lightbox-panel" role="dialog" aria-modal="true" aria-labelledby="lbTitle">
<button class="lightbox-close" data-close aria-label="Close reel">×</button>
<div class="lightbox-frame">
<div class="bar bar-top" aria-hidden="true"></div>
<div class="lightbox-poster" id="lbPoster">
<span class="play play-lg" aria-hidden="true"></span>
</div>
<div class="bar bar-bottom" aria-hidden="true"></div>
</div>
<div class="lightbox-meta">
<span class="tc" id="lbRuntime">00:60:00</span>
<h3 id="lbTitle">Reel</h3>
<p id="lbDesc"></p>
<p class="lightbox-result"><span>Result</span> <strong id="lbResult"></strong></p>
</div>
</div>
</div>
<div class="toast-wrap" id="toastWrap" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Landing Commercial
A one-page marketing site for Vantablack Films, a fictional studio that makes commercials and brand films built to move metrics, not just audiences. The design is film-forward: a near-black background, warm amber accents, Inter for text with JetBrains Mono reserved for timecodes and technical labels, and letterbox black bars that frame a full-bleed hero the way a widescreen frame would. The flow runs from a transparent sticky nav through the letterboxed hero, an auto-scrolling client logo wall, a filterable case-reel grid, an outcomes band with count-up ROI and engagement figures, a three-step process, a rotating client testimonial, and a bold quote-request CTA.
Every case reel opens a lightweight lightbox (closeable via the backdrop, the × button, or Escape) that surfaces the campaign, runtime, and the result it delivered. The filter chips above the grid instantly narrow reels by category, hover reveals a play badge and runtime timecode on each tile, the outcomes stats count up when scrolled into view, and the testimonial rotates on a timer with clickable dots. A toast() helper confirms interactions, and the quote form validates name, email, and budget inline before confirming.
It is entirely vanilla — no frameworks, no build step — so the markup, styles, and single script lift cleanly into any project and re-skin easily. Scroll reveals run through IntersectionObserver, a prefers-reduced-motion block calms the marquee and reveal animations, and the layout reflows from wide desktop down to roughly 360px with an accessible mobile menu.
Illustrative UI only — fictional brand, not a real product.