Agency — Video / Production Landing
A full-page landing for Nocturne, a fictional film and motion studio, built on a black canvas with cinematic amber-to-red accents and an animated film-grain overlay. A transparent sticky nav sits over a full-bleed hero with an animated poster and play button, followed by a client marquee, a hover-play showreel grid, three service blocks, a behind-the-scenes band with count-up stats, a four-act process, a rotating testimonial carousel, and a bold make-something CTA. Vanilla JS drives the reel modal, scroll reveals, and validated forms.
MCP
Code
:root {
--bg: #0a0807;
--bg-2: #120d0a;
--panel: #18110d;
--ink: #f5efe8;
--muted: #b3a79b;
--faint: #6f655c;
--amber: #f5a623;
--amber-2: #ff7a18;
--red: #e23030;
--line: rgba(245, 239, 232, 0.1);
--line-2: rgba(245, 239, 232, 0.18);
--glow: 0 20px 60px rgba(255, 122, 24, 0.18);
--maxw: 1180px;
--font: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
--display: "Anton", "Inter", sans-serif;
--mono: "Space Mono", ui-monospace, monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
background: var(--bg);
color: var(--ink);
font-family: var(--font);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
/* film grain overlay */
.grain {
position: fixed; inset: 0; z-index: 9999; pointer-events: none; opacity: 0.06;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
animation: grain 0.5s steps(4) infinite;
}
@keyframes grain {
0%,100% { transform: translate(0,0); }
25% { transform: translate(-4%, 2%); }
50% { transform: translate(3%, -3%); }
75% { transform: translate(-2%, 4%); }
}
.eyebrow {
font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.28em;
text-transform: uppercase; color: var(--amber); margin-bottom: 18px;
}
.section-title {
font-family: var(--display); font-weight: 400;
font-size: clamp(2.2rem, 5.4vw, 4.2rem); line-height: 0.98;
letter-spacing: 0.01em; text-transform: uppercase;
}
.section-head { margin-bottom: 56px; }
.flare {
background: linear-gradient(100deg, var(--amber) 0%, var(--amber-2) 50%, var(--red) 100%);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
/* buttons */
.btn {
display: inline-flex; align-items: center; gap: 12px; cursor: pointer;
font-family: var(--font); font-weight: 600; font-size: 0.95rem;
padding: 15px 26px; border-radius: 100px; border: 1px solid transparent;
transition: transform 0.22s ease, box-shadow 0.22s ease, background 0.22s ease;
}
.btn--primary {
background: linear-gradient(100deg, var(--amber-2), var(--red));
color: #fff; box-shadow: var(--glow);
}
.btn--primary:hover { transform: translateY(-3px); box-shadow: 0 26px 70px rgba(226, 48, 48, 0.34); }
.btn--ghost { background: transparent; border-color: var(--line-2); color: var(--ink); }
.btn--ghost:hover { border-color: var(--amber); color: var(--amber); transform: translateY(-3px); }
.play {
width: 22px; height: 22px; border-radius: 50%;
background: rgba(255,255,255,0.18); position: relative; flex: 0 0 auto;
}
.play::after {
content: ""; position: absolute; top: 50%; left: 54%; transform: translate(-50%,-50%);
border-left: 8px solid #fff; border-top: 5px solid transparent; border-bottom: 5px solid transparent;
}
/* NAV */
.nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
transition: background 0.3s ease, border-color 0.3s ease, padding 0.3s ease;
border-bottom: 1px solid transparent; padding: 8px 0;
}
.nav.is-stuck {
background: rgba(10, 8, 7, 0.82); backdrop-filter: blur(14px);
border-bottom-color: var(--line);
}
.nav__inner { display: flex; align-items: center; justify-content: space-between; height: 64px; }
.brand {
display: inline-flex; align-items: center; gap: 10px;
font-family: var(--display); font-size: 1.35rem; letter-spacing: 0.12em;
}
.brand__dot {
width: 11px; height: 11px; border-radius: 50%;
background: linear-gradient(var(--amber), var(--red));
box-shadow: 0 0 14px var(--amber-2);
animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(0.78); } }
.nav__links { display: flex; align-items: center; gap: 30px; }
.nav__links a { font-size: 0.92rem; color: var(--muted); transition: color 0.2s; }
.nav__links a:hover { color: var(--ink); }
.nav__cta {
padding: 10px 20px; border: 1px solid var(--line-2); border-radius: 100px;
color: var(--ink) !important;
}
.nav__cta:hover { border-color: var(--amber); color: var(--amber) !important; }
.nav__toggle { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 8px; }
.nav__toggle span { width: 24px; height: 2px; background: var(--ink); transition: 0.3s; }
.nav.is-open .nav__toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav.is-open .nav__toggle span:nth-child(2) { opacity: 0; }
.nav.is-open .nav__toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
/* HERO */
.hero { position: relative; min-height: 100vh; display: flex; align-items: center; overflow: hidden; }
.hero__poster {
position: absolute; inset: 0; z-index: 0;
background:
radial-gradient(120% 90% at 78% 18%, rgba(255,122,24,0.35), transparent 55%),
radial-gradient(90% 80% at 12% 90%, rgba(226,48,48,0.28), transparent 60%),
linear-gradient(160deg, #1c130c, #0a0807 70%);
animation: drift 16s ease-in-out infinite alternate;
}
@keyframes drift { from { transform: scale(1.05) translate(0,0); } to { transform: scale(1.12) translate(-2%, -2%); } }
.hero__veil {
position: absolute; inset: 0; z-index: 1;
background: linear-gradient(to top, var(--bg) 4%, transparent 40%), linear-gradient(90deg, rgba(10,8,7,0.6), transparent 60%);
}
.hero__inner { position: relative; z-index: 2; padding: 120px 24px 90px; max-width: 880px; }
.hero__title {
font-family: var(--display); font-weight: 400; text-transform: uppercase;
font-size: clamp(2.8rem, 9vw, 6.6rem); line-height: 0.92; letter-spacing: 0.005em;
margin-bottom: 26px;
}
.hero__sub { font-size: clamp(1rem, 2.2vw, 1.22rem); color: var(--muted); max-width: 560px; margin-bottom: 36px; }
.hero__cta { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 52px; }
.hero__meta { display: flex; gap: 38px; flex-wrap: wrap; font-family: var(--mono); font-size: 0.8rem; color: var(--faint); }
.hero__meta strong { color: var(--ink); font-family: var(--font); font-size: 1.4rem; display: block; }
.hero__scroll {
position: absolute; bottom: 28px; left: 50%; transform: translateX(-50%); z-index: 2;
font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.3em; text-transform: uppercase;
color: var(--faint); display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.hero__scroll span { width: 1px; height: 38px; background: linear-gradient(var(--amber), transparent); animation: scrolly 1.8s ease-in-out infinite; }
@keyframes scrolly { 0%,100% { transform: scaleY(0.4); transform-origin: top; opacity: 0.4; } 50% { transform: scaleY(1); opacity: 1; } }
/* CLIENTS MARQUEE */
.clients { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--bg-2); overflow: hidden; }
.marquee { padding: 26px 0; }
.marquee__row {
display: flex; gap: 64px; align-items: center; white-space: nowrap;
width: max-content; animation: marquee 28s linear infinite;
}
.marquee:hover .marquee__row { animation-play-state: paused; }
.marquee__row span {
font-family: var(--display); font-size: 1.5rem; letter-spacing: 0.12em;
color: var(--faint); transition: color 0.25s;
}
.marquee__row span:hover { color: var(--amber); }
@keyframes marquee { to { transform: translateX(-50%); } }
/* generic section spacing */
.work, .services, .bts, .process, .voices { padding: 110px 0; }
/* WORK GRID */
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.card {
border: 1px solid var(--line); border-radius: 16px; overflow: hidden; background: var(--panel);
transition: transform 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
.card:hover { transform: translateY(-6px); border-color: var(--line-2); box-shadow: var(--glow); }
.card__media {
position: relative; aspect-ratio: 16 / 11; overflow: hidden;
background:
radial-gradient(90% 80% at 70% 20%, hsla(var(--hue, 20), 90%, 55%, 0.55), transparent 60%),
linear-gradient(160deg, #2a1c12, #100b08);
}
.card__media { --hue: 20; }
.card__play {
position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%) scale(0.86);
width: 58px; height: 58px; border-radius: 50%;
background: rgba(10,8,7,0.55); backdrop-filter: blur(6px); border: 1px solid rgba(255,255,255,0.3);
opacity: 0; transition: 0.3s;
}
.card__play::after {
content: ""; position: absolute; top: 50%; left: 55%; transform: translate(-50%,-50%);
border-left: 16px solid #fff; border-top: 10px solid transparent; border-bottom: 10px solid transparent;
}
.card:hover .card__play { opacity: 1; transform: translate(-50%,-50%) scale(1); }
.card__time {
position: absolute; bottom: 12px; right: 12px; font-family: var(--mono); font-size: 0.72rem;
padding: 3px 9px; border-radius: 6px; background: rgba(10,8,7,0.7); color: var(--ink);
}
.card__body { padding: 18px 20px 22px; }
.card__body::before {
content: attr(data-cat); display: inline-block; margin-bottom: 10px;
font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.16em; text-transform: uppercase;
color: var(--amber);
}
.card h3 { font-size: 1.12rem; margin-bottom: 6px; }
.card__body p { font-size: 0.88rem; color: var(--muted); }
/* SERVICES */
.services { background: var(--bg-2); border-top: 1px solid var(--line); }
.svc { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }
.svc__item {
padding: 34px 30px 38px; border: 1px solid var(--line); border-radius: 18px;
background: linear-gradient(180deg, var(--panel), transparent);
transition: transform 0.3s, border-color 0.3s;
}
.svc__item:hover { transform: translateY(-6px); border-color: var(--amber); }
.svc__no { font-family: var(--mono); font-size: 0.8rem; color: var(--amber); letter-spacing: 0.2em; }
.svc__item h3 { font-family: var(--display); font-weight: 400; font-size: 1.7rem; text-transform: uppercase; margin: 14px 0 12px; }
.svc__item p { color: var(--muted); font-size: 0.95rem; margin-bottom: 20px; }
.svc__item ul { list-style: none; display: flex; flex-direction: column; gap: 9px; }
.svc__item li { font-size: 0.88rem; color: var(--faint); padding-left: 18px; position: relative; }
.svc__item li::before { content: "›"; position: absolute; left: 0; color: var(--amber); }
/* BEHIND THE SCENES */
.bts__inner { display: grid; grid-template-columns: 1.05fr 1fr; gap: 60px; align-items: center; }
.bts__text { color: var(--muted); font-size: 1.05rem; margin: 22px 0 34px; max-width: 460px; }
.bts__stats { display: flex; gap: 40px; flex-wrap: wrap; }
.bts__stats strong { font-family: var(--display); font-weight: 400; font-size: 2.6rem; color: var(--amber); display: block; }
.bts__stats span { font-size: 0.82rem; color: var(--faint); text-transform: uppercase; letter-spacing: 0.1em; }
.bts__frames { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.bts__frame {
display: block; aspect-ratio: 4 / 3; border-radius: 12px; border: 1px solid var(--line);
background:
radial-gradient(90% 80% at 60% 30%, hsla(var(--hue,20), 88%, 52%, 0.5), transparent 62%),
linear-gradient(160deg, #271a10, #0d0906);
transition: transform 0.35s;
}
.bts__frame:nth-child(odd) { transform: translateY(16px); }
.bts__frames:hover .bts__frame { transform: translateY(0) scale(1.02); }
/* PROCESS */
.process { border-top: 1px solid var(--line); }
.steps { list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; counter-reset: s; }
.step { padding: 0 26px; position: relative; }
.step:not(:last-child)::after { content: ""; position: absolute; top: 22px; right: -1px; width: 1px; height: 90px; background: var(--line); }
.step__no { font-family: var(--display); font-size: 2.4rem; color: var(--line-2); transition: color 0.3s; }
.step:hover .step__no { color: var(--amber); }
.step h3 { font-size: 1.2rem; margin: 12px 0 10px; }
.step p { font-size: 0.92rem; color: var(--muted); }
/* TESTIMONIALS */
.voices { background: var(--bg-2); border-top: 1px solid var(--line); }
.quotes { position: relative; min-height: 220px; max-width: 820px; margin: 0 auto; }
.quote {
position: absolute; inset: 0; opacity: 0; transform: translateY(14px);
transition: opacity 0.5s ease, transform 0.5s ease; pointer-events: none; text-align: center;
}
.quote.is-active { opacity: 1; transform: translateY(0); pointer-events: auto; position: relative; }
.quote blockquote {
font-family: var(--display); font-weight: 400; text-transform: uppercase;
font-size: clamp(1.4rem, 3.4vw, 2.3rem); line-height: 1.12; letter-spacing: 0.01em; margin-bottom: 24px;
}
.quote figcaption strong { display: block; color: var(--amber); }
.quote figcaption span { font-size: 0.85rem; color: var(--faint); }
.quotes__dots { display: flex; gap: 12px; justify-content: center; margin-top: 36px; }
.quotes__dots button { width: 30px; height: 4px; border-radius: 4px; border: 0; background: var(--line-2); cursor: pointer; transition: background 0.3s; }
.quotes__dots button.is-active { background: var(--amber); }
/* CTA */
.cta { position: relative; padding: 130px 0; overflow: hidden; text-align: center; border-top: 1px solid var(--line); }
.cta__poster {
position: absolute; inset: 0; z-index: 0;
background:
radial-gradient(70% 90% at 50% 0%, rgba(255,122,24,0.32), transparent 60%),
radial-gradient(60% 80% at 50% 100%, rgba(226,48,48,0.24), transparent 62%),
var(--bg);
}
.cta__inner { position: relative; z-index: 1; max-width: 760px; }
.cta__title { font-family: var(--display); font-weight: 400; text-transform: uppercase; font-size: clamp(2.6rem, 8vw, 5.4rem); line-height: 0.95; }
.cta__sub { color: var(--muted); font-size: 1.1rem; margin: 22px auto 36px; max-width: 480px; }
.cta__form { display: flex; gap: 12px; max-width: 560px; margin: 0 auto; flex-wrap: wrap; justify-content: center; }
.cta__form input {
flex: 1 1 180px; min-width: 0; padding: 15px 20px; border-radius: 100px;
border: 1px solid var(--line-2); background: rgba(255,255,255,0.04); color: var(--ink); font-size: 0.95rem; font-family: inherit;
}
.cta__form input::placeholder { color: var(--faint); }
.cta__form input:focus { outline: none; border-color: var(--amber); }
.cta__form input.invalid { border-color: var(--red); background: rgba(226,48,48,0.08); }
/* FOOTER */
.footer { border-top: 1px solid var(--line); padding: 70px 0 32px; background: var(--bg); }
.footer__inner { display: grid; grid-template-columns: 2fr 1fr 1fr 1.4fr; gap: 40px; }
.footer__brand p { color: var(--muted); font-size: 0.9rem; margin-top: 14px; max-width: 280px; }
.footer__col h4 { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.16em; color: var(--faint); margin-bottom: 16px; }
.footer__col a { display: block; color: var(--muted); font-size: 0.92rem; margin-bottom: 11px; transition: color 0.2s; }
.footer__col a:hover { color: var(--amber); }
.footer__news { display: flex; gap: 8px; }
.footer__news input { flex: 1; padding: 11px 16px; border-radius: 100px; border: 1px solid var(--line-2); background: rgba(255,255,255,0.04); color: var(--ink); font-size: 0.88rem; min-width: 0; }
.footer__news input:focus { outline: none; border-color: var(--amber); }
.footer__news input.invalid { border-color: var(--red); }
.footer__news button { width: 42px; flex: 0 0 auto; border: 0; border-radius: 50%; background: linear-gradient(var(--amber-2), var(--red)); color: #fff; cursor: pointer; font-size: 1.1rem; }
.footer__bottom { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-top: 50px; padding-top: 26px; border-top: 1px solid var(--line); font-family: var(--mono); font-size: 0.74rem; color: var(--faint); }
/* MODAL */
.modal { position: fixed; inset: 0; z-index: 200; display: none; align-items: center; justify-content: center; }
.modal.is-open { display: flex; }
.modal__backdrop { position: absolute; inset: 0; background: rgba(5,3,2,0.86); backdrop-filter: blur(6px); }
.modal__box { position: relative; z-index: 1; width: min(880px, 92vw); }
.modal__close { position: absolute; top: -44px; right: 0; background: none; border: 0; color: var(--ink); font-size: 2rem; cursor: pointer; line-height: 1; }
.modal__screen {
aspect-ratio: 16 / 9; border-radius: 14px; border: 1px solid var(--line-2); display: flex;
flex-direction: column; align-items: center; justify-content: center; gap: 18px;
background: radial-gradient(80% 80% at 50% 40%, rgba(255,122,24,0.35), transparent 60%), linear-gradient(160deg, #1c130c, #0a0807);
}
.modal__screen p { font-family: var(--mono); letter-spacing: 0.2em; color: var(--muted); text-transform: uppercase; font-size: 0.8rem; }
.modal__bigplay { width: 76px; height: 76px; border-radius: 50%; background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.4); position: relative; }
.modal__bigplay::after { content: ""; position: absolute; top: 50%; left: 56%; transform: translate(-50%,-50%); border-left: 22px solid #fff; border-top: 14px solid transparent; border-bottom: 14px solid transparent; }
/* TOAST */
.toast {
position: fixed; bottom: 28px; left: 50%; transform: translate(-50%, 120%); z-index: 300;
background: var(--panel); border: 1px solid var(--amber); color: var(--ink);
padding: 14px 24px; border-radius: 100px; font-size: 0.92rem; box-shadow: var(--glow);
transition: transform 0.4s cubic-bezier(0.2,0.9,0.3,1.2);
}
.toast.is-show { transform: translate(-50%, 0); }
/* REVEAL */
.reveal { opacity: 0; transform: translateY(30px); transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.is-in { opacity: 1; transform: none; }
/* RESPONSIVE */
@media (max-width: 980px) {
.grid, .svc { grid-template-columns: repeat(2, 1fr); }
.steps { grid-template-columns: repeat(2, 1fr); gap: 40px 0; }
.step::after { display: none; }
.bts__inner { grid-template-columns: 1fr; gap: 40px; }
.footer__inner { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 720px) {
.nav__links {
position: fixed; top: 64px; left: 0; right: 0; flex-direction: column; align-items: stretch; gap: 0;
background: rgba(10,8,7,0.97); backdrop-filter: blur(16px); border-bottom: 1px solid var(--line);
padding: 14px 24px 26px; transform: translateY(-130%); transition: transform 0.35s ease; visibility: hidden;
}
.nav.is-open .nav__links { transform: translateY(0); visibility: visible; }
.nav__links a { padding: 14px 0; border-bottom: 1px solid var(--line); }
.nav__cta { margin-top: 12px; text-align: center; border: 1px solid var(--amber); }
.nav__toggle { display: flex; }
.work, .services, .bts, .process, .voices { padding: 80px 0; }
.grid { grid-template-columns: 1fr; }
.footer__inner { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
.svc { grid-template-columns: 1fr; }
.steps { grid-template-columns: 1fr; }
.footer__inner { grid-template-columns: 1fr; }
.hero__meta { gap: 22px; }
.bts__stats { gap: 24px; }
.cta__form { flex-direction: column; }
.cta__form .btn { justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation: none !important; transition-duration: 0.01ms !important; }
.reveal { opacity: 1; transform: none; }
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 3200);
}
/* ---------- apply data-hue to media gradients ---------- */
document.querySelectorAll("[data-hue]").forEach(function (el) {
el.style.setProperty("--hue", el.getAttribute("data-hue"));
});
/* ---------- sticky nav + mobile menu ---------- */
var nav = document.getElementById("nav");
var toggle = document.getElementById("navToggle");
var navLinks = document.getElementById("navLinks");
window.addEventListener(
"scroll",
function () {
nav.classList.toggle("is-stuck", window.scrollY > 20);
},
{ passive: true }
);
if (toggle) {
toggle.addEventListener("click", function () {
var open = nav.classList.toggle("is-open");
toggle.setAttribute("aria-expanded", open ? "true" : "false");
});
}
if (navLinks) {
navLinks.querySelectorAll("a").forEach(function (a) {
a.addEventListener("click", function () {
nav.classList.remove("is-open");
toggle.setAttribute("aria-expanded", "false");
});
});
}
/* ---------- scroll reveal ---------- */
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
e.target.classList.add("is-in");
io.unobserve(e.target);
}
});
},
{ threshold: 0.14 }
);
document.querySelectorAll(".reveal").forEach(function (el, i) {
el.style.transitionDelay = (i % 6) * 60 + "ms";
io.observe(el);
});
/* ---------- count-up stats ---------- */
function countUp(el) {
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
var start = performance.now();
var dur = 1400;
function tick(now) {
var p = Math.min((now - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(eased * target).toLocaleString();
if (p < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
var statObs = new IntersectionObserver(
function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
countUp(e.target);
statObs.unobserve(e.target);
}
});
},
{ threshold: 0.5 }
);
document.querySelectorAll("[data-count]").forEach(function (el) {
statObs.observe(el);
});
/* ---------- testimonials carousel ---------- */
var quotes = Array.prototype.slice.call(
document.querySelectorAll("#quotes .quote")
);
var dots = Array.prototype.slice.call(
document.querySelectorAll("#quoteDots button")
);
var qi = 0;
var qTimer;
function showQuote(n) {
qi = (n + quotes.length) % quotes.length;
quotes.forEach(function (q, i) {
q.classList.toggle("is-active", i === qi);
});
dots.forEach(function (d, i) {
d.classList.toggle("is-active", i === qi);
});
}
function autoQuote() {
clearInterval(qTimer);
qTimer = setInterval(function () {
showQuote(qi + 1);
}, 5200);
}
dots.forEach(function (d, i) {
d.addEventListener("click", function () {
showQuote(i);
autoQuote();
});
});
if (quotes.length) autoQuote();
/* ---------- card play -> toast ---------- */
document.querySelectorAll("#workGrid .card").forEach(function (card) {
card.addEventListener("click", function () {
var title = card.querySelector("h3");
openModal();
toast("Loading reel — " + (title ? title.textContent : "clip"));
});
});
/* ---------- reel modal ---------- */
var modal = document.getElementById("reelModal");
var playReel = document.getElementById("playReel");
function openModal() {
if (!modal) return;
modal.classList.add("is-open");
modal.setAttribute("aria-hidden", "false");
}
function closeModal() {
if (!modal) return;
modal.classList.remove("is-open");
modal.setAttribute("aria-hidden", "true");
}
if (playReel) playReel.addEventListener("click", openModal);
if (modal) {
modal.querySelectorAll("[data-close]").forEach(function (el) {
el.addEventListener("click", closeModal);
});
}
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") closeModal();
});
/* ---------- form validation ---------- */
function validEmail(v) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
}
function handleForm(form, successMsg) {
if (!form) return;
form.addEventListener("submit", function (e) {
e.preventDefault();
var ok = true;
form.querySelectorAll("input[required]").forEach(function (input) {
var bad =
input.type === "email"
? !validEmail(input.value)
: input.value.trim().length < 2;
input.classList.toggle("invalid", bad);
if (bad) ok = false;
});
if (!ok) {
toast("Please check the highlighted fields.");
return;
}
form.reset();
toast(successMsg);
});
form.querySelectorAll("input").forEach(function (input) {
input.addEventListener("input", function () {
input.classList.remove("invalid");
});
});
}
handleForm(
document.getElementById("ctaForm"),
"Booked — we'll reach out within one business day."
);
handleForm(
document.getElementById("newsForm"),
"You're on the list for reel updates."
);
/* ---------- year (footer is static but keep current) ---------- */
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Nocturne — Film & Motion Studio</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=Anton&family=Inter:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="grain" aria-hidden="true"></div>
<!-- NAV -->
<header class="nav" id="nav">
<div class="wrap nav__inner">
<a href="#top" class="brand" aria-label="Nocturne home">
<span class="brand__dot" aria-hidden="true"></span>
NOCTURNE
</a>
<nav class="nav__links" id="navLinks" aria-label="Primary">
<a href="#work">Work</a>
<a href="#services">Services</a>
<a href="#process">Process</a>
<a href="#voices">Voices</a>
<a href="#contact" class="nav__cta">Start a project</a>
</nav>
<button class="nav__toggle" id="navToggle" 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="hero__poster" aria-hidden="true"></div>
<div class="hero__veil" aria-hidden="true"></div>
<div class="wrap hero__inner">
<p class="eyebrow reveal">Film · Motion · Commercial</p>
<h1 class="hero__title reveal">
We make<br /><span class="flare">moving</span> pictures<br />that move people.
</h1>
<p class="hero__sub reveal">
Nocturne is a film & motion studio crafting cinematic stories for brands
that refuse to be background noise. Shot on set, finished in the suite, felt in the chest.
</p>
<div class="hero__cta reveal">
<button class="btn btn--primary" id="playReel" aria-label="Play showreel">
<span class="play" aria-hidden="true"></span>
Watch the 2026 reel
</button>
<a href="#work" class="btn btn--ghost">See selected work</a>
</div>
<div class="hero__meta reveal">
<span><strong>120+</strong> films shipped</span>
<span><strong>18</strong> awards</span>
<span><strong>4K · 6K</strong> finishing</span>
</div>
</div>
<div class="hero__scroll" aria-hidden="true">scroll<span></span></div>
</section>
<!-- CLIENT MARQUEE -->
<section class="clients" aria-label="Selected clients">
<div class="marquee" id="marquee">
<div class="marquee__row">
<span>AURELIA</span><span>NORTHWIND</span><span>VELO</span><span>HALCYON</span>
<span>OBSIDIAN</span><span>MERIDIAN</span><span>LUMEN CO.</span><span>FÊTE</span>
<span>AURELIA</span><span>NORTHWIND</span><span>VELO</span><span>HALCYON</span>
<span>OBSIDIAN</span><span>MERIDIAN</span><span>LUMEN CO.</span><span>FÊTE</span>
</div>
</div>
</section>
<!-- WORK GRID -->
<section class="work" id="work">
<div class="wrap">
<div class="section-head reveal">
<p class="eyebrow">Selected work</p>
<h2 class="section-title">Frames worth pausing on</h2>
</div>
<div class="grid" id="workGrid">
<article class="card reveal" style="--h:1">
<div class="card__media" data-hue="14">
<span class="card__play" aria-hidden="true"></span>
<span class="card__time">01:42</span>
</div>
<div class="card__body" data-cat="Commercial">
<h3>Aurelia — "Golden Hour"</h3>
<p>Luxury fragrance spot · 35mm anamorphic</p>
</div>
</article>
<article class="card reveal" style="--h:0">
<div class="card__media" data-hue="358">
<span class="card__play" aria-hidden="true"></span>
<span class="card__time">03:08</span>
</div>
<div class="card__body" data-cat="Brand Film">
<h3>Northwind — "Built to Last"</h3>
<p>Brand documentary · field & factory</p>
</div>
</article>
<article class="card reveal" style="--h:1">
<div class="card__media" data-hue="28">
<span class="card__play" aria-hidden="true"></span>
<span class="card__time">00:46</span>
</div>
<div class="card__body" data-cat="Motion">
<h3>Velo — "Kinetic"</h3>
<p>Product launch · 3D motion & type</p>
</div>
</article>
<article class="card reveal" style="--h:0">
<div class="card__media" data-hue="345">
<span class="card__play" aria-hidden="true"></span>
<span class="card__time">04:21</span>
</div>
<div class="card__body" data-cat="Music Video">
<h3>Halcyon — "Neon Rain"</h3>
<p>Music video · neon & practical fx</p>
</div>
</article>
<article class="card reveal" style="--h:0">
<div class="card__media" data-hue="20">
<span class="card__play" aria-hidden="true"></span>
<span class="card__time">00:30</span>
</div>
<div class="card__body" data-cat="Commercial">
<h3>Meridian — "First Light"</h3>
<p>TV spot · aerial & macro</p>
</div>
</article>
<article class="card reveal" style="--h:1">
<div class="card__media" data-hue="6">
<span class="card__play" aria-hidden="true"></span>
<span class="card__time">02:55</span>
</div>
<div class="card__body" data-cat="Brand Film">
<h3>Obsidian — "After Dark"</h3>
<p>Brand film · low-key cinematography</p>
</div>
</article>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="services" id="services">
<div class="wrap">
<div class="section-head reveal">
<p class="eyebrow">What we do</p>
<h2 class="section-title">From the first board<br />to the final grade</h2>
</div>
<div class="svc">
<article class="svc__item reveal">
<span class="svc__no">01</span>
<h3>Film & Commercial</h3>
<p>End-to-end production — concept, casting, location, crew and direction.
We shoot the hero spots that anchor a campaign.</p>
<ul>
<li>Creative direction</li><li>On-set production</li><li>Casting & locations</li>
</ul>
</article>
<article class="svc__item reveal">
<span class="svc__no">02</span>
<h3>Motion & Design</h3>
<p>2D and 3D motion, title sequences, and design-driven explainers that make
complex ideas feel inevitable.</p>
<ul>
<li>3D & CGI</li><li>Title design</li><li>Animated brand systems</li>
</ul>
</article>
<article class="svc__item reveal">
<span class="svc__no">03</span>
<h3>Post & Finishing</h3>
<p>Edit, sound design, color grade and delivery. The room where footage
becomes a film — in 4K and 6K.</p>
<ul>
<li>Editorial</li><li>Color grading</li><li>Sound & mix</li>
</ul>
</article>
</div>
</div>
</section>
<!-- BEHIND THE SCENES -->
<section class="bts" id="bts">
<div class="wrap bts__inner">
<div class="bts__copy reveal">
<p class="eyebrow">Behind the scenes</p>
<h2 class="section-title">A crew that lives<br />for set days</h2>
<p class="bts__text">
Cranes at 5am. Coffee that's gone cold by the third take. The quiet right before
"action." We're a tight team of directors, DPs, editors and colorists who'd rather
be on a soundstage than anywhere else — and it shows on screen.
</p>
<div class="bts__stats">
<div><strong data-count="120">0</strong><span>productions</span></div>
<div><strong data-count="34">0</strong><span>countries shot</span></div>
<div><strong data-count="18">0</strong><span>industry awards</span></div>
</div>
</div>
<div class="bts__frames reveal" aria-hidden="true">
<span class="bts__frame" data-hue="20"></span>
<span class="bts__frame" data-hue="0"></span>
<span class="bts__frame" data-hue="32"></span>
<span class="bts__frame" data-hue="350"></span>
</div>
</div>
</section>
<!-- PROCESS -->
<section class="process" id="process">
<div class="wrap">
<div class="section-head reveal">
<p class="eyebrow">How it works</p>
<h2 class="section-title">Four acts, one film</h2>
</div>
<ol class="steps">
<li class="step reveal"><span class="step__no">01</span><h3>Discovery</h3><p>We dig into the brief, the audience and the feeling. Then we pitch a treatment.</p></li>
<li class="step reveal"><span class="step__no">02</span><h3>Pre-production</h3><p>Storyboards, shot lists, casting, locations and a schedule that holds.</p></li>
<li class="step reveal"><span class="step__no">03</span><h3>The shoot</h3><p>Lights, camera, crew. We capture every frame we planned — and the magic we didn't.</p></li>
<li class="step reveal"><span class="step__no">04</span><h3>Post & delivery</h3><p>Edit, sound, grade and master. Delivered in every format you need.</p></li>
</ol>
</div>
</section>
<!-- TESTIMONIALS -->
<section class="voices" id="voices">
<div class="wrap">
<div class="section-head reveal">
<p class="eyebrow">Voices</p>
<h2 class="section-title">What our clients say</h2>
</div>
<div class="quotes" id="quotes">
<figure class="quote is-active">
<blockquote>"Nocturne didn't just shoot an ad — they gave our brand a pulse. The grade alone made our launch feel like a feature film."</blockquote>
<figcaption><strong>Dana Whitlock</strong><span>VP Brand, Aurelia</span></figcaption>
</figure>
<figure class="quote">
<blockquote>"Every frame felt deliberate. They turned a two-day shoot into a year of content we keep coming back to."</blockquote>
<figcaption><strong>Marc Esteban</strong><span>Head of Marketing, Northwind</span></figcaption>
</figure>
<figure class="quote">
<blockquote>"The most calm, prepared set I've ever stood on — and the loudest reaction we've ever had to a launch film."</blockquote>
<figcaption><strong>Priya Nair</strong><span>Founder, Velo</span></figcaption>
</figure>
</div>
<div class="quotes__dots" id="quoteDots" role="tablist" aria-label="Testimonials">
<button class="is-active" role="tab" aria-label="Testimonial 1"></button>
<button role="tab" aria-label="Testimonial 2"></button>
<button role="tab" aria-label="Testimonial 3"></button>
</div>
</div>
</section>
<!-- CTA -->
<section class="cta" id="contact">
<div class="cta__poster" aria-hidden="true"></div>
<div class="wrap cta__inner reveal">
<h2 class="cta__title">Let's make<br /><span class="flare">something</span></h2>
<p class="cta__sub">Got a launch, a story, or a wild idea? Tell us about it. We answer within one business day.</p>
<form class="cta__form" id="ctaForm" novalidate>
<input type="text" name="name" placeholder="Your name" aria-label="Your name" required />
<input type="email" name="email" placeholder="Work email" aria-label="Work email" required />
<button type="submit" class="btn btn--primary">Book a call</button>
</form>
</div>
</section>
</main>
<!-- FOOTER -->
<footer class="footer">
<div class="wrap footer__inner">
<div class="footer__brand">
<a href="#top" class="brand"><span class="brand__dot" aria-hidden="true"></span>NOCTURNE</a>
<p>A film & motion studio. Made between the soundstage and the suite.</p>
</div>
<nav class="footer__col" aria-label="Studio">
<h4>Studio</h4>
<a href="#work">Work</a><a href="#services">Services</a><a href="#process">Process</a>
</nav>
<nav class="footer__col" aria-label="Connect">
<h4>Connect</h4>
<a href="#contact">Start a project</a><a href="#voices">Reviews</a><a href="#bts">Behind the scenes</a>
</nav>
<div class="footer__col">
<h4>Reel updates</h4>
<form class="footer__news" id="newsForm" novalidate>
<input type="email" name="email" placeholder="your@email.com" aria-label="Email for updates" required />
<button type="submit" aria-label="Subscribe">→</button>
</form>
</div>
</div>
<div class="wrap footer__bottom">
<span>© 2026 Nocturne Studio. Fictional brand.</span>
<span>Shot on location · everywhere.</span>
</div>
</footer>
<!-- REEL MODAL -->
<div class="modal" id="reelModal" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Showreel">
<div class="modal__backdrop" data-close></div>
<div class="modal__box">
<button class="modal__close" data-close aria-label="Close">×</button>
<div class="modal__screen">
<span class="modal__bigplay" aria-hidden="true"></span>
<p>Nocturne · Showreel 2026</p>
</div>
</div>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Video / Production Landing
A complete one-page site for Nocturne, a fictional film & motion studio that “makes moving pictures that move people.” The identity is deliberately cinematic: a near-black background, warm amber → orange → red gradient accents, the condensed display face Anton paired with Inter and a Space Mono technical accent, and a subtle animated film-grain layer over the whole page. The flow runs top to bottom through a transparent sticky nav, a full-bleed hero with a drifting poster and a primary “watch the reel” play button, an auto-scrolling client marquee, a six-tile showreel grid, a three-block services section, a behind-the-scenes band, a four-act process, a testimonial carousel, a high-contrast CTA, and a multi-column footer.
The hero CTA and any work tile open a lightweight showreel modal (closeable with the backdrop, the × button, or Escape), while a toast() helper confirms clicks and form submissions. Work tiles reveal a play overlay and runtime on hover, the client marquee pauses on hover, the behind-the-scenes stats count up when scrolled into view, and the testimonial quotes rotate on a timer with clickable dots. Every section reveals on scroll via IntersectionObserver, and both the CTA and footer-newsletter forms validate inline before confirming.
It is entirely vanilla — no frameworks, no build step — so the markup, styles, and one small script lift cleanly into any project and re-skin easily. A prefers-reduced-motion block disables the grain, drift, and reveal animations for users who opt out, and the layout reflows from wide desktop down to roughly 360px with an animated hamburger menu.
Illustrative UI only — fictional brand, not a real product.