Streaming — General SVOD Landing
A blockbuster-grade marketing landing page for a fictional general SVOD service, built in pure HTML, CSS and vanilla JavaScript. It pairs a cinematic title-wall hero and email-to-start capture with value-prop cards, horizontally scrollable content showcase rows, ranked and continue-watching badges, three pricing tiers with a featured plan, an accessible single-open FAQ accordion, a full footer, a fading sticky nav, mobile menu, toast feedback and scroll-reveal animations.
MCP
Code
:root {
--bg: #0b0b0f;
--surface: #15151c;
--surface-2: #1e1e27;
--ink: #f4f4f7;
--ink-2: #b6b7c3;
--muted: #83859a;
--brand: #e50914;
--brand-hi: #ff2530;
--accent: #ffffff;
--line: rgba(255, 255, 255, 0.1);
--line-2: rgba(255, 255, 255, 0.16);
--r-sm: 8px;
--r-md: 12px;
--r-lg: 18px;
--shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
--glow: 0 0 0 1px var(--line-2), 0 18px 50px rgba(229, 9, 20, 0.28);
--nav-h: 64px;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
h1, h2, h3 { line-height: 1.15; margin: 0; letter-spacing: -0.02em; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
position: absolute; left: 12px; top: -60px; z-index: 200;
background: var(--ink); color: #000; padding: 10px 16px; border-radius: var(--r-sm);
font-weight: 700; transition: top .2s;
}
.skip-link:focus { top: 12px; }
:focus-visible { outline: 2px solid var(--brand-hi); outline-offset: 3px; border-radius: 4px; }
/* ===== BUTTONS ===== */
.btn {
display: inline-flex; align-items: center; gap: .4em; justify-content: center;
font: inherit; font-weight: 700; cursor: pointer; border: 0;
border-radius: var(--r-sm); padding: 11px 20px; white-space: nowrap;
transition: transform .15s, background .2s, box-shadow .2s, filter .2s;
}
.btn:active { transform: translateY(1px) scale(.99); }
.btn--brand { background: var(--brand); color: #fff; }
.btn--brand:hover { background: var(--brand-hi); }
.btn--cta {
background: var(--brand); color: #fff; font-size: 1.05rem; font-weight: 800;
padding: 14px 24px; box-shadow: 0 8px 24px rgba(229, 9, 20, 0.35);
}
.btn--cta:hover { background: var(--brand-hi); box-shadow: 0 12px 32px rgba(229, 9, 20, 0.5); }
.lang {
background: rgba(0, 0, 0, 0.45); color: var(--ink); border: 1px solid var(--line-2);
border-radius: var(--r-sm); padding: 7px 12px; font: inherit; font-weight: 600;
cursor: pointer; backdrop-filter: blur(6px);
}
.lang:hover { border-color: var(--accent); }
/* ===== NAV ===== */
.nav {
position: fixed; inset: 0 0 auto 0; z-index: 100; height: var(--nav-h);
transition: background .3s, border-color .3s, backdrop-filter .3s;
border-bottom: 1px solid transparent;
}
.nav.is-scrolled {
background: rgba(11, 11, 15, 0.86);
backdrop-filter: blur(12px) saturate(1.2);
border-bottom-color: var(--line);
}
.nav__inner {
max-width: 1280px; margin: 0 auto; height: 100%;
padding: 0 clamp(16px, 4vw, 48px);
display: flex; align-items: center; gap: 28px;
}
.brand {
font-weight: 800; font-size: 1.45rem; letter-spacing: -.04em; color: var(--ink);
display: inline-flex; align-items: center; gap: 4px;
}
.brand__mark { color: var(--brand); font-size: .85em; }
.brand__plus { color: var(--brand); }
.nav__links { display: flex; gap: 22px; margin-left: 14px; }
.nav__links a { color: var(--ink-2); font-weight: 600; font-size: .95rem; transition: color .2s; }
.nav__links a:hover { color: var(--ink); }
.nav__actions { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.nav__burger {
display: none; flex-direction: column; gap: 5px; width: 42px; height: 38px;
background: transparent; border: 1px solid var(--line-2); border-radius: var(--r-sm);
cursor: pointer; align-items: center; justify-content: center; padding: 0;
}
.nav__burger span { width: 20px; height: 2px; background: var(--ink); border-radius: 2px; transition: .25s; }
.nav__burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
/* ===== MOBILE NAV ===== */
.mnav {
position: fixed; inset: var(--nav-h) 0 0 0; z-index: 90;
background: rgba(8, 8, 12, 0.96); backdrop-filter: blur(14px);
animation: fade .2s ease;
}
.mnav__panel { display: flex; flex-direction: column; gap: 4px; padding: 18px; }
.mnav__panel a {
font-size: 1.15rem; font-weight: 700; padding: 16px 14px;
border-bottom: 1px solid var(--line); color: var(--ink);
}
.mnav__cta { border: 0; margin-top: 16px; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
/* ===== HERO ===== */
.hero {
position: relative; min-height: min(94vh, 820px);
display: flex; align-items: center; justify-content: center; text-align: center;
padding: calc(var(--nav-h) + 40px) clamp(16px, 5vw, 48px) 64px;
overflow: hidden;
}
.hero__wall { position: absolute; inset: 0; opacity: .55; }
.wall {
position: absolute; inset: -10% -6%;
display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px;
transform: rotate(-8deg) scale(1.25); filter: saturate(.9);
}
.poster {
border-radius: var(--r-md); aspect-ratio: 2/3;
display: flex; align-items: flex-end; padding: 10px;
font-size: .72rem; font-weight: 700; color: rgba(255, 255, 255, 0.85);
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
animation: drift 14s ease-in-out infinite;
}
.p1 { background: linear-gradient(160deg, #3a1c71, #d76d77); }
.p2 { background: linear-gradient(160deg, #0f2027, #2c5364); }
.p3 { background: linear-gradient(160deg, #870000, #190a05); }
.p4 { background: linear-gradient(160deg, #232526, #414345); }
.p5 { background: linear-gradient(160deg, #42275a, #734b6d); }
.p6 { background: linear-gradient(160deg, #1a2980, #26d0ce); }
.p7 { background: linear-gradient(160deg, #ec008c, #fc6767); }
.p8 { background: linear-gradient(160deg, #0b8793, #360033); }
.p9 { background: linear-gradient(160deg, #485563, #29323c); }
.p10 { background: linear-gradient(160deg, #f12711, #f5af19); }
.p11 { background: linear-gradient(160deg, #136a8a, #267871); }
.p12 { background: linear-gradient(160deg, #cb2d3e, #ef473a); }
.poster:nth-child(odd) { animation-delay: -3s; }
.poster:nth-child(3n) { animation-delay: -6s; }
@keyframes drift { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
@media (prefers-reduced-motion: reduce) { .poster { animation: none; } }
.hero__scrim {
position: absolute; inset: 0;
background:
radial-gradient(120% 90% at 50% 35%, rgba(11, 11, 15, 0.35) 0%, rgba(11, 11, 15, 0.82) 55%, var(--bg) 100%),
linear-gradient(to bottom, rgba(11, 11, 15, 0.4), rgba(11, 11, 15, 0.2) 40%, var(--bg) 96%);
}
.hero__content { position: relative; z-index: 2; max-width: 760px; }
.hero__title {
font-size: clamp(2rem, 6vw, 3.6rem); font-weight: 800;
text-shadow: 0 2px 24px rgba(0, 0, 0, 0.6);
}
.hero__sub {
margin: 18px auto 26px; max-width: 600px; font-size: clamp(1.05rem, 2.4vw, 1.35rem);
color: var(--ink); font-weight: 500; text-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
}
/* ===== START FORM ===== */
.start__label { color: var(--ink-2); font-size: 1rem; margin: 0 0 12px; }
.start__row {
display: flex; gap: 10px; max-width: 560px; margin: 0 auto; flex-wrap: wrap;
justify-content: center;
}
.start__input {
flex: 1 1 280px; min-width: 0; font: inherit; font-size: 1.05rem;
padding: 14px 16px; border-radius: var(--r-sm);
background: rgba(0, 0, 0, 0.55); color: var(--ink);
border: 1px solid var(--line-2); backdrop-filter: blur(6px);
}
.start__input::placeholder { color: var(--muted); }
.start__input:focus { border-color: var(--accent); background: rgba(0, 0, 0, 0.7); outline: none; }
.start__input.is-invalid { border-color: var(--brand-hi); box-shadow: 0 0 0 2px rgba(229, 9, 20, 0.3); }
.start__error { color: #ffb3b8; font-size: .9rem; font-weight: 600; min-height: 1.2em; margin: 10px 0 0; }
.start--inline { margin-top: 18px; }
/* ===== VALUE PROPS ===== */
.props { max-width: 1180px; margin: 0 auto; padding: 8px clamp(16px, 5vw, 48px) 40px; }
.props__grid {
list-style: none; margin: 0; padding: 0;
display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px;
}
.prop {
background: linear-gradient(180deg, var(--surface-2), var(--surface));
border: 1px solid var(--line); border-radius: var(--r-lg); padding: 26px 22px;
transition: transform .2s, border-color .2s, box-shadow .2s;
}
.prop:hover { transform: translateY(-4px); border-color: var(--line-2); box-shadow: var(--shadow); }
.prop__ico { font-size: 2rem; display: block; margin-bottom: 12px; }
.prop h3 { font-size: 1.2rem; margin-bottom: 8px; }
.prop p { color: var(--ink-2); margin: 0; font-size: .95rem; }
/* ===== SHOWCASE ROWS ===== */
.showcase { max-width: 1280px; margin: 0 auto; padding: 30px clamp(0px, 0vw, 0px) 20px; }
.showcase__head, .plans__head { padding: 0 clamp(16px, 5vw, 48px); margin-bottom: 18px; }
.showcase__head h2, .plans__head h2 { font-size: clamp(1.5rem, 4vw, 2rem); font-weight: 800; }
.showcase__head p, .plans__head p { color: var(--muted); margin: 6px 0 0; }
.row { margin-bottom: 34px; }
.row__title {
font-size: 1.15rem; font-weight: 700; margin: 0 0 12px;
padding: 0 clamp(16px, 5vw, 48px);
}
.row__scroll-wrap { position: relative; }
.row__track {
display: flex; gap: 12px; overflow-x: auto; scroll-behavior: smooth;
padding: 6px clamp(16px, 5vw, 48px) 14px;
scrollbar-width: none;
}
.row__track::-webkit-scrollbar { display: none; }
.card {
position: relative; flex: 0 0 auto; width: 220px; aspect-ratio: 16/9;
border-radius: var(--r-md); overflow: hidden; cursor: pointer;
border: 1px solid var(--line);
display: flex; align-items: flex-end; padding: 12px;
transition: transform .22s, box-shadow .22s, z-index 0s; z-index: 1;
}
.card:hover, .card:focus-visible {
transform: scale(1.08) translateY(-4px); z-index: 5;
box-shadow: var(--glow);
}
.card__scrim {
position: absolute; inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.1) 55%, rgba(0, 0, 0, 0.3) 100%);
}
.card__meta { position: relative; z-index: 2; width: 100%; }
.card__name { font-weight: 700; font-size: .95rem; text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); }
.card__tags { display: flex; align-items: center; gap: 6px; margin-top: 4px; flex-wrap: wrap; }
.badge {
font-size: .62rem; font-weight: 800; letter-spacing: .04em;
padding: 2px 6px; border-radius: 4px; line-height: 1.4;
}
.badge--q { background: var(--ink); color: #000; }
.badge--new { background: var(--brand); color: #fff; }
.badge--rank { background: rgba(0, 0, 0, 0.6); border: 1px solid var(--line-2); color: var(--ink-2); }
.card__match { color: #6ee787; font-size: .68rem; font-weight: 800; }
.card__nav-btn {
position: absolute; top: 0; bottom: 14px; width: clamp(16px, 5vw, 48px); z-index: 8;
background: linear-gradient(90deg, rgba(11, 11, 15, 0.92), rgba(11, 11, 15, 0));
border: 0; color: var(--ink); font-size: 1.8rem; cursor: pointer;
display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity .2s;
}
.card__nav-btn--next { left: auto; right: 0; transform: scaleX(-1); }
.card__nav-btn--prev { left: 0; }
.row__scroll-wrap:hover .card__nav-btn { opacity: 1; }
@media (hover: none) { .card__nav-btn { display: none; } }
/* ===== PLANS ===== */
.plans { max-width: 1100px; margin: 0 auto; padding: 36px clamp(16px, 5vw, 48px) 20px; }
.plans__grid {
display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px;
}
.plan {
position: relative; background: linear-gradient(180deg, var(--surface-2), var(--surface));
border: 1px solid var(--line); border-radius: var(--r-lg); padding: 26px 22px;
display: flex; flex-direction: column; transition: transform .2s, border-color .2s, box-shadow .2s;
}
.plan:hover { transform: translateY(-4px); border-color: var(--line-2); box-shadow: var(--shadow); }
.plan.is-featured { border-color: var(--brand); box-shadow: var(--glow); }
.plan__flag {
position: absolute; top: -12px; left: 22px; background: var(--brand); color: #fff;
font-size: .68rem; font-weight: 800; letter-spacing: .06em; padding: 4px 10px; border-radius: 999px;
}
.plan__name { font-size: 1.25rem; font-weight: 800; }
.plan__price { margin: 10px 0 2px; font-size: 2.1rem; font-weight: 800; }
.plan__price span { font-size: .9rem; font-weight: 600; color: var(--muted); }
.plan__tag { color: var(--ink-2); font-size: .9rem; margin: 0 0 16px; }
.plan__feats { list-style: none; margin: 0 0 22px; padding: 0; display: grid; gap: 10px; }
.plan__feats li { display: flex; gap: 9px; align-items: flex-start; font-size: .92rem; color: var(--ink-2); }
.plan__feats li::before { content: "✓"; color: #6ee787; font-weight: 800; }
.plan .btn { margin-top: auto; width: 100%; }
.plans__note { text-align: center; color: var(--muted); font-size: .9rem; margin: 24px auto 0; max-width: 640px; }
/* ===== FAQ ===== */
.faq { max-width: 860px; margin: 0 auto; padding: 40px clamp(16px, 5vw, 48px) 30px; }
.faq__title { font-size: clamp(1.6rem, 4vw, 2.1rem); font-weight: 800; text-align: center; margin-bottom: 22px; }
.faq__list { display: flex; flex-direction: column; gap: 8px; }
.acc { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.acc__btn {
width: 100%; text-align: left; background: transparent; border: 0; color: var(--ink);
font: inherit; font-size: clamp(1rem, 2.5vw, 1.2rem); font-weight: 600; cursor: pointer;
padding: 20px 22px; display: flex; justify-content: space-between; align-items: center; gap: 16px;
}
.acc__btn:hover { background: var(--surface-2); }
.acc__sign { font-size: 1.6rem; font-weight: 400; transition: transform .25s; flex: 0 0 auto; }
.acc[aria-expanded="true"] .acc__sign { transform: rotate(45deg); }
.acc__panel { max-height: 0; overflow: hidden; transition: max-height .3s ease; }
.acc__panel-inner { padding: 0 22px 20px; color: var(--ink-2); }
.faq__cta { text-align: center; margin-top: 30px; }
.faq__cta > p { font-size: 1.1rem; color: var(--ink); font-weight: 500; }
/* ===== FOOTER ===== */
.foot { border-top: 1px solid var(--line); margin-top: 30px; background: #08080c; }
.foot__inner { max-width: 1100px; margin: 0 auto; padding: 40px clamp(16px, 5vw, 48px) 56px; }
.foot__contact { color: var(--muted); margin: 0 0 24px; }
.foot__contact a { text-decoration: underline; }
.foot__cols {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px 24px; margin-bottom: 24px;
}
.foot__cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.foot__cols a { color: var(--muted); font-size: .85rem; }
.foot__cols a:hover { color: var(--ink); text-decoration: underline; }
.lang--foot { margin-bottom: 18px; }
.foot__legal { color: var(--muted); font-size: .82rem; margin: 0; }
@media (max-width: 720px) { .foot__cols { grid-template-columns: repeat(2, 1fr); } }
/* ===== REVEAL ===== */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .6s ease, transform .6s ease; }
.reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
.reveal { opacity: 1; transform: none; transition: none; }
html { scroll-behavior: auto; }
}
/* ===== TOAST ===== */
.toast {
position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 30px);
background: var(--surface-2); color: var(--ink); border: 1px solid var(--line-2);
padding: 13px 20px; border-radius: var(--r-md); box-shadow: var(--shadow);
font-weight: 600; font-size: .95rem; opacity: 0; pointer-events: none;
transition: opacity .25s, transform .25s; z-index: 300; max-width: calc(100vw - 32px);
}
.toast.is-show { opacity: 1; transform: translate(-50%, 0); }
/* ===== RESPONSIVE ===== */
@media (max-width: 860px) {
.nav__links { display: none; }
.nav__burger { display: flex; }
.lang { display: none; }
.btn--brand { padding: 9px 16px; }
}
@media (max-width: 520px) {
.wall { grid-template-columns: repeat(4, 1fr); }
.card { width: 168px; }
.start__row { flex-direction: column; }
.start__input, .start__row .btn { width: 100%; }
.hero { min-height: auto; padding-bottom: 48px; }
.plan__price { font-size: 1.9rem; }
}(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");
}, 2800);
}
/* ---------- Scrolled nav ---------- */
var nav = document.getElementById("nav");
function onScroll() {
if (window.scrollY > 24) nav.classList.add("is-scrolled");
else nav.classList.remove("is-scrolled");
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---------- Mobile nav ---------- */
var burger = document.getElementById("burger");
var mobileNav = document.getElementById("mobileNav");
function setMenu(open) {
burger.setAttribute("aria-expanded", String(open));
mobileNav.hidden = !open;
document.body.style.overflow = open ? "hidden" : "";
}
burger.addEventListener("click", function () {
setMenu(burger.getAttribute("aria-expanded") !== "true");
});
mobileNav.querySelectorAll("a").forEach(function (a) {
a.addEventListener("click", function () { setMenu(false); });
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && burger.getAttribute("aria-expanded") === "true") setMenu(false);
});
/* ---------- Email start forms ---------- */
function wireStart(formId, inputId, errId) {
var form = document.getElementById(formId);
if (!form) return;
var input = document.getElementById(inputId);
var err = document.getElementById(errId);
var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
form.addEventListener("submit", function (e) {
e.preventDefault();
var v = input.value.trim();
if (!v) {
input.classList.add("is-invalid");
err.textContent = "Email is required to start.";
input.focus();
return;
}
if (!re.test(v)) {
input.classList.add("is-invalid");
err.textContent = "Please enter a valid email address.";
input.focus();
return;
}
input.classList.remove("is-invalid");
err.textContent = "";
input.value = "";
toast("Welcome! Choose a plan to finish setting up " + v.split("@")[0] + "'s account.");
var plans = document.getElementById("plans");
if (plans) plans.scrollIntoView({ behavior: "smooth", block: "start" });
});
input.addEventListener("input", function () {
input.classList.remove("is-invalid");
err.textContent = "";
});
}
wireStart("startForm", "email", "startError");
wireStart("startForm2", "email2", "startError2");
/* ---------- Content showcase rows ---------- */
var GRADS = [
"linear-gradient(160deg,#3a1c71,#d76d77)", "linear-gradient(160deg,#0f2027,#2c5364)",
"linear-gradient(160deg,#870000,#190a05)", "linear-gradient(160deg,#232526,#414345)",
"linear-gradient(160deg,#42275a,#734b6d)", "linear-gradient(160deg,#1a2980,#26d0ce)",
"linear-gradient(160deg,#ec008c,#fc6767)", "linear-gradient(160deg,#0b8793,#360033)",
"linear-gradient(160deg,#485563,#29323c)", "linear-gradient(160deg,#f12711,#f5af19)",
"linear-gradient(160deg,#136a8a,#267871)", "linear-gradient(160deg,#cb2d3e,#ef473a)"
];
var ROWS = [
{
title: "Trending Now",
ranked: true,
items: ["Aurora Run", "The Quiet Coast", "Vega Nine", "Last Light", "Crown of Ash", "Saltwater Kings", "Neon Harbor", "Wildflower", "Iron Meridian", "Echo Valley"]
},
{
title: "Nebula+ Originals",
items: ["Glasshouse", "Northwind", "The Cartographer", "Ember & Ash", "Tidewater", "Silver Lining", "Hollow Crown", "Paper Cities", "The Long Field", "Static"]
},
{
title: "Award-Winning Films",
items: ["A Slower River", "Migration", "The Bell Keeper", "Dust to Gold", "Quiet Country", "Lanterns", "Foxglove", "The Inland Sea", "Threadbare", "Open Water"]
},
{
title: "Continue Watching",
progress: true,
items: ["Vega Nine", "Saltwater Kings", "Neon Harbor", "Northwind", "Iron Meridian", "Echo Valley", "Static", "Tidewater"]
}
];
var QUALITY = ["4K", "HD", "HDR"];
var rowsHost = document.getElementById("rows");
ROWS.forEach(function (row, ri) {
var wrap = document.createElement("section");
wrap.className = "row reveal";
var h = document.createElement("h3");
h.className = "row__title";
h.textContent = row.title;
wrap.appendChild(h);
var scrollWrap = document.createElement("div");
scrollWrap.className = "row__scroll-wrap";
var track = document.createElement("div");
track.className = "row__track";
track.setAttribute("role", "list");
row.items.forEach(function (name, i) {
var card = document.createElement("button");
card.type = "button";
card.className = "card";
card.setAttribute("role", "listitem");
card.style.background = GRADS[(ri * 3 + i) % GRADS.length];
card.setAttribute("aria-label", "Play " + name);
var match = 80 + ((i * 7 + ri * 11) % 19);
var quality = QUALITY[(i + ri) % QUALITY.length];
var isNew = i % 4 === 1;
var rankHtml = row.ranked ? '<span class="badge badge--rank">#' + (i + 1) + "</span>" : "";
var newHtml = isNew ? '<span class="badge badge--new">NEW</span>' : "";
card.innerHTML =
'<span class="card__scrim"></span>' +
'<span class="card__meta">' +
'<span class="card__name">' + name + "</span>" +
'<span class="card__tags">' +
rankHtml +
'<span class="badge badge--q">' + quality + "</span>" +
newHtml +
'<span class="card__match">' + match + "% match</span>" +
"</span>" +
"</span>" +
(row.progress
? '<span style="position:absolute;left:0;right:0;bottom:0;height:4px;background:rgba(255,255,255,0.25);z-index:3">' +
'<span style="display:block;height:100%;width:' + (25 + ((i * 13) % 60)) + '%;background:var(--brand)"></span>' +
"</span>"
: "");
card.addEventListener("click", function () {
toast(row.progress ? "Resuming “" + name + "”" : "Playing “" + name + "”");
});
track.appendChild(card);
});
var prev = document.createElement("button");
prev.type = "button";
prev.className = "card__nav-btn card__nav-btn--prev";
prev.setAttribute("aria-label", "Scroll " + row.title + " left");
prev.innerHTML = "‹";
var next = document.createElement("button");
next.type = "button";
next.className = "card__nav-btn card__nav-btn--next";
next.setAttribute("aria-label", "Scroll " + row.title + " right");
next.innerHTML = "‹";
function scrollBy(dir) {
track.scrollBy({ left: dir * Math.round(track.clientWidth * 0.8), behavior: "smooth" });
}
prev.addEventListener("click", function () { scrollBy(-1); });
next.addEventListener("click", function () { scrollBy(1); });
scrollWrap.appendChild(prev);
scrollWrap.appendChild(track);
scrollWrap.appendChild(next);
wrap.appendChild(scrollWrap);
rowsHost.appendChild(wrap);
});
/* ---------- Plans ---------- */
var PLANS = [
{
name: "Mobile", price: "$4.99", tag: "Watch on phone & tablet",
feats: ["1 device at a time", "Good (480p) quality", "Phone & tablet only", "Includes ads"]
},
{
name: "Standard", price: "$11.99", tag: "Great for couples", featured: true,
feats: ["2 devices at once", "Full HD (1080p)", "All devices", "Ad-free", "Offline downloads"]
},
{
name: "Premium", price: "$17.99", tag: "Best for families",
feats: ["4 devices at once", "Ultra HD 4K + HDR", "Spatial audio", "Ad-free", "Offline on 6 devices"]
}
];
var plansGrid = document.getElementById("plansGrid");
PLANS.forEach(function (p) {
var card = document.createElement("article");
card.className = "plan reveal" + (p.featured ? " is-featured" : "");
var feats = p.feats.map(function (f) { return "<li>" + f + "</li>"; }).join("");
card.innerHTML =
(p.featured ? '<span class="plan__flag">MOST POPULAR</span>' : "") +
'<h3 class="plan__name">' + p.name + "</h3>" +
'<p class="plan__price">' + p.price + "<span> /month</span></p>" +
'<p class="plan__tag">' + p.tag + "</p>" +
"<ul class=\"plan__feats\">" + feats + "</ul>" +
'<button class="btn ' + (p.featured ? "btn--cta" : "btn--brand") + '" type="button">Choose ' + p.name + "</button>";
card.querySelector("button").addEventListener("click", function () {
toast("You selected the " + p.name + " plan (" + p.price + "/mo). Enter your email to continue.");
document.getElementById("email").focus();
});
plansGrid.appendChild(card);
});
/* ---------- FAQ accordion ---------- */
var FAQ = [
{ q: "What is Nebula+?", a: "Nebula+ is a streaming demo with thousands of fictional movies, series and live events. Watch as much as you want, whenever you want — all for one low monthly price. There's always something new to discover." },
{ q: "How much does it cost?", a: "Watch Nebula+ from $4.99 to $17.99 a month with no extra costs and no contracts. Choose the plan that's right for you and change or cancel it anytime." },
{ q: "Where can I watch?", a: "Watch anywhere, anytime. Sign in to stream instantly on the web, or on smart TVs, phones, tablets, streaming players and game consoles. Your spot is saved across every device." },
{ q: "How do I cancel?", a: "Nebula+ is flexible. There are no annoying contracts and no commitments. You can easily cancel your account online in two clicks, with no cancellation fees. Start and stop whenever you like." },
{ q: "Is Nebula+ good for kids?", a: "The Kids experience is included with every membership, giving parents control while children enjoy family-friendly originals and films in their own dedicated space." }
];
var faqList = document.getElementById("faqList");
FAQ.forEach(function (item, i) {
var acc = document.createElement("div");
acc.className = "acc";
acc.setAttribute("aria-expanded", "false");
var pid = "faq-panel-" + i;
var bid = "faq-btn-" + i;
acc.innerHTML =
'<button class="acc__btn" id="' + bid + '" aria-expanded="false" aria-controls="' + pid + '">' +
"<span>" + item.q + "</span><span class=\"acc__sign\" aria-hidden=\"true\">+</span>" +
"</button>" +
'<div class="acc__panel" id="' + pid + '" role="region" aria-labelledby="' + bid + '">' +
'<div class="acc__panel-inner">' + item.a + "</div>" +
"</div>";
var btn = acc.querySelector(".acc__btn");
var panel = acc.querySelector(".acc__panel");
btn.addEventListener("click", function () {
var open = acc.getAttribute("aria-expanded") === "true";
faqList.querySelectorAll(".acc").forEach(function (other) {
other.setAttribute("aria-expanded", "false");
other.querySelector(".acc__btn").setAttribute("aria-expanded", "false");
other.querySelector(".acc__panel").style.maxHeight = "0px";
});
if (!open) {
acc.setAttribute("aria-expanded", "true");
btn.setAttribute("aria-expanded", "true");
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
faqList.appendChild(acc);
});
/* ---------- Scroll reveal ---------- */
var reveals = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window) {
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.12, rootMargin: "0px 0px -8% 0px" });
reveals.forEach(function (el) { io.observe(el); });
} else {
reveals.forEach(function (el) { el.classList.add("is-in"); });
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nebula+ — Stream Everything</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&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===== TOP NAV ===== -->
<header class="nav" id="nav">
<div class="nav__inner">
<a class="brand" href="#" aria-label="Nebula Plus home">
<span class="brand__mark" aria-hidden="true">▶</span>NEBULA<span class="brand__plus">+</span>
</a>
<nav class="nav__links" aria-label="Primary">
<a href="#showcase">Browse</a>
<a href="#plans">Plans</a>
<a href="#faq">FAQ</a>
</nav>
<div class="nav__actions">
<button class="lang" type="button" aria-label="Language">EN ⌄</button>
<a class="btn btn--brand" href="#start">Sign In</a>
<button class="nav__burger" id="burger" type="button" aria-label="Open menu" aria-expanded="false" aria-controls="mobileNav">
<span></span><span></span><span></span>
</button>
</div>
</div>
</header>
<!-- ===== MOBILE NAV ===== -->
<div class="mnav" id="mobileNav" hidden>
<nav class="mnav__panel" aria-label="Mobile">
<a href="#showcase">Browse</a>
<a href="#plans">Plans</a>
<a href="#faq">FAQ</a>
<a class="btn btn--brand mnav__cta" href="#start">Sign In</a>
</nav>
</div>
<main id="main">
<!-- ===== HERO BILLBOARD ===== -->
<section class="hero" aria-label="Get started">
<div class="hero__wall" aria-hidden="true">
<div class="wall">
<span class="poster p1">Aurora Run</span>
<span class="poster p2">The Quiet Coast</span>
<span class="poster p3">Vega Nine</span>
<span class="poster p4">Last Light</span>
<span class="poster p5">Crown of Ash</span>
<span class="poster p6">Saltwater Kings</span>
<span class="poster p7">Neon Harbor</span>
<span class="poster p8">Wildflower</span>
<span class="poster p9">Iron Meridian</span>
<span class="poster p10">Echo Valley</span>
<span class="poster p11">Glasshouse</span>
<span class="poster p12">Northwind</span>
</div>
</div>
<div class="hero__scrim" aria-hidden="true"></div>
<div class="hero__content reveal">
<h1 class="hero__title">Movies, series & live. All in one place.</h1>
<p class="hero__sub">Stream thousands of award-winning titles in up to 4K HDR. Watch on any device. Cancel whenever you like.</p>
<form class="start" id="startForm" novalidate>
<p class="start__label">Ready to watch? Enter your email to create or restart your membership.</p>
<div class="start__row">
<label class="sr-only" for="email">Email address</label>
<input class="start__input" id="email" type="email" inputmode="email" autocomplete="email" placeholder="Email address" required />
<button class="btn btn--cta" type="submit">Get Started <span aria-hidden="true">›</span></button>
</div>
<p class="start__error" id="startError" role="alert" aria-live="polite"></p>
</form>
</div>
</section>
<!-- ===== VALUE PROPS ===== -->
<section class="props" aria-label="Why Nebula Plus">
<ul class="props__grid">
<li class="prop reveal">
<span class="prop__ico" aria-hidden="true">📺</span>
<h3>Watch anywhere</h3>
<p>Stream on your phone, tablet, laptop and TV — your spot is always saved across devices.</p>
</li>
<li class="prop reveal">
<span class="prop__ico" aria-hidden="true">⬇️</span>
<h3>Download & go</h3>
<p>Save titles to watch offline on flights, trains and anywhere the signal drops.</p>
</li>
<li class="prop reveal">
<span class="prop__ico" aria-hidden="true">🎬</span>
<h3>4K & HDR</h3>
<p>Crisp Ultra HD picture with Dolby-grade sound on supported titles and devices.</p>
</li>
<li class="prop reveal">
<span class="prop__ico" aria-hidden="true">🚫</span>
<h3>Cancel anytime</h3>
<p>No contracts, no surprises. Join for a single weekend or stay for years.</p>
</li>
</ul>
</section>
<!-- ===== CONTENT SHOWCASE ROWS ===== -->
<section class="showcase" id="showcase" aria-label="Featured content">
<div class="showcase__head reveal">
<h2>There's always something new</h2>
<p>Originals, blockbusters and live events refreshed every week.</p>
</div>
<div id="rows"></div>
</section>
<!-- ===== PRICING / PLANS ===== -->
<section class="plans" id="plans" aria-label="Choose a plan">
<div class="plans__head reveal">
<h2>Pick the plan that fits</h2>
<p>Switch or cancel at any time. Prices shown in USD.</p>
</div>
<div class="plans__grid" id="plansGrid"></div>
<p class="plans__note reveal">All plans include unlimited streaming, no ads on Standard & Premium, and offline downloads.</p>
</section>
<!-- ===== FAQ ===== -->
<section class="faq" id="faq" aria-label="Frequently asked questions">
<h2 class="faq__title reveal">Frequently asked questions</h2>
<div class="faq__list" id="faqList"></div>
<div class="faq__cta reveal">
<p>Ready to watch? Enter your email to create or restart your membership.</p>
<form class="start start--inline" id="startForm2" novalidate>
<div class="start__row">
<label class="sr-only" for="email2">Email address</label>
<input class="start__input" id="email2" type="email" inputmode="email" autocomplete="email" placeholder="Email address" required />
<button class="btn btn--cta" type="submit">Get Started <span aria-hidden="true">›</span></button>
</div>
<p class="start__error" id="startError2" role="alert" aria-live="polite"></p>
</form>
</div>
</section>
</main>
<!-- ===== FOOTER ===== -->
<footer class="foot" aria-label="Footer">
<div class="foot__inner">
<p class="foot__contact">Questions? Call <a href="#">1-800-NEBULA+</a></p>
<nav class="foot__cols" aria-label="Footer links">
<ul><li><a href="#">FAQ</a></li><li><a href="#">Help Centre</a></li><li><a href="#">Account</a></li><li><a href="#">Media Centre</a></li></ul>
<ul><li><a href="#">Investor Relations</a></li><li><a href="#">Jobs</a></li><li><a href="#">Ways to Watch</a></li><li><a href="#">Terms of Use</a></li></ul>
<ul><li><a href="#">Privacy</a></li><li><a href="#">Cookie Preferences</a></li><li><a href="#">Corporate Info</a></li><li><a href="#">Contact Us</a></li></ul>
<ul><li><a href="#">Speed Test</a></li><li><a href="#">Legal Notices</a></li><li><a href="#">Only on Nebula+</a></li><li><a href="#">Gift Cards</a></li></ul>
</nav>
<button class="lang lang--foot" type="button">EN ⌄</button>
<p class="foot__legal">Nebula+ — an illustrative UI demo. Fictional titles, not a real streaming service.</p>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>General SVOD Landing
A premium, Netflix-style marketing landing page for Nebula+, a fictional subscription video-on-demand service. The dark cinematic hero floats a drifting wall of poster gradients behind a layered scrim, with a headline, value sub-copy and an email-to-start form. Submitting a valid address fires a toast and smooth-scrolls the visitor straight to the pricing section, while inline validation flags empty or malformed emails without a page reload.
Below the fold, four value-prop cards (watch anywhere, download and go, 4K & HDR, cancel anytime) lead into the content showcase: horizontally scrollable rows of 16:9 cards that scale and glow on hover, carrying HD/4K/HDR quality badges, NEW flags, ranked numbers, match percentages and continue-watching progress bars. Per-row arrow buttons page the track left and right. The pricing block renders three tiers from data with a highlighted “most popular” plan, and an accessible FAQ accordion opens one panel at a time with animated height and rotating plus signs.
Everything is vanilla: a sticky nav that gains a blurred background on scroll, a slide-in mobile
menu with Escape-to-close, a reusable toast(msg) helper, and an IntersectionObserver-driven
scroll-reveal that degrades gracefully. Semantic landmarks, ARIA on the menu, accordion and forms,
keyboard-usable cards and WCAG-AA contrast keep it accessible, and the layout reflows cleanly down
to ~360px.
Illustrative UI only — fictional titles, not a real streaming service.