Video — Pricing
A cinematic dark pricing page for a videographer studio with half-day, full-day and per-project rate cards, a live currency and rush-fee toggle, itemized add-ons that update a running estimate in real time, a compact tier comparison table, and a request-quote CTA. Letterbox hero bars, amber film accents and JetBrains Mono timecodes give it a film-forward feel, with keyboard-usable controls, focus rings and toast feedback throughout.
MCP
Code
:root {
--bg: #0a0a0b;
--surface: #141416;
--surface-2: #1c1c1f;
--amber: #ffb020;
--amber-d: #e6971a;
--red: #ff4d4d;
--ink: #f4f4f6;
--muted: #8a8a92;
--line: rgba(255, 255, 255, 0.10);
--line-2: rgba(255, 255, 255, 0.18);
--white: #fff;
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
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;
}
h1, h2, h3 { margin: 0; line-height: 1.15; letter-spacing: -0.02em; }
.amber { color: var(--amber); }
.tc, .tc-mini, .price .amt { font-family: "JetBrains Mono", ui-monospace, monospace; }
/* Focus */
:focus-visible {
outline: 2px solid var(--amber);
outline-offset: 2px;
border-radius: var(--r-sm);
}
/* ---------- Hero ---------- */
.hero {
position: relative;
background:
radial-gradient(1200px 480px at 70% -10%, rgba(255, 176, 32, 0.10), transparent 60%),
var(--bg);
overflow: hidden;
}
.bar {
height: 44px;
background: #000;
}
.bar-top { border-bottom: 1px solid var(--line); }
.bar-bottom { border-top: 1px solid var(--line); }
.hero-inner {
max-width: 1080px;
margin: 0 auto;
padding: 48px 24px 56px;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 12.5px;
color: var(--muted);
border: 1px solid var(--line);
background: rgba(255, 255, 255, 0.03);
padding: 7px 12px;
border-radius: 999px;
margin-bottom: 24px;
}
.eyebrow .tc { color: var(--ink); font-size: 12px; }
.eyebrow .sep { opacity: 0.5; }
.rec {
width: 8px; height: 8px; border-radius: 50%;
background: var(--red);
box-shadow: 0 0 0 0 rgba(255, 77, 77, 0.6);
animation: pulse 1.6s ease-out infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 77, 77, 0.55); }
100% { box-shadow: 0 0 0 8px rgba(255, 77, 77, 0); }
}
.hero h1 {
font-size: clamp(30px, 5.4vw, 52px);
font-weight: 800;
}
.lede {
color: var(--muted);
max-width: 54ch;
margin: 18px 0 28px;
font-size: 16px;
}
/* Controls */
.controls { display: flex; flex-wrap: wrap; gap: 22px; }
.switch { display: flex; flex-direction: column; gap: 8px; }
.switch-label {
font-size: 11px; text-transform: uppercase; letter-spacing: 0.09em;
color: var(--muted); font-weight: 600;
}
.seg {
display: inline-flex;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 999px;
padding: 4px;
gap: 2px;
}
.seg-btn {
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--muted);
background: transparent;
border: 0;
padding: 8px 16px;
border-radius: 999px;
cursor: pointer;
transition: color 0.18s, background 0.18s;
}
.seg-btn:hover { color: var(--ink); }
.seg-btn.is-active {
color: #16110a;
background: var(--amber);
box-shadow: 0 4px 14px rgba(255, 176, 32, 0.22);
}
/* ---------- Sections ---------- */
main { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
.section { padding: 56px 0; border-top: 1px solid var(--line); }
.section:first-child { border-top: 0; }
.section-head { margin-bottom: 28px; }
.section-head h2 { font-size: clamp(22px, 3vw, 30px); font-weight: 700; }
.section-sub { color: var(--muted); margin: 8px 0 0; font-size: 14.5px; }
/* ---------- Rate cards ---------- */
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
}
.card {
position: relative;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 26px 22px 22px;
display: flex;
flex-direction: column;
transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s;
}
.card:hover { transform: translateY(-4px); border-color: var(--line-2); }
.card.is-selected {
border-color: var(--amber);
box-shadow: 0 0 0 1px var(--amber), 0 18px 44px rgba(0, 0, 0, 0.5);
}
.card.is-featured {
background: linear-gradient(180deg, rgba(255, 176, 32, 0.07), var(--surface) 40%);
border-color: rgba(255, 176, 32, 0.35);
}
.ribbon {
position: absolute;
top: -11px; left: 22px;
background: var(--amber);
color: #16110a;
font-size: 11px; font-weight: 700;
letter-spacing: 0.03em;
padding: 4px 11px;
border-radius: 999px;
}
.card-kicker {
font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.08em;
color: var(--amber); font-weight: 700;
}
.card h3 { font-size: 21px; margin: 8px 0 12px; font-weight: 700; }
.price {
display: flex;
align-items: baseline;
gap: 3px;
flex-wrap: wrap;
}
.price .from { font-size: 12px; color: var(--muted); margin-right: 4px; align-self: center; }
.price .cur { font-size: 20px; font-weight: 700; color: var(--ink); align-self: flex-start; margin-top: 3px; }
.price .amt { font-size: 34px; font-weight: 700; color: var(--white); }
.price .per { font-size: 13px; color: var(--muted); }
.card-note { color: var(--muted); font-size: 13.5px; margin: 12px 0 0; }
.feat {
list-style: none;
margin: 18px 0 22px;
padding: 18px 0 0;
border-top: 1px solid var(--line);
display: grid;
gap: 10px;
font-size: 14px;
}
.feat li {
position: relative;
padding-left: 24px;
color: #d7d7db;
}
.feat li::before {
content: "";
position: absolute;
left: 0; top: 6px;
width: 14px; height: 8px;
border-left: 2px solid var(--amber);
border-bottom: 2px solid var(--amber);
transform: rotate(-45deg);
}
.pick-btn {
margin-top: auto;
font: inherit; font-weight: 600; font-size: 14px;
color: var(--ink);
background: var(--surface-2);
border: 1px solid var(--line-2);
padding: 12px 14px;
border-radius: var(--r-md);
cursor: pointer;
transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.pick-btn:hover { background: rgba(255, 255, 255, 0.08); }
.card.is-selected .pick-btn {
background: var(--amber);
color: #16110a;
border-color: var(--amber);
}
/* ---------- Add-ons ---------- */
.addon-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14px;
}
.addon {
display: flex;
align-items: flex-start;
gap: 14px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 18px;
cursor: pointer;
transition: border-color 0.18s, background 0.18s;
}
.addon:hover { border-color: var(--line-2); }
.addon.is-on { border-color: rgba(255, 176, 32, 0.5); background: rgba(255, 176, 32, 0.05); }
.addon-check {
flex: none;
width: 22px; height: 22px;
border: 1.5px solid var(--line-2);
border-radius: var(--r-sm);
margin-top: 2px;
display: grid;
place-items: center;
transition: background 0.18s, border-color 0.18s;
}
.addon.is-on .addon-check {
background: var(--amber);
border-color: var(--amber);
}
.addon-check svg { width: 13px; height: 13px; opacity: 0; transition: opacity 0.15s; }
.addon.is-on .addon-check svg { opacity: 1; }
.addon-body { flex: 1; min-width: 0; }
.addon-title {
display: flex; justify-content: space-between; gap: 10px; align-items: baseline;
font-weight: 600; font-size: 14.5px;
}
.addon-price { color: var(--amber); font-weight: 700; font-size: 13.5px; white-space: nowrap; }
.addon-price .amt { font-family: "JetBrains Mono", monospace; }
.addon-desc { color: var(--muted); font-size: 13px; margin-top: 4px; }
.addon .badge {
display: inline-block;
margin-top: 8px;
font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
color: var(--amber);
border: 1px solid rgba(255, 176, 32, 0.4);
border-radius: 999px;
padding: 2px 8px;
}
/* ---------- Comparison table ---------- */
.table-wrap {
overflow-x: auto;
border: 1px solid var(--line);
border-radius: var(--r-lg);
background: var(--surface);
}
.cmp {
width: 100%;
border-collapse: collapse;
min-width: 520px;
font-size: 14px;
}
.cmp th, .cmp td {
padding: 14px 18px;
text-align: left;
border-bottom: 1px solid var(--line);
}
.cmp thead th {
font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
color: var(--muted); font-weight: 700;
background: var(--surface-2);
}
.cmp tbody th { color: var(--ink); font-weight: 600; }
.cmp td { color: #cfcfd4; }
.cmp tr:last-child th, .cmp tr:last-child td { border-bottom: 0; }
.cmp .col-hl {
background: rgba(255, 176, 32, 0.06);
color: var(--ink);
}
.cmp thead .col-hl { color: var(--amber); }
.cmp .yes { color: var(--amber); font-weight: 700; }
.cmp .no { color: var(--muted); }
/* ---------- Quote + estimate ---------- */
.quote {
display: grid;
grid-template-columns: 1fr 1.1fr;
gap: 20px;
align-items: start;
}
.estimate {
position: sticky;
top: 20px;
background: linear-gradient(180deg, rgba(255, 176, 32, 0.06), var(--surface));
border: 1px solid rgba(255, 176, 32, 0.28);
border-radius: var(--r-lg);
padding: 24px;
}
.est-line {
display: flex; justify-content: space-between; align-items: baseline;
padding: 12px 0;
border-bottom: 1px solid var(--line);
}
.est-label { color: var(--muted); font-size: 13.5px; }
.est-val { font-weight: 600; font-size: 15px; text-align: right; }
.est-line.total { border-bottom: 0; padding-top: 16px; }
.est-line.total .est-label { color: var(--ink); font-weight: 600; }
.est-val.big {
font-family: "JetBrains Mono", monospace;
font-size: 30px; font-weight: 700;
color: var(--amber);
}
.est-fine { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
.quote-form {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 26px 24px;
}
.quote-form h2 { font-size: 22px; }
.field { margin-top: 16px; display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 13px; color: var(--muted); font-weight: 600; }
.field input, .field textarea {
font: inherit; font-size: 14px;
color: var(--ink);
background: var(--bg);
border: 1px solid var(--line-2);
border-radius: var(--r-md);
padding: 11px 13px;
resize: vertical;
transition: border-color 0.18s, box-shadow 0.18s;
}
.field input::placeholder, .field textarea::placeholder { color: #565660; }
.field input:focus, .field textarea:focus {
outline: none;
border-color: var(--amber);
box-shadow: 0 0 0 3px rgba(255, 176, 32, 0.16);
}
.field.err input, .field.err textarea { border-color: var(--red); }
.submit-btn {
margin-top: 22px;
width: 100%;
font: inherit; font-weight: 700; font-size: 15px;
color: #16110a;
background: var(--amber);
border: 0;
padding: 14px;
border-radius: var(--r-md);
cursor: pointer;
transition: background 0.18s, transform 0.1s;
}
.submit-btn:hover { background: var(--amber-d); }
.submit-btn:active { transform: translateY(1px); }
/* ---------- Footer ---------- */
.foot {
max-width: 1080px;
margin: 0 auto;
padding: 26px 24px 48px;
border-top: 1px solid var(--line);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
color: var(--muted);
font-size: 12.5px;
}
.tc-mini { color: var(--muted); }
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 20px);
background: var(--surface-2);
border: 1px solid var(--line-2);
color: var(--ink);
padding: 13px 20px;
border-radius: var(--r-md);
font-size: 14px; font-weight: 500;
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55);
opacity: 0;
pointer-events: none;
transition: opacity 0.24s, transform 0.24s;
z-index: 50;
max-width: calc(100% - 32px);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
.toast::before {
content: "";
display: inline-block;
width: 8px; height: 8px; margin-right: 9px;
border-radius: 50%; background: var(--amber);
vertical-align: middle;
}
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.cards { grid-template-columns: 1fr; }
.quote { grid-template-columns: 1fr; }
.estimate { position: static; }
}
@media (max-width: 520px) {
.hero-inner { padding: 32px 18px 40px; }
main { padding: 0 18px; }
.section { padding: 40px 0; }
.controls { gap: 16px; }
.seg-btn { padding: 8px 12px; font-size: 12.5px; }
.addon-grid { grid-template-columns: 1fr; }
.price .amt { font-size: 30px; }
.est-val.big { font-size: 26px; }
.foot { flex-direction: column; }
}(function () {
"use strict";
// ---- Data (base prices in USD) ----
var BASE = {
half: { day: 1200, hour: 340, label: "Half Day" },
full: { day: 2150, hour: 275, label: "Full Day" },
project: { day: 6800, hour: 0, label: "Project" }
};
// approximate hours used to derive hourly totals for day-rate display parity
var TIER_HOURS = { half: 4, full: 9, project: 0 };
var RUSH_RATE = 0.2; // 20% surcharge in hourly/rush mode
var ADDONS = [
{ id: "second", title: "Second shooter", price: 650, desc: "Extra camera op for multi-angle coverage.", badge: "" },
{ id: "drone", title: "Drone / aerial", price: 480, desc: "Licensed FPV & cinematic aerial passes.", badge: "Popular" },
{ id: "grade", title: "Advanced color grade", price: 350, desc: "Per-scene look development + LUTs.", badge: "" },
{ id: "rush", title: "Same-week delivery", price: 500, desc: "Guaranteed turnaround under 5 days.", badge: "" },
{ id: "audio", title: "On-site audio kit", price: 220, desc: "Lav mics, boom & recorded scratch.", badge: "" },
{ id: "captions", title: "Captions & subtitles", price: 160, desc: "SRT + burned-in social versions.", badge: "" }
];
var CUR = {
USD: { sign: "$", rate: 1 },
EUR: { sign: "€", rate: 0.92 },
GBP: { sign: "£", rate: 0.79 }
};
var state = {
billing: "day",
currency: "USD",
tier: "full",
addons: {}
};
// ---- Helpers ----
function fmt(n) {
return Math.round(n).toLocaleString("en-US");
}
function conv(usd) {
return usd * CUR[state.currency].rate;
}
function sign() {
return CUR[state.currency].sign;
}
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
// Compute the displayed base price for a tier under current billing mode
function tierDisplayPrice(tier) {
var b = BASE[tier];
if (tier === "project") return b.day; // project always fixed "from"
if (state.billing === "hour") {
return b.hour * TIER_HOURS[tier] * (1 + RUSH_RATE);
}
return b.day;
}
// ---- Render add-ons ----
var addonsWrap = document.getElementById("addons");
var checkSVG =
'<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 12l4.5 4.5L19 7" stroke="#16110a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
ADDONS.forEach(function (a) {
var el = document.createElement("button");
el.type = "button";
el.className = "addon";
el.setAttribute("aria-pressed", "false");
el.dataset.addon = a.id;
el.innerHTML =
'<span class="addon-check">' + checkSVG + "</span>" +
'<span class="addon-body">' +
'<span class="addon-title"><span>' + a.title + "</span>" +
'<span class="addon-price">+' + sign() + '<span class="amt" data-addon-amt="' + a.id + '">' + fmt(conv(a.price)) + "</span></span></span>" +
'<span class="addon-desc">' + a.desc + "</span>" +
(a.badge ? '<span class="badge">' + a.badge + "</span>" : "") +
"</span>";
el.addEventListener("click", function () {
state.addons[a.id] = !state.addons[a.id];
el.classList.toggle("is-on", state.addons[a.id]);
el.setAttribute("aria-pressed", state.addons[a.id] ? "true" : "false");
update();
});
addonsWrap.appendChild(el);
});
// ---- Currency signs everywhere ----
function refreshSigns() {
document.querySelectorAll("[data-cur-sign]").forEach(function (s) {
s.textContent = sign();
});
}
// ---- Update all prices & estimate ----
function update() {
refreshSigns();
// Rate card prices
["half", "full", "project"].forEach(function (tier) {
var amtEl = document.querySelector('[data-price="' + tier + '"]');
if (amtEl) amtEl.textContent = fmt(conv(tierDisplayPrice(tier)));
});
// "per" label
document.querySelectorAll("[data-per]").forEach(function (p) {
p.textContent = state.billing === "hour" ? "/ shoot" : "/ day";
});
// Add-on prices
ADDONS.forEach(function (a) {
var el = document.querySelector('[data-addon-amt="' + a.id + '"]');
if (el) el.textContent = fmt(conv(a.price));
});
// Estimate
var base = tierDisplayPrice(state.tier);
var addTotal = 0;
var count = 0;
ADDONS.forEach(function (a) {
if (state.addons[a.id]) {
addTotal += a.price;
count++;
}
});
var subtotal = base + addTotal;
document.getElementById("estTier").textContent =
BASE[state.tier].label +
(state.billing === "hour" && state.tier !== "project" ? " (rush)" : "");
document.getElementById("estAddons").textContent =
count === 0 ? "None selected" : count + (count === 1 ? " add-on" : " add-ons") + " · +" + sign() + fmt(conv(addTotal));
document.getElementById("estTotal").textContent = fmt(conv(subtotal));
var fine;
if (state.tier === "project") {
fine = "Project bids are fixed-scope; total shown is a starting estimate.";
} else if (state.billing === "hour") {
fine = "Hourly + rush mode: 20% surcharge applied to on-site time.";
} else {
fine = "Standard day rate. Rush surcharge not applied.";
}
document.getElementById("estFine").textContent = fine;
}
// ---- Segmented controls ----
function wireSeg(id, key, cb) {
var seg = document.getElementById(id);
seg.addEventListener("click", function (e) {
var btn = e.target.closest(".seg-btn");
if (!btn) return;
seg.querySelectorAll(".seg-btn").forEach(function (b) {
b.classList.remove("is-active");
b.setAttribute("aria-pressed", "false");
});
btn.classList.add("is-active");
btn.setAttribute("aria-pressed", "true");
cb(btn);
});
}
wireSeg("billingSeg", "billing", function (btn) {
state.billing = btn.dataset.billing;
update();
toast(state.billing === "hour" ? "Switched to hourly + rush pricing" : "Switched to standard day rates");
});
wireSeg("currencySeg", "currency", function (btn) {
state.currency = btn.dataset.cur;
update();
toast("Prices shown in " + state.currency);
});
// ---- Tier selection ----
var cards = document.getElementById("cards");
function selectTier(tier, announce) {
state.tier = tier;
cards.querySelectorAll(".card").forEach(function (c) {
c.classList.toggle("is-selected", c.dataset.tier === tier);
});
cards.querySelectorAll(".pick-btn").forEach(function (b) {
var on = b.dataset.pick === tier;
b.textContent = on ? "Selected ✓" : "Select " + BASE[b.dataset.pick].label;
});
update();
if (announce) toast(BASE[tier].label + " selected");
}
cards.addEventListener("click", function (e) {
var btn = e.target.closest(".pick-btn");
if (btn) {
selectTier(btn.dataset.pick, true);
return;
}
});
// keyboard: Enter/Space on focused card selects it
cards.querySelectorAll(".card").forEach(function (card) {
card.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
selectTier(card.dataset.tier, true);
}
});
});
// ---- Quote form ----
var form = document.getElementById("quoteForm");
function setErr(fieldEl, on) {
fieldEl.classList.toggle("err", on);
}
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = document.getElementById("qName");
var email = document.getElementById("qEmail");
var ok = true;
if (!name.value.trim()) { setErr(name.parentElement, true); ok = false; }
else setErr(name.parentElement, false);
var emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value.trim());
if (!emailOk) { setErr(email.parentElement, true); ok = false; }
else setErr(email.parentElement, false);
if (!ok) {
toast("Please add your name and a valid email.");
(name.value.trim() ? email : name).focus();
return;
}
var total = document.getElementById("estTotal").textContent;
toast("Quote request sent — est. " + sign() + total + ". We'll reply within a day.");
form.reset();
});
// clear error on input
form.querySelectorAll("input").forEach(function (inp) {
inp.addEventListener("input", function () { setErr(inp.parentElement, false); });
});
// ---- Running timecode in hero (25fps) ----
var tcEl = document.getElementById("timecode");
var frame = 0;
function pad(n) { return String(n).padStart(2, "0"); }
setInterval(function () {
frame = (frame + 1) % (25 * 60 * 60 * 24);
var f = frame % 25;
var totalSec = Math.floor(frame / 25);
var s = totalSec % 60;
var m = Math.floor(totalSec / 60) % 60;
var h = Math.floor(totalSec / 3600) % 24;
tcEl.textContent = pad(h) + ":" + pad(m) + ":" + pad(s) + ":" + pad(f);
}, 40);
// ---- Init ----
selectTier("full", false);
update();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Northlight Frames — Pricing</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@500;700&display=swap"
rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Hero with letterbox bars -->
<header class="hero" role="banner">
<div class="bar bar-top" aria-hidden="true"></div>
<div class="hero-inner">
<div class="eyebrow">
<span class="rec" aria-hidden="true"></span>
<span class="tc" id="timecode">00:00:00:00</span>
<span class="sep">·</span>
<span>Northlight Frames — Studio Rates 2026</span>
</div>
<h1>Cinematic coverage,<br /><span class="amber">priced for the shoot.</span></h1>
<p class="lede">
Documentary, brand and event films from a Portland-based two-person crew.
Pick a rate card, stack the add-ons you need, and get a same-day quote.
</p>
<div class="controls" role="group" aria-label="Pricing options">
<div class="switch" role="group" aria-label="Billing mode">
<span class="switch-label">Billing</span>
<div class="seg" id="billingSeg">
<button class="seg-btn is-active" data-billing="day" aria-pressed="true">Day rate</button>
<button class="seg-btn" data-billing="hour" aria-pressed="false">Hourly + rush</button>
</div>
</div>
<div class="switch" role="group" aria-label="Currency">
<span class="switch-label">Currency</span>
<div class="seg" id="currencySeg">
<button class="seg-btn is-active" data-cur="USD" aria-pressed="true">USD</button>
<button class="seg-btn" data-cur="EUR" aria-pressed="false">EUR</button>
<button class="seg-btn" data-cur="GBP" aria-pressed="false">GBP</button>
</div>
</div>
</div>
</div>
<div class="bar bar-bottom" aria-hidden="true"></div>
</header>
<main>
<!-- Rate cards -->
<section class="section" aria-labelledby="rates-h">
<div class="section-head">
<h2 id="rates-h">Rate cards</h2>
<p class="section-sub">All cards include pre-production call, gear, and licensed music. Travel within 30 mi free.</p>
</div>
<div class="cards" id="cards">
<!-- Half day -->
<article class="card" data-tier="half" tabindex="0" aria-label="Half day tier">
<header class="card-head">
<span class="card-kicker">Half Day · up to 4h</span>
<h3>Half Day</h3>
<div class="price">
<span class="cur" data-cur-sign>$</span><span class="amt" data-price="half">1,200</span>
<span class="per" data-per>/ day</span>
</div>
<p class="card-note">One shooter, one location. Ideal for a testimonial or short social piece.</p>
</header>
<ul class="feat">
<li>1 videographer</li>
<li>Up to 4 hours on site</li>
<li>One edited deliverable (≤ 90s)</li>
<li>1 round of revisions</li>
<li>7-day delivery</li>
</ul>
<button class="pick-btn" data-pick="half">Select Half Day</button>
</article>
<!-- Full day (featured) -->
<article class="card is-featured" data-tier="full" tabindex="0" aria-label="Full day tier, most booked">
<span class="ribbon">Most booked</span>
<header class="card-head">
<span class="card-kicker">Full Day · up to 9h</span>
<h3>Full Day</h3>
<div class="price">
<span class="cur" data-cur-sign>$</span><span class="amt" data-price="full">2,150</span>
<span class="per" data-per>/ day</span>
</div>
<p class="card-note">The everyday workhorse for brand films, events and multi-scene shoots.</p>
</header>
<ul class="feat">
<li>1 videographer + audio kit</li>
<li>Up to 9 hours, multiple locations</li>
<li>Up to 3 edited deliverables</li>
<li>2 rounds of revisions</li>
<li>5-day delivery + raw archive</li>
</ul>
<button class="pick-btn" data-pick="full">Select Full Day</button>
</article>
<!-- Project -->
<article class="card" data-tier="project" tabindex="0" aria-label="Project tier">
<header class="card-head">
<span class="card-kicker">Project · fixed scope</span>
<h3>Project</h3>
<div class="price">
<span class="from">from</span>
<span class="cur" data-cur-sign>$</span><span class="amt" data-price="project">6,800</span>
</div>
<p class="card-note">Full production from treatment to final grade, quoted per deliverable.</p>
</header>
<ul class="feat">
<li>2-person crew + producer</li>
<li>Multi-day scheduling</li>
<li>Scripting & storyboard</li>
<li>Color grade + sound mix</li>
<li>Unlimited licensed masters</li>
</ul>
<button class="pick-btn" data-pick="project">Select Project</button>
</article>
</div>
</section>
<!-- Add-ons + estimate -->
<section class="section" aria-labelledby="addons-h">
<div class="section-head">
<h2 id="addons-h">Add-ons</h2>
<p class="section-sub">Toggle extras — your estimate at the bottom updates instantly.</p>
</div>
<div class="addon-grid" id="addons">
<!-- populated by JS -->
</div>
</section>
<!-- Comparison table -->
<section class="section" aria-labelledby="cmp-h">
<div class="section-head">
<h2 id="cmp-h">Compare tiers</h2>
<p class="section-sub">What each rate card ships with.</p>
</div>
<div class="table-wrap" role="region" aria-label="Tier comparison" tabindex="0">
<table class="cmp">
<thead>
<tr>
<th scope="col">Feature</th>
<th scope="col">Half Day</th>
<th scope="col" class="col-hl">Full Day</th>
<th scope="col">Project</th>
</tr>
</thead>
<tbody>
<tr><th scope="row">Crew size</th><td>1</td><td class="col-hl">1 + audio</td><td>2 + producer</td></tr>
<tr><th scope="row">Hours on site</th><td>4</td><td class="col-hl">9</td><td>Scheduled</td></tr>
<tr><th scope="row">Deliverables</th><td>1</td><td class="col-hl">3</td><td>Unlimited</td></tr>
<tr><th scope="row">Revisions</th><td>1</td><td class="col-hl">2</td><td>Unlimited</td></tr>
<tr><th scope="row">Color grade</th><td class="no">—</td><td class="col-hl yes">✓</td><td class="yes">✓</td></tr>
<tr><th scope="row">Raw archive</th><td class="no">—</td><td class="col-hl yes">✓</td><td class="yes">✓</td></tr>
<tr><th scope="row">Delivery</th><td>7 days</td><td class="col-hl">5 days</td><td>Milestones</td></tr>
</tbody>
</table>
</div>
</section>
<!-- Running estimate + quote CTA -->
<section class="section quote" aria-labelledby="quote-h">
<div class="estimate" aria-live="polite">
<div class="est-line">
<span class="est-label">Selected tier</span>
<span class="est-val" id="estTier">Full Day</span>
</div>
<div class="est-line">
<span class="est-label">Add-ons</span>
<span class="est-val" id="estAddons">0 selected</span>
</div>
<div class="est-line total">
<span class="est-label">Estimated total</span>
<span class="est-val big"><span data-cur-sign>$</span><span id="estTotal">2,150</span></span>
</div>
<p class="est-fine" id="estFine">Standard day rate. Rush surcharge not applied.</p>
</div>
<form class="quote-form" id="quoteForm" novalidate>
<h2 id="quote-h">Request a quote</h2>
<p class="section-sub">We reply within one business day with a fixed bid.</p>
<div class="field">
<label for="qName">Name</label>
<input id="qName" name="name" type="text" autocomplete="name" required placeholder="Alex Rivera" />
</div>
<div class="field">
<label for="qEmail">Email</label>
<input id="qEmail" name="email" type="email" autocomplete="email" required placeholder="[email protected]" />
</div>
<div class="field">
<label for="qBrief">Project brief</label>
<textarea id="qBrief" name="brief" rows="3" placeholder="Two-day brand shoot in Portland, ~3 deliverables…"></textarea>
</div>
<button type="submit" class="submit-btn">Send request →</button>
</form>
</section>
</main>
<footer class="foot">
<span>© 2026 Northlight Frames — fictional studio for demo purposes.</span>
<span class="tc-mini">TC 25fps · Portland OR</span>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A film-forward pricing page for a fictional videographer studio, Northlight Frames. The hero sits inside letterbox black bars with a running timecode and a billing toggle that flips every rate card between hourly rush pricing and standard day rates, while a currency switcher rewrites all figures on the fly. Three headline rate cards — Half Day, Full Day and Project — carry feature lists, badges and a highlighted “most booked” tier.
Below the cards, an interactive add-ons panel lets visitors toggle extras like a second shooter, drone coverage, color grading and same-week delivery. Each toggle animates a running estimate at the bottom of the page, combining the selected base tier with the chosen add-ons and any rush surcharge. A compact comparison table lines up what each tier includes at a glance.
Everything is vanilla JS: the currency and billing switches, the add-on toggles, the live estimate math, and a request-quote form that validates input and fires a toast. Controls are keyboard-operable with visible focus rings, the layout collapses to a single column under 520px, and colors meet WCAG AA contrast against the cinematic dark surface.