Interior — Services
A warm editorial services page for an interior studio: four offer cards (full design, e-design, consultation, styling) each with a deliverables list, a from-price, a duration badge, and an enquire action that opens a filterable request panel. Includes a scope toggle that recalculates estimated pricing per room, a comparison strip, and toast-confirmed enquiries — all vanilla JS with muted natural tones, thin rules, and generous whitespace.
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;
--serif: "Cormorant Garamond", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a { color: inherit; }
::selection { background: var(--clay); color: var(--white); }
:focus-visible {
outline: 2px solid var(--clay-d);
outline-offset: 3px;
border-radius: var(--r-sm);
}
.wrap {
max-width: 1080px;
margin: 0 auto;
padding: 0 24px;
}
/* ---- Masthead ---- */
.masthead {
display: flex;
align-items: center;
justify-content: space-between;
padding: 26px 0;
border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
display: grid;
place-items: center;
width: 40px; height: 40px;
background: var(--walnut);
color: var(--paper);
font-family: var(--serif);
font-weight: 600;
font-size: 16px;
letter-spacing: .5px;
border-radius: var(--r-sm);
}
.brand-name {
font-family: var(--serif);
font-size: 22px;
font-weight: 600;
letter-spacing: .3px;
}
.topnav { display: flex; align-items: center; gap: 26px; }
.topnav a {
text-decoration: none;
font-size: 14px;
font-weight: 500;
color: var(--ink-2);
transition: color .18s;
}
.topnav a:hover { color: var(--ink); }
.topnav-cta {
padding: 9px 16px;
border: 1px solid var(--ink);
border-radius: 999px;
color: var(--ink) !important;
}
.topnav-cta:hover { background: var(--ink); color: var(--paper) !important; }
/* ---- Intro ---- */
.intro { padding: 64px 0 40px; max-width: 760px; }
.eyebrow {
margin: 0 0 20px;
font-size: 12px;
letter-spacing: 2.5px;
text-transform: uppercase;
color: var(--clay-d);
font-weight: 600;
}
.intro h1 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(38px, 6vw, 64px);
line-height: 1.04;
margin: 0 0 22px;
letter-spacing: -.5px;
}
.lede {
font-size: 18px;
color: var(--ink-2);
max-width: 560px;
margin: 0 0 34px;
}
.scope {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 14px 18px;
padding: 16px 20px;
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
}
.scope-label {
font-size: 13px;
font-weight: 600;
letter-spacing: .3px;
color: var(--ink-2);
text-transform: uppercase;
}
.stepper {
display: inline-flex;
align-items: center;
gap: 4px;
background: var(--white);
border: 1px solid var(--line);
border-radius: 999px;
padding: 4px;
}
.step {
width: 30px; height: 30px;
border: none;
background: transparent;
border-radius: 999px;
font-size: 18px;
line-height: 1;
color: var(--walnut);
cursor: pointer;
transition: background .16s, transform .12s;
}
.step:hover { background: var(--bg); }
.step:active { transform: scale(.9); }
.step-val {
min-width: 78px;
text-align: center;
font-size: 14px;
font-weight: 600;
}
.step-val output { font-variant-numeric: tabular-nums; }
.scope-hint { font-size: 13px; color: var(--muted); }
/* ---- Grid ---- */
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
padding: 8px 0 20px;
}
.card {
position: relative;
display: flex;
flex-direction: column;
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 28px 26px 24px;
overflow: hidden;
transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
.card::after {
content: "";
position: absolute;
left: 0; bottom: 0;
height: 3px; width: 100%;
background: var(--clay);
transform: scaleX(0);
transform-origin: left;
transition: transform .35s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 18px 40px -22px rgba(92, 68, 51, 0.45);
border-color: rgba(176, 137, 104, 0.5);
}
.card:hover::after { transform: scaleX(1); }
.card.is-featured { border-color: rgba(176, 137, 104, 0.55); }
.card-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
}
.card-swatch {
width: 44px; height: 44px;
border-radius: var(--r-md);
flex: 0 0 auto;
box-shadow: inset 0 0 0 1px rgba(44,38,32,.08);
}
.badge {
font-size: 11px;
font-weight: 700;
letter-spacing: .6px;
text-transform: uppercase;
padding: 5px 10px;
border-radius: 999px;
white-space: nowrap;
}
.badge-featured { background: var(--walnut); color: var(--paper); }
.badge-dur {
background: rgba(156, 175, 136, 0.22);
color: #566146;
}
.card h2 {
font-family: var(--serif);
font-weight: 600;
font-size: 27px;
margin: 0 0 6px;
letter-spacing: .2px;
}
.card-brief {
font-size: 14.5px;
color: var(--ink-2);
margin: 0 0 18px;
}
.deliverables {
list-style: none;
margin: 0 0 20px;
padding: 18px 0 0;
border-top: 1px solid var(--line);
display: grid;
gap: 9px;
}
.deliverables li {
position: relative;
padding-left: 22px;
font-size: 13.5px;
color: var(--ink-2);
}
.deliverables li::before {
content: "";
position: absolute;
left: 0; top: 8px;
width: 8px; height: 8px;
border-radius: 999px;
background: var(--clay);
}
.card-foot {
margin-top: auto;
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 14px;
padding-top: 16px;
border-top: 1px solid var(--line);
}
.price-label {
display: block;
font-size: 11px;
letter-spacing: 1px;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 2px;
}
.price {
font-family: var(--serif);
font-size: 32px;
font-weight: 600;
color: var(--walnut);
line-height: 1;
}
.price small { font-size: 15px; color: var(--muted); font-family: var(--sans); font-weight: 500; }
.btn {
font-family: var(--sans);
font-size: 14px;
font-weight: 600;
border-radius: 999px;
cursor: pointer;
border: 1px solid var(--ink);
background: transparent;
color: var(--ink);
padding: 10px 20px;
transition: background .18s, color .18s, transform .12s;
}
.btn:hover { background: var(--ink); color: var(--paper); }
.btn:active { transform: scale(.97); }
.btn-solid { background: var(--walnut); border-color: var(--walnut); color: var(--paper); }
.btn-solid:hover { background: var(--clay-d); border-color: var(--clay-d); }
.btn-block { width: 100%; padding: 13px; }
/* ---- Process ---- */
.process {
padding: 40px 0 12px;
border-top: 1px solid var(--line);
margin-top: 20px;
}
.process h2 {
font-family: var(--serif);
font-weight: 500;
font-size: 34px;
margin: 0 0 26px;
}
.steps {
list-style: none;
margin: 0; padding: 0;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.steps li { padding-top: 8px; border-top: 2px solid var(--clay); }
.steps-n {
font-family: var(--serif);
font-size: 15px;
color: var(--clay-d);
font-weight: 600;
}
.steps h3 { font-size: 16px; margin: 6px 0 6px; }
.steps p { font-size: 13.5px; color: var(--ink-2); margin: 0; }
/* ---- Footer ---- */
.foot {
margin-top: 48px;
padding: 26px 0 40px;
border-top: 1px solid var(--line);
font-size: 13.5px;
color: var(--ink-2);
}
.foot a { color: var(--clay-d); text-decoration: none; }
.foot a:hover { text-decoration: underline; }
.foot-fine { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
/* ---- Enquiry panel ---- */
.panel-backdrop {
position: fixed; inset: 0;
background: rgba(44, 38, 32, 0.42);
backdrop-filter: blur(2px);
z-index: 40;
opacity: 0;
transition: opacity .28s;
}
.panel-backdrop.show { opacity: 1; }
.panel {
position: fixed;
top: 0; right: 0;
height: 100%;
width: min(420px, 92vw);
background: var(--paper);
z-index: 50;
box-shadow: -20px 0 60px -30px rgba(44,38,32,.6);
transform: translateX(100%);
transition: transform .34s cubic-bezier(.4,0,.2,1);
display: flex;
flex-direction: column;
padding: 26px 28px 32px;
overflow-y: auto;
}
.panel.open { transform: translateX(0); }
.panel-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 22px;
}
.panel-eyebrow {
margin: 0 0 4px;
font-size: 11px;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--clay-d);
font-weight: 600;
}
.panel-head h2 { font-family: var(--serif); font-weight: 600; font-size: 30px; margin: 0; }
.panel-close {
border: 1px solid var(--line);
background: var(--white);
width: 38px; height: 38px;
border-radius: 999px;
font-size: 22px;
line-height: 1;
color: var(--ink-2);
cursor: pointer;
transition: background .16s, color .16s;
}
.panel-close:hover { background: var(--walnut); color: var(--paper); }
.panel-form { display: grid; gap: 15px; }
.field { display: grid; gap: 6px; }
.field span {
font-size: 12px;
font-weight: 600;
letter-spacing: .4px;
text-transform: uppercase;
color: var(--ink-2);
}
.field input,
.field select,
.field textarea {
font-family: var(--sans);
font-size: 14.5px;
color: var(--ink);
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 11px 13px;
transition: border-color .16s, box-shadow .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 textarea { resize: vertical; }
.panel-summary {
font-size: 13.5px;
color: var(--ink-2);
background: var(--bg);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 12px 14px;
}
.panel-summary strong { color: var(--walnut); font-family: var(--serif); font-size: 17px; }
.panel-fine { font-size: 12px; color: var(--muted); text-align: center; margin: 4px 0 0; }
/* ---- Toast ---- */
.toast {
position: fixed;
left: 50%; bottom: 28px;
transform: translate(-50%, 24px);
background: var(--walnut);
color: var(--paper);
font-size: 14px;
font-weight: 500;
padding: 13px 22px;
border-radius: 999px;
box-shadow: 0 16px 36px -16px rgba(44,38,32,.7);
opacity: 0;
pointer-events: none;
z-index: 60;
transition: opacity .28s, transform .28s;
max-width: calc(100vw - 32px);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ---- Responsive ---- */
@media (max-width: 820px) {
.grid { grid-template-columns: 1fr; }
.steps { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
.wrap { padding: 0 16px; }
.brand-name { display: none; }
.topnav { gap: 16px; }
.topnav a:not(.topnav-cta) { display: none; }
.intro { padding: 40px 0 30px; }
.lede { font-size: 16px; }
.scope { padding: 14px 16px; }
.scope-hint { width: 100%; }
.steps { grid-template-columns: 1fr; }
.card { padding: 24px 20px 22px; }
.card-foot { flex-direction: column; align-items: stretch; }
.card-foot .btn { width: 100%; }
}(function () {
"use strict";
// --- Service data -------------------------------------------------------
var SERVICES = [
{
id: "full-design",
name: "Full Design",
swatch: "linear-gradient(135deg, #b08968, #8c6a4f)",
duration: "10–14 weeks",
base: 4800, // fixed studio fee
perRoom: 1200, // added per room
featured: true,
brief: "End to end. We plan, source, and install a complete scheme so you move into a finished home.",
deliverables: [
"Space planning & 3D concept",
"Curated furniture & lighting schedule",
"Full sourcing & procurement",
"Trade coordination on site",
"Styling & final reveal day"
]
},
{
id: "e-design",
name: "E-Design",
swatch: "linear-gradient(135deg, #9caf88, #6f8258)",
duration: "3–4 weeks",
base: 950,
perRoom: 480,
featured: false,
brief: "A remote design package delivered as a clickable board — you source and install at your own pace.",
deliverables: [
"Digital mood & concept board",
"Scaled 2D floor layout",
"Shoppable product list with links",
"One round of revisions",
"Styling & placement guide (PDF)"
]
},
{
id: "consultation",
name: "Consultation",
swatch: "linear-gradient(135deg, #c9a888, #b08968)",
duration: "Half day",
base: 420,
perRoom: 0,
featured: false,
brief: "A focused visit to unblock decisions — paint, layout, lighting — with clear notes to act on.",
deliverables: [
"90-minute on-site or video visit",
"Colour & material direction",
"Layout & flow recommendations",
"Written action summary",
"Priority shopping shortlist"
]
},
{
id: "styling",
name: "Styling",
swatch: "linear-gradient(135deg, #5c4433, #3f2e22)",
duration: "1–2 weeks",
base: 1400,
perRoom: 600,
featured: false,
brief: "The finishing layer — art, textiles, and objects arranged so the room finally feels complete.",
deliverables: [
"Accessory & art sourcing",
"Textile & soft-furnishing edit",
"In-person styling session",
"Shelf & surface composition",
"Photo-ready final styling"
]
}
];
var MIN_ROOMS = 1;
var MAX_ROOMS = 12;
var rooms = 3;
var grid = document.querySelector(".grid");
var roomOut = document.getElementById("roomCount");
function money(n) {
return "$" + n.toLocaleString("en-US");
}
function priceFor(svc, r) {
return svc.base + svc.perRoom * Math.max(0, r - 1);
}
// --- Render cards -------------------------------------------------------
function renderCards() {
grid.innerHTML = "";
SERVICES.forEach(function (svc) {
var card = document.createElement("article");
card.className = "card" + (svc.featured ? " is-featured" : "");
card.setAttribute("data-id", svc.id);
var badge = svc.featured
? '<span class="badge badge-featured">Most requested</span>'
: '<span class="badge badge-dur">' + svc.duration + "</span>";
var deliverables = svc.deliverables
.map(function (d) { return "<li>" + d + "</li>"; })
.join("");
var priceWrap = svc.perRoom
? '<span class="price" data-price>' + money(priceFor(svc, rooms)) + "</span>"
: '<span class="price" data-price>' + money(svc.base) + " <small>flat</small></span>";
card.innerHTML =
'<div class="card-top">' +
'<span class="card-swatch" style="background:' + svc.swatch + '"></span>' +
badge +
"</div>" +
"<h2>" + svc.name + "</h2>" +
'<p class="card-brief">' + svc.brief + "</p>" +
'<ul class="deliverables">' + deliverables + "</ul>" +
'<div class="card-foot">' +
"<div>" +
'<span class="price-label">From</span>' +
priceWrap +
"</div>" +
'<button type="button" class="btn" data-enquire="' + svc.name + '">Enquire</button>' +
"</div>";
grid.appendChild(card);
});
}
function updatePrices() {
roomOut.textContent = String(rooms);
SERVICES.forEach(function (svc) {
var card = grid.querySelector('[data-id="' + svc.id + '"]');
if (!card) return;
var el = card.querySelector("[data-price]");
if (!el) return;
if (svc.perRoom) {
el.textContent = money(priceFor(svc, rooms));
el.style.transition = "color .2s";
el.style.color = "var(--clay-d)";
setTimeout(function () { el.style.color = ""; }, 220);
}
});
var fRooms = document.getElementById("fRooms");
if (fRooms) fRooms.value = String(rooms);
if (panel.classList.contains("open")) updateSummary();
}
renderCards();
// --- Stepper ------------------------------------------------------------
document.querySelectorAll(".step").forEach(function (btn) {
btn.addEventListener("click", function () {
var delta = parseInt(btn.getAttribute("data-step"), 10);
rooms = Math.min(MAX_ROOMS, Math.max(MIN_ROOMS, rooms + delta));
updatePrices();
});
});
// --- Enquiry panel ------------------------------------------------------
var panel = document.getElementById("panel");
var backdrop = document.getElementById("backdrop");
var panelClose = document.getElementById("panelClose");
var panelTitle = document.getElementById("panelTitle");
var form = document.getElementById("enquiryForm");
var fService = document.getElementById("fService");
var fRooms = document.getElementById("fRooms");
var summary = document.getElementById("panelSummary");
var lastFocus = null;
// Populate service select
SERVICES.forEach(function (svc) {
var opt = document.createElement("option");
opt.value = svc.id;
opt.textContent = svc.name;
fService.appendChild(opt);
});
var studioOpt = document.createElement("option");
studioOpt.value = "studio";
studioOpt.textContent = "General / not sure yet";
fService.appendChild(studioOpt);
function updateSummary() {
var svc = SERVICES.filter(function (s) { return s.id === fService.value; })[0];
var r = Math.min(MAX_ROOMS, Math.max(MIN_ROOMS, parseInt(fRooms.value, 10) || 1));
if (!svc) {
summary.innerHTML = "We'll help you pick the right service on the call.";
return;
}
var total = priceFor(svc, r);
var line = svc.perRoom
? "Estimated from <strong>" + money(total) + "</strong> for " + r + " room" + (r > 1 ? "s" : "")
: "Estimated from <strong>" + money(svc.base) + "</strong> (flat rate)";
summary.innerHTML = svc.name + " · " + svc.duration + "<br>" + line;
}
function openPanel(serviceName) {
lastFocus = document.activeElement;
var match = SERVICES.filter(function (s) { return s.name === serviceName; })[0];
fService.value = match ? match.id : "studio";
fRooms.value = String(rooms);
panelTitle.textContent = match ? "Enquire · " + match.name : "Enquire";
updateSummary();
backdrop.hidden = false;
requestAnimationFrame(function () {
backdrop.classList.add("show");
panel.classList.add("open");
});
panel.setAttribute("aria-hidden", "false");
document.body.style.overflow = "hidden";
setTimeout(function () {
var firstInput = form.querySelector('input[name="name"]');
if (firstInput) firstInput.focus();
}, 320);
}
function closePanel() {
panel.classList.remove("open");
backdrop.classList.remove("show");
panel.setAttribute("aria-hidden", "true");
document.body.style.overflow = "";
setTimeout(function () { backdrop.hidden = true; }, 300);
if (lastFocus && lastFocus.focus) lastFocus.focus();
}
document.addEventListener("click", function (e) {
var trigger = e.target.closest("[data-enquire]");
if (trigger) {
e.preventDefault();
openPanel(trigger.getAttribute("data-enquire"));
}
});
panelClose.addEventListener("click", closePanel);
backdrop.addEventListener("click", closePanel);
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && panel.classList.contains("open")) closePanel();
});
fService.addEventListener("change", updateSummary);
fRooms.addEventListener("input", updateSummary);
// --- Submit -------------------------------------------------------------
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = form.name.value.trim();
var email = form.email.value.trim();
if (!name) { toast("Please add your name."); form.name.focus(); return; }
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
toast("That email doesn't look right.");
form.email.focus();
return;
}
var svc = SERVICES.filter(function (s) { return s.id === fService.value; })[0];
var label = svc ? svc.name : "your project";
closePanel();
form.reset();
fRooms.value = String(rooms);
toast("Thanks " + name.split(" ")[0] + " — your " + label + " enquiry is in.");
});
// --- Toast --------------------------------------------------------------
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 3200);
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Marlowe & Reed — Services</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=Cormorant+Garamond:wght@400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="wrap">
<header class="masthead">
<div class="brand">
<span class="brand-mark">M&R</span>
<span class="brand-name">Marlowe & Reed</span>
</div>
<nav class="topnav" aria-label="Primary">
<a href="#services">Services</a>
<a href="#process">Process</a>
<a href="#" class="topnav-cta" data-enquire="Studio enquiry">Book a call</a>
</nav>
</header>
<section class="intro">
<p class="eyebrow">Interior Design Studio — est. 2014</p>
<h1>Considered rooms,<br />built around how you live.</h1>
<p class="lede">Four ways to work with the studio — from a single afternoon of advice to a full,
end-to-end design service. Every engagement begins with the same question: what do you want
this space to feel like?</p>
<div class="scope" role="group" aria-label="Estimate scope">
<span class="scope-label">Estimate for</span>
<div class="stepper" role="group" aria-label="Number of rooms">
<button type="button" class="step" data-step="-1" aria-label="Fewer rooms">−</button>
<span class="step-val"><output id="roomCount" aria-live="polite">3</output> rooms</span>
<button type="button" class="step" data-step="1" aria-label="More rooms">+</button>
</div>
<span class="scope-hint">Prices update as you adjust.</span>
</div>
</section>
<main id="services" class="grid" aria-label="Services">
<!-- cards injected by script.js -->
</main>
<section id="process" class="process">
<h2>How it runs</h2>
<ol class="steps">
<li><span class="steps-n">01</span><h3>Enquiry</h3><p>Tell us the rooms, the timeline and the feeling you're after.</p></li>
<li><span class="steps-n">02</span><h3>Concept</h3><p>Mood direction, plans and a curated palette to react to.</p></li>
<li><span class="steps-n">03</span><h3>Sourcing</h3><p>Furniture, lighting and finishes specified to your budget.</p></li>
<li><span class="steps-n">04</span><h3>Install</h3><p>Delivery, styling and the final reveal, handled by us.</p></li>
</ol>
</section>
<footer class="foot">
<p>Marlowe & Reed · 42 Copperfield Lane, London · <a href="#" data-enquire="General enquiry">[email protected]</a></p>
<p class="foot-fine">Fictional studio for demonstration. All prices illustrative.</p>
</footer>
</div>
<!-- Enquiry panel -->
<div class="panel-backdrop" id="backdrop" hidden></div>
<aside class="panel" id="panel" aria-hidden="true" aria-labelledby="panelTitle" role="dialog">
<div class="panel-head">
<div>
<p class="panel-eyebrow">Request a proposal</p>
<h2 id="panelTitle">Enquire</h2>
</div>
<button type="button" class="panel-close" id="panelClose" aria-label="Close">×</button>
</div>
<form class="panel-form" id="enquiryForm" novalidate>
<label class="field">
<span>Service</span>
<select name="service" id="fService"></select>
</label>
<label class="field">
<span>Name</span>
<input type="text" name="name" required placeholder="Your name" autocomplete="name" />
</label>
<label class="field">
<span>Email</span>
<input type="email" name="email" required placeholder="[email protected]" autocomplete="email" />
</label>
<label class="field">
<span>Rooms</span>
<input type="number" name="rooms" id="fRooms" min="1" max="12" value="3" />
</label>
<label class="field">
<span>Tell us about the space</span>
<textarea name="notes" rows="3" placeholder="A calm main bedroom, lots of natural light…"></textarea>
</label>
<div class="panel-summary" id="panelSummary" aria-live="polite"></div>
<button type="submit" class="btn btn-solid btn-block">Send enquiry</button>
<p class="panel-fine">We reply within two working days. No obligation.</p>
</form>
</aside>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A calm, editorial services page for a fictional interior studio, Marlowe & Reed. Four service cards — Full Design, E-Design, Consultation, and Styling — each present a short brief, an itemised deliverables list, a duration badge, and a from $X price. A room-count stepper at the top recalculates every card’s estimate live, so the pricing feels responsive rather than static.
Each card carries an Enquire button that opens a slide-in request panel pre-filled with the chosen service; submitting it fires a confirmation toast and resets the form. A “Most requested” badge highlights the flagship tier, and hovering any card lifts it with a soft shadow and a thin clay underline that draws left-to-right.
The whole layout is built with vanilla HTML, CSS, and JavaScript — no frameworks. It stays legible down to 360px, keeps focus rings visible for keyboard users, and leans on a warm natural palette (clay, walnut, sage) with Cormorant Garamond headings over Inter body text for an unhurried, print-like feel.