Interior — Pricing Packages
A warm editorial pricing page for an interior studio presenting three design package tiers — Refresh, Signature, Full Home — each with room count, revision rounds, deliverables list, and delivery timeline. A monthly-or-onetime billing toggle recalculates every price live, a room-count stepper scales the estimate, a sticky comparison table lines up every deliverable feature by feature, and a Book action opens a slide-in panel that confirms with a toast. Built with vanilla HTML, CSS, and JavaScript in muted clay and walnut tones.
MCP
Code
:root {
--bg: #f6f2ec;
--paper: #fbf9f5;
--ink: #2c2620;
--ink-2: #5a5147;
--muted: #8a8175;
--clay: #b08968;
--clay-d: #8c6a4f;
--walnut: #5c4433;
--sage: #9caf88;
--line: rgba(44, 38, 32, 0.12);
--white: #fff;
--r-sm: 4px;
--r-md: 8px;
--r-lg: 14px;
--shadow: 0 1px 2px rgba(44, 38, 32, 0.04), 0 18px 40px -24px rgba(44, 38, 32, 0.35);
--shadow-lift: 0 2px 4px rgba(44, 38, 32, 0.06), 0 30px 60px -30px rgba(92, 68, 51, 0.45);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, .serif {
font-family: "Cormorant Garamond", Georgia, serif;
font-weight: 500;
letter-spacing: 0.2px;
}
a { color: inherit; }
::selection { background: rgba(176, 137, 104, 0.28); }
:focus-visible {
outline: 2px solid var(--clay-d);
outline-offset: 2px;
border-radius: var(--r-sm);
}
.page {
max-width: 1080px;
margin: 0 auto;
padding: 0 24px 72px;
}
/* Masthead */
.masthead {
display: flex;
align-items: center;
justify-content: space-between;
padding: 26px 0 22px;
border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
display: grid;
place-items: center;
width: 38px;
height: 38px;
border-radius: var(--r-sm);
background: var(--walnut);
color: var(--paper);
font-family: "Cormorant Garamond", serif;
font-size: 15px;
letter-spacing: 0.5px;
}
.brand-name {
font-family: "Cormorant Garamond", serif;
font-size: 22px;
letter-spacing: 0.4px;
}
.mast-nav { display: flex; gap: 26px; }
.mast-nav a {
text-decoration: none;
color: var(--ink-2);
font-size: 14px;
font-weight: 500;
padding-bottom: 2px;
border-bottom: 1px solid transparent;
transition: color 0.18s, border-color 0.18s;
}
.mast-nav a:hover { color: var(--ink); border-color: var(--clay); }
/* Hero */
.hero { padding: 54px 0 40px; max-width: 720px; }
.eyebrow {
margin: 0 0 14px;
text-transform: uppercase;
letter-spacing: 2.4px;
font-size: 11px;
font-weight: 600;
color: var(--clay-d);
}
.hero h1 {
margin: 0 0 18px;
font-size: clamp(2.4rem, 6vw, 3.6rem);
line-height: 1.04;
color: var(--ink);
}
.lede {
margin: 0 0 30px;
font-size: 17px;
color: var(--ink-2);
max-width: 60ch;
}
/* Controls */
.controls {
display: flex;
flex-wrap: wrap;
gap: 28px 44px;
align-items: flex-end;
padding: 22px 24px;
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow);
}
.control-label {
display: block;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.4px;
color: var(--muted);
text-transform: uppercase;
margin-bottom: 10px;
}
.segmented {
display: inline-flex;
padding: 4px;
background: var(--bg);
border: 1px solid var(--line);
border-radius: 999px;
}
.seg {
border: 0;
background: transparent;
padding: 8px 18px;
border-radius: 999px;
font: inherit;
font-size: 14px;
font-weight: 600;
color: var(--ink-2);
cursor: pointer;
transition: background 0.2s, color 0.2s, box-shadow 0.2s;
}
.seg.is-active {
background: var(--white);
color: var(--walnut);
box-shadow: 0 1px 2px rgba(44, 38, 32, 0.12);
}
.save-note {
display: inline-block;
margin-left: 12px;
font-size: 12.5px;
color: var(--sage);
font-weight: 600;
vertical-align: middle;
}
.stepper-row { display: inline-flex; align-items: center; gap: 6px; }
.step-btn {
width: 40px;
height: 40px;
border-radius: var(--r-sm);
border: 1px solid var(--line);
background: var(--white);
color: var(--walnut);
font-size: 20px;
line-height: 1;
cursor: pointer;
transition: background 0.16s, border-color 0.16s, transform 0.08s;
}
.step-btn:hover { border-color: var(--clay); background: var(--paper); }
.step-btn:active { transform: scale(0.94); }
.step-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.step-value {
min-width: 42px;
text-align: center;
font-family: "Cormorant Garamond", serif;
font-size: 26px;
color: var(--ink);
}
/* Packages */
.packages {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 22px;
padding: 48px 0 12px;
align-items: stretch;
}
.pkg {
position: relative;
display: flex;
flex-direction: column;
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 30px 26px 28px;
box-shadow: var(--shadow);
transition: transform 0.25s cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 0.25s, border-color 0.25s;
}
.pkg::after {
content: "";
position: absolute;
left: 26px;
right: 26px;
bottom: 0;
height: 2px;
background: var(--clay);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.4s ease;
}
.pkg:hover { transform: translateY(-6px); box-shadow: var(--shadow-lift); }
.pkg:hover::after { transform: scaleX(1); }
.pkg.is-featured {
border-color: var(--clay);
box-shadow: 0 2px 4px rgba(44, 38, 32, 0.06), 0 28px 60px -28px rgba(176, 137, 104, 0.6);
background: linear-gradient(180deg, #fff 0%, var(--paper) 100%);
}
.ribbon {
position: absolute;
top: 18px;
right: -1px;
background: var(--walnut);
color: var(--paper);
font-size: 10.5px;
font-weight: 700;
letter-spacing: 1px;
text-transform: uppercase;
padding: 6px 14px;
border-radius: var(--r-sm) 0 0 var(--r-sm);
}
.pkg-name {
margin: 0;
font-size: 30px;
color: var(--walnut);
}
.pkg-tag {
margin: 4px 0 20px;
font-size: 13.5px;
color: var(--muted);
}
.price-block { display: flex; align-items: flex-end; gap: 6px; margin-bottom: 4px; }
.price {
font-family: "Cormorant Garamond", serif;
font-size: 44px;
line-height: 1;
color: var(--ink);
transition: color 0.2s;
}
.price-per { font-size: 13px; color: var(--muted); padding-bottom: 6px; }
.price-sub {
font-size: 12.5px;
color: var(--clay-d);
margin: 0 0 22px;
min-height: 18px;
}
.meta-strip {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.badge {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12px;
font-weight: 600;
color: var(--ink-2);
background: var(--bg);
border: 1px solid var(--line);
padding: 5px 10px;
border-radius: 999px;
}
.badge b { color: var(--walnut); font-weight: 700; }
.deliverables {
list-style: none;
margin: 0 0 26px;
padding: 20px 0 0;
border-top: 1px solid var(--line);
display: grid;
gap: 11px;
flex: 1;
}
.deliverables li {
display: flex;
gap: 10px;
font-size: 14px;
color: var(--ink-2);
align-items: flex-start;
}
.tick {
flex: none;
width: 16px;
height: 16px;
margin-top: 2px;
border-radius: 50%;
display: grid;
place-items: center;
background: rgba(156, 175, 136, 0.28);
color: var(--walnut);
font-size: 10px;
}
.btn {
font: inherit;
font-weight: 600;
font-size: 15px;
cursor: pointer;
border-radius: var(--r-md);
border: 1px solid transparent;
padding: 13px 18px;
transition: background 0.18s, color 0.18s, border-color 0.18s, transform 0.08s;
}
.btn:active { transform: translateY(1px); }
.btn-block { width: 100%; }
.btn-primary { background: var(--walnut); color: var(--paper); }
.btn-primary:hover { background: #4a3527; }
.btn-outline {
background: transparent;
color: var(--walnut);
border-color: var(--clay);
}
.btn-outline:hover { background: var(--clay); color: var(--paper); border-color: var(--clay); }
/* Comparison */
.compare, .faq { padding-top: 72px; }
.section-title {
margin: 0 0 8px;
font-size: clamp(1.9rem, 4vw, 2.5rem);
color: var(--ink);
}
.section-sub { margin: 0 0 30px; color: var(--ink-2); font-size: 15.5px; }
.table-scroll {
overflow-x: auto;
border: 1px solid var(--line);
border-radius: var(--r-lg);
background: var(--paper);
box-shadow: var(--shadow);
}
.compare-table {
width: 100%;
border-collapse: collapse;
min-width: 560px;
}
.compare-table th,
.compare-table td {
padding: 15px 18px;
text-align: center;
font-size: 14px;
border-bottom: 1px solid var(--line);
}
.compare-table thead th {
font-family: "Cormorant Garamond", serif;
font-size: 20px;
color: var(--walnut);
font-weight: 600;
border-bottom: 1px solid var(--line);
}
.compare-table .feat-col { text-align: left; width: 40%; }
.compare-table tbody td:first-child {
text-align: left;
color: var(--ink-2);
font-weight: 500;
}
.compare-table .col-flag,
.compare-table td.col-flag {
background: rgba(176, 137, 104, 0.09);
}
.compare-table tbody tr:last-child td { border-bottom: 0; }
.mark-yes { color: var(--sage); font-size: 16px; font-weight: 700; }
.mark-no { color: var(--muted); opacity: 0.55; }
.mark-text { color: var(--walnut); font-weight: 600; }
/* FAQ */
.faq-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.faq-item {
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 4px 20px;
transition: border-color 0.2s;
}
.faq-item[open] { border-color: var(--clay); }
.faq-item summary {
cursor: pointer;
list-style: none;
padding: 16px 0;
font-weight: 600;
color: var(--ink);
font-size: 15.5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
content: "+";
color: var(--clay-d);
font-size: 20px;
transition: transform 0.2s;
}
.faq-item[open] summary::after { content: "–"; }
.faq-item p {
margin: 0 0 18px;
color: var(--ink-2);
font-size: 14.5px;
}
/* Footer */
.page-foot {
margin-top: 72px;
padding-top: 26px;
border-top: 1px solid var(--line);
display: flex;
gap: 10px;
flex-wrap: wrap;
color: var(--muted);
font-size: 13.5px;
}
/* Booking panel */
.scrim {
position: fixed;
inset: 0;
background: rgba(44, 38, 32, 0.42);
opacity: 0;
transition: opacity 0.3s;
z-index: 40;
}
.scrim.is-open { opacity: 1; }
.booking {
position: fixed;
top: 0;
right: 0;
height: 100%;
width: min(420px, 100%);
background: var(--paper);
border-left: 1px solid var(--line);
box-shadow: -30px 0 60px -30px rgba(44, 38, 32, 0.5);
transform: translateX(102%);
transition: transform 0.34s cubic-bezier(0.2, 0.7, 0.3, 1);
z-index: 50;
display: flex;
flex-direction: column;
overflow-y: auto;
padding: 26px 26px 34px;
}
.booking.is-open { transform: translateX(0); }
.booking-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 22px;
}
.booking-kicker {
margin: 0 0 2px;
text-transform: uppercase;
letter-spacing: 1.6px;
font-size: 10.5px;
font-weight: 600;
color: var(--clay-d);
}
.booking-head h3 { margin: 0; font-size: 30px; color: var(--walnut); }
.icon-btn {
border: 1px solid var(--line);
background: var(--white);
width: 34px;
height: 34px;
border-radius: var(--r-sm);
cursor: pointer;
color: var(--ink-2);
font-size: 14px;
transition: background 0.16s, border-color 0.16s;
}
.icon-btn:hover { background: var(--bg); border-color: var(--clay); }
.booking-summary {
background: var(--bg);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 18px;
margin-bottom: 24px;
}
.sum-row {
display: flex;
justify-content: space-between;
padding: 7px 0;
font-size: 14px;
color: var(--ink-2);
}
.sum-row strong { color: var(--ink); }
.sum-total {
border-top: 1px solid var(--line);
margin-top: 4px;
padding-top: 12px;
font-size: 15px;
}
.sum-total strong {
font-family: "Cormorant Garamond", serif;
font-size: 24px;
color: var(--walnut);
}
.booking-form { display: grid; gap: 16px; }
.field { display: grid; gap: 7px; }
.field > span { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.field input,
.field select,
.field textarea {
font: inherit;
font-size: 14.5px;
padding: 11px 13px;
border: 1px solid var(--line);
border-radius: var(--r-md);
background: var(--white);
color: var(--ink);
resize: vertical;
transition: border-color 0.16s, box-shadow 0.16s;
}
.field input:focus,
.field select:focus,
.field textarea:focus {
outline: none;
border-color: var(--clay);
box-shadow: 0 0 0 3px rgba(176, 137, 104, 0.18);
}
.field input:invalid:not(:placeholder-shown) {
border-color: #b4553f;
}
.booking-note {
margin: 2px 0 0;
font-size: 12.5px;
color: var(--muted);
text-align: center;
}
/* Toast */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 24px);
background: var(--walnut);
color: var(--paper);
padding: 13px 20px;
border-radius: 999px;
font-size: 14px;
font-weight: 500;
box-shadow: var(--shadow-lift);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s, transform 0.25s;
z-index: 60;
max-width: calc(100% - 32px);
}
.toast.is-show { opacity: 1; transform: translate(-50%, 0); }
/* Responsive */
@media (max-width: 900px) {
.packages { grid-template-columns: 1fr; max-width: 460px; margin: 0 auto; }
.pkg:hover { transform: translateY(-3px); }
}
@media (max-width: 640px) {
.faq-grid { grid-template-columns: 1fr; }
.mast-nav { display: none; }
}
@media (max-width: 520px) {
.page { padding: 0 16px 56px; }
.hero { padding: 38px 0 30px; }
.controls { flex-direction: column; align-items: stretch; gap: 22px; padding: 18px; }
.segmented { width: 100%; }
.seg { flex: 1; text-align: center; }
.save-note { display: block; margin: 8px 0 0; }
.stepper-row { justify-content: space-between; }
.step-value { flex: 1; }
.booking { width: 100%; }
.price { font-size: 38px; }
.pkg-name { font-size: 26px; }
}
@media (prefers-reduced-motion: reduce) {
* { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}(function () {
"use strict";
// --- Data model ---------------------------------------------------------
// basePerRoom = price per room; base = flat studio fee added once.
var PACKAGES = [
{
id: "refresh",
name: "Refresh",
tag: "One room, styled and reworked",
base: 900,
perRoom: 1400,
revisions: 2,
timeline: "3–4 weeks",
featured: false,
deliverables: [
"Concept moodboard & palette",
"2D furniture layout plan",
"Curated shopping list with links",
"One styling call (45 min)"
]
},
{
id: "signature",
name: "Signature",
tag: "Full room design, managed end to end",
base: 1800,
perRoom: 2600,
revisions: 3,
timeline: "6–8 weeks",
featured: true,
deliverables: [
"Everything in Refresh",
"3D renders of each room",
"Custom joinery & lighting spec",
"Trade pricing + procurement",
"Delivery & install coordination"
]
},
{
id: "full-home",
name: "Full Home",
tag: "Whole-home direction, cohesive throughout",
base: 3200,
perRoom: 3400,
revisions: 5,
timeline: "10–14 weeks",
featured: false,
deliverables: [
"Everything in Signature",
"Whole-home style framework",
"Bespoke furniture design",
"On-site project management",
"Final styling & photo-ready reveal"
]
}
];
var COMPARE_ROWS = [
{ label: "Moodboard & palette", vals: ["yes", "yes", "yes"] },
{ label: "2D layout plan", vals: ["yes", "yes", "yes"] },
{ label: "Shopping list", vals: ["yes", "yes", "yes"] },
{ label: "3D renders", vals: ["no", "yes", "yes"] },
{ label: "Joinery & lighting spec", vals: ["no", "yes", "yes"] },
{ label: "Procurement & install", vals: ["no", "yes", "yes"] },
{ label: "On-site project management", vals: ["no", "no", "yes"] },
{ label: "Bespoke furniture design", vals: ["no", "no", "yes"] },
{ label: "Revision rounds", vals: ["2", "3", "5"] },
{ label: "Typical timeline", vals: ["3–4 wks", "6–8 wks", "10–14 wks"] }
];
var state = { rooms: 3, billing: "once" };
// --- Helpers ------------------------------------------------------------
function money(n) {
return "$" + Math.round(n).toLocaleString("en-US");
}
function priceFor(pkg) {
return pkg.base + pkg.perRoom * state.rooms;
}
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 2600);
}
// --- Render packages ----------------------------------------------------
var packagesEl = document.getElementById("packages");
function renderPackages() {
packagesEl.innerHTML = "";
PACKAGES.forEach(function (pkg) {
var total = priceFor(pkg);
var monthly = total / 4;
var showMonthly = state.billing === "monthly";
var card = document.createElement("article");
card.className = "pkg" + (pkg.featured ? " is-featured" : "");
card.dataset.id = pkg.id;
var deliverables = pkg.deliverables
.map(function (d) {
return (
'<li><span class="tick" aria-hidden="true">✓</span>' + d + "</li>"
);
})
.join("");
var priceMain = showMonthly ? money(monthly) : money(total);
var priceUnit = showMonthly ? "/mo × 4" : "one-time";
var priceSub = showMonthly
? "Total " + money(total) + " · " + state.rooms + " room" + (state.rooms > 1 ? "s" : "")
: "For " + state.rooms + " room" + (state.rooms > 1 ? "s" : "") + " · incl. studio fee";
card.innerHTML =
(pkg.featured ? '<span class="ribbon">Most chosen</span>' : "") +
'<h3 class="pkg-name">' + pkg.name + "</h3>" +
'<p class="pkg-tag">' + pkg.tag + "</p>" +
'<div class="price-block">' +
'<span class="price">' + priceMain + "</span>" +
'<span class="price-per">' + priceUnit + "</span>" +
"</div>" +
'<p class="price-sub">' + priceSub + "</p>" +
'<div class="meta-strip">' +
'<span class="badge"><b>' + state.rooms + "</b> rooms</span>" +
'<span class="badge"><b>' + pkg.revisions + "</b> revisions</span>" +
'<span class="badge">' + pkg.timeline + "</span>" +
"</div>" +
'<ul class="deliverables">' + deliverables + "</ul>" +
'<button class="btn ' +
(pkg.featured ? "btn-primary" : "btn-outline") +
' btn-block book-btn" data-id="' +
pkg.id +
'">Book this package</button>';
packagesEl.appendChild(card);
});
}
// --- Render comparison --------------------------------------------------
function renderCompare() {
var body = document.getElementById("compare-body");
body.innerHTML = COMPARE_ROWS.map(function (row) {
var cells = row.vals
.map(function (v, i) {
var cls = i === 1 ? " class=\"col-flag\"" : "";
var content;
if (v === "yes") content = '<span class="mark-yes" aria-label="Included">✓</span>';
else if (v === "no") content = '<span class="mark-no" aria-label="Not included">—</span>';
else content = '<span class="mark-text">' + v + "</span>";
return "<td" + cls + ">" + content + "</td>";
})
.join("");
return "<tr><td>" + row.label + "</td>" + cells + "</tr>";
}).join("");
}
// --- Controls -----------------------------------------------------------
var roomsValue = document.getElementById("rooms-value");
var minusBtn = document.getElementById("rooms-minus");
var plusBtn = document.getElementById("rooms-plus");
var MIN_ROOMS = 1;
var MAX_ROOMS = 8;
function updateStepperButtons() {
minusBtn.disabled = state.rooms <= MIN_ROOMS;
plusBtn.disabled = state.rooms >= MAX_ROOMS;
roomsValue.textContent = state.rooms;
}
function changeRooms(delta) {
var next = Math.min(MAX_ROOMS, Math.max(MIN_ROOMS, state.rooms + delta));
if (next === state.rooms) return;
state.rooms = next;
updateStepperButtons();
renderPackages();
syncBookingSummary();
}
minusBtn.addEventListener("click", function () { changeRooms(-1); });
plusBtn.addEventListener("click", function () { changeRooms(1); });
var segOnce = document.getElementById("seg-once");
var segMonthly = document.getElementById("seg-monthly");
var saveNote = document.getElementById("save-note");
function setBilling(mode) {
state.billing = mode;
var isMonthly = mode === "monthly";
segMonthly.classList.toggle("is-active", isMonthly);
segOnce.classList.toggle("is-active", !isMonthly);
segMonthly.setAttribute("aria-selected", String(isMonthly));
segOnce.setAttribute("aria-selected", String(!isMonthly));
saveNote.hidden = !isMonthly;
renderPackages();
syncBookingSummary();
}
segOnce.addEventListener("click", function () { setBilling("once"); });
segMonthly.addEventListener("click", function () { setBilling("monthly"); });
// Delegated book buttons
packagesEl.addEventListener("click", function (e) {
var btn = e.target.closest(".book-btn");
if (!btn) return;
openBooking(btn.dataset.id);
});
// --- Booking panel ------------------------------------------------------
var booking = document.getElementById("booking");
var scrim = document.getElementById("scrim");
var closeBtn = document.getElementById("booking-close");
var form = document.getElementById("booking-form");
var currentPkgId = null;
var lastFocused = null;
function syncBookingSummary() {
if (!currentPkgId) return;
var pkg = PACKAGES.find(function (p) { return p.id === currentPkgId; });
if (!pkg) return;
document.getElementById("booking-title").textContent = pkg.name;
document.getElementById("sum-package").textContent = pkg.name;
document.getElementById("sum-rooms").textContent = state.rooms;
document.getElementById("sum-timeline").textContent = pkg.timeline;
var total = priceFor(pkg);
var label =
state.billing === "monthly"
? money(total / 4) + "/mo × 4"
: money(total);
document.getElementById("sum-total").textContent = label;
}
function openBooking(id) {
currentPkgId = id;
lastFocused = document.activeElement;
syncBookingSummary();
scrim.hidden = false;
// force reflow so transition runs
void scrim.offsetWidth;
scrim.classList.add("is-open");
booking.classList.add("is-open");
booking.setAttribute("aria-hidden", "false");
document.body.style.overflow = "hidden";
setTimeout(function () { document.getElementById("f-name").focus(); }, 320);
document.addEventListener("keydown", onKeydown);
}
function closeBooking() {
booking.classList.remove("is-open");
scrim.classList.remove("is-open");
booking.setAttribute("aria-hidden", "true");
document.body.style.overflow = "";
document.removeEventListener("keydown", onKeydown);
setTimeout(function () { scrim.hidden = true; }, 320);
if (lastFocused && lastFocused.focus) lastFocused.focus();
}
function onKeydown(e) {
if (e.key === "Escape") { closeBooking(); return; }
if (e.key === "Tab") trapFocus(e);
}
function trapFocus(e) {
var focusable = booking.querySelectorAll(
'a[href], button:not([disabled]), input, select, textarea'
);
if (!focusable.length) return;
var first = focusable[0];
var last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
closeBtn.addEventListener("click", closeBooking);
scrim.addEventListener("click", closeBooking);
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = document.getElementById("f-name");
var email = document.getElementById("f-email");
if (!name.value.trim()) { name.focus(); toast("Please add your name"); return; }
if (!email.checkValidity()) { email.focus(); toast("Please add a valid email"); return; }
var pkg = PACKAGES.find(function (p) { return p.id === currentPkgId; });
closeBooking();
form.reset();
toast("Request sent — we'll email a " + (pkg ? pkg.name : "") + " proposal within 2 days");
});
// --- Init ---------------------------------------------------------------
updateStepperButtons();
renderPackages();
renderCompare();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Marlowe & Reed — Design Packages</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=Cormorant+Garamond:wght@400;500;600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page">
<header class="masthead">
<div class="brand">
<span class="brand-mark" aria-hidden="true">M&R</span>
<span class="brand-name">Marlowe & Reed</span>
</div>
<nav class="mast-nav" aria-label="Primary">
<a href="#packages">Packages</a>
<a href="#compare">Compare</a>
<a href="#faq">FAQ</a>
</nav>
</header>
<section class="hero">
<p class="eyebrow">Interior Design Studio · Est. 2014</p>
<h1>Design packages made for how you actually live</h1>
<p class="lede">Three considered tiers, transparent deliverables, and a fixed timeline. Choose the scope, we handle the rest — from a single-room refresh to a whole home.</p>
<div class="controls" role="group" aria-label="Pricing controls">
<div class="billing" role="group" aria-label="Billing period">
<span class="control-label" id="billing-label">Billing</span>
<div class="segmented" role="tablist" aria-labelledby="billing-label">
<button class="seg is-active" id="seg-once" role="tab" aria-selected="true" data-billing="once">One-time</button>
<button class="seg" id="seg-monthly" role="tab" aria-selected="false" data-billing="monthly">Monthly ×4</button>
</div>
<span class="save-note" id="save-note" hidden>Spread over 4 payments</span>
</div>
<div class="stepper" role="group" aria-label="Number of rooms">
<span class="control-label" id="rooms-label">Rooms to design</span>
<div class="stepper-row">
<button class="step-btn" id="rooms-minus" aria-label="Fewer rooms">–</button>
<output class="step-value" id="rooms-value" aria-live="polite" for="rooms-minus rooms-plus">3</output>
<button class="step-btn" id="rooms-plus" aria-label="More rooms">+</button>
</div>
</div>
</div>
</section>
<main id="packages" class="packages" aria-label="Design packages"></main>
<section id="compare" class="compare">
<h2 class="section-title">Compare every deliverable</h2>
<p class="section-sub">Nothing hidden in fine print — here is exactly what each tier includes.</p>
<div class="table-scroll">
<table class="compare-table">
<thead>
<tr>
<th scope="col" class="feat-col">Deliverable</th>
<th scope="col">Refresh</th>
<th scope="col" class="col-flag">Signature</th>
<th scope="col">Full Home</th>
</tr>
</thead>
<tbody id="compare-body"></tbody>
</table>
</div>
</section>
<section id="faq" class="faq">
<h2 class="section-title">Good to know</h2>
<div class="faq-grid">
<details class="faq-item" open>
<summary>How do revisions work?</summary>
<p>Each package includes a set number of revision rounds on the concept and layout. Extra rounds are billed at a flat $180 each, agreed before we begin.</p>
</details>
<details class="faq-item">
<summary>Do you supply the furniture?</summary>
<p>We provide a full shopping list with trade pricing where available. Procurement and installation are included from the Signature tier upward.</p>
</details>
<details class="faq-item">
<summary>What if my project grows?</summary>
<p>Add rooms with the stepper above and the estimate updates instantly. You can also upgrade tiers mid-project — we credit what you've already paid.</p>
</details>
<details class="faq-item">
<summary>Is the timeline guaranteed?</summary>
<p>Timelines start once we receive your brief and deposit. Delivery windows are per-room and shown on each card; we build in a two-week buffer for trade lead times.</p>
</details>
</div>
</section>
<footer class="page-foot">
<span>© 2026 Marlowe & Reed Interiors</span>
<span class="foot-dot" aria-hidden="true">·</span>
<span>[email protected]</span>
</footer>
</div>
<!-- Booking panel -->
<div class="scrim" id="scrim" hidden></div>
<aside class="booking" id="booking" aria-hidden="true" aria-labelledby="booking-title" role="dialog" aria-modal="true">
<div class="booking-head">
<div>
<p class="booking-kicker">Book a package</p>
<h3 id="booking-title">Signature</h3>
</div>
<button class="icon-btn" id="booking-close" aria-label="Close booking panel">✕</button>
</div>
<div class="booking-summary" id="booking-summary">
<div class="sum-row"><span>Package</span><strong id="sum-package">Signature</strong></div>
<div class="sum-row"><span>Rooms</span><strong id="sum-rooms">3</strong></div>
<div class="sum-row"><span>Timeline</span><strong id="sum-timeline">6–8 weeks</strong></div>
<div class="sum-row sum-total"><span>Estimate</span><strong id="sum-total">$9,600</strong></div>
</div>
<form class="booking-form" id="booking-form" novalidate>
<label class="field">
<span>Full name</span>
<input type="text" name="name" id="f-name" autocomplete="name" required placeholder="Ava Lindqvist" />
</label>
<label class="field">
<span>Email</span>
<input type="email" name="email" id="f-email" autocomplete="email" required placeholder="[email protected]" />
</label>
<label class="field">
<span>Preferred start</span>
<select name="start" id="f-start">
<option>As soon as possible</option>
<option>Within a month</option>
<option>In 2–3 months</option>
<option>Just exploring</option>
</select>
</label>
<label class="field">
<span>Tell us about the space</span>
<textarea name="notes" id="f-notes" rows="3" placeholder="A tired living room and a home office that needs to double as a guest room…"></textarea>
</label>
<button type="submit" class="btn btn-primary btn-block">Request this package</button>
<p class="booking-note">No payment now — we'll confirm scope and send a proposal within two working days.</p>
</form>
</aside>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A calm, print-like pricing page for a fictional interior studio, Marlowe & Reed. Three package cards — Refresh, Signature, and Full Home — each state a room count, the number of included revision rounds, an itemised deliverables list, and a delivery timeline. A billing toggle switches every card between a one-time fee and a spread monthly plan, while a room-count stepper scales the estimate so the pricing responds to the client’s actual scope rather than sitting static.
The flagship Signature tier wears a Most chosen ribbon and a soft clay glow. Below the cards, a full comparison table lays out every deliverable row by row with check and dash marks, so nothing is hidden. Each Book this package button opens a slide-in booking panel pre-filled with the chosen tier and live estimate; submitting fires a confirmation toast and resets the form.
The layout is built entirely with vanilla HTML, CSS, and JavaScript — no frameworks or build step. It stays legible down to 360px, keeps focus rings visible for keyboard users, and pairs Cormorant Garamond headings with Inter body text over a warm natural palette of clay, walnut, and sage for an unhurried, editorial feel.