Interior — Quote Request
A warm editorial quote-request form for an interior design studio: pick a project type, tally rooms, drag a budget slider, multiselect styles, choose a timeline, and enter contact details while a live estimate range updates in real time before a clean submit summary.
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 18px 44px -28px rgba(44, 38, 32, 0.4);
}
* { 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;
}
.display {
font-family: "Cormorant Garamond", Georgia, serif;
font-weight: 500;
letter-spacing: 0.01em;
}
.page {
max-width: 1080px;
margin: 0 auto;
padding: clamp(28px, 5vw, 64px) clamp(18px, 4vw, 44px) 80px;
}
/* Masthead */
.masthead { margin-bottom: 40px; }
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
margin-bottom: 26px;
}
.brand-mark {
display: grid;
place-items: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--walnut);
color: var(--paper);
font-family: "Cormorant Garamond", serif;
font-size: 17px;
letter-spacing: 0.02em;
}
.brand-name {
font-weight: 600;
font-size: 15px;
letter-spacing: 0.04em;
}
.eyebrow {
margin: 0 0 8px;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--clay-d);
}
h1.display {
margin: 0 0 12px;
font-size: clamp(40px, 7vw, 64px);
line-height: 1.02;
}
.lede {
margin: 0;
max-width: 46ch;
color: var(--ink-2);
font-size: 16px;
}
/* Layout */
.layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
gap: 40px;
align-items: start;
}
/* Form */
.form { min-width: 0; }
.field {
padding: 26px 0;
border-top: 1px solid var(--line);
}
.field:first-child { border-top: 0; padding-top: 0; }
.field-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin-bottom: 16px;
}
.field-title {
margin: 0 0 16px;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink);
}
.field-head .field-title { margin: 0; }
.field-note, .step-hint, .opt {
font-size: 13px;
color: var(--muted);
font-weight: 500;
}
/* Project type cards */
.type-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.type-card {
position: relative;
display: block;
cursor: pointer;
}
.type-card input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.type-body {
display: block;
padding: 18px 18px 16px;
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s, background 0.18s;
}
.type-name {
display: block;
font-family: "Cormorant Garamond", serif;
font-size: 24px;
font-weight: 600;
line-height: 1.1;
margin-bottom: 4px;
}
.type-desc {
display: block;
font-size: 13px;
color: var(--ink-2);
}
.type-card:hover .type-body {
border-color: var(--clay);
transform: translateY(-2px);
}
.type-card input:checked + .type-body {
border-color: var(--walnut);
background: var(--white);
box-shadow: inset 0 0 0 1px var(--walnut), var(--shadow);
}
.type-card input:focus-visible + .type-body {
outline: 2px solid var(--clay);
outline-offset: 3px;
}
/* Stepper */
.stepper {
display: inline-flex;
align-items: center;
gap: 6px;
}
.step-btn {
width: 44px;
height: 44px;
border-radius: var(--r-md);
border: 1px solid var(--line);
background: var(--paper);
color: var(--ink);
font-size: 22px;
line-height: 1;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, transform 0.08s;
}
.step-btn:hover { border-color: var(--clay); background: var(--white); }
.step-btn:active { transform: scale(0.94); }
.step-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.step-btn:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.step-val {
min-width: 52px;
text-align: center;
font-family: "Cormorant Garamond", serif;
font-size: 30px;
font-weight: 600;
}
.step-hint { margin-left: 4px; }
/* Budget slider */
.budget-read {
font-family: "Cormorant Garamond", serif;
font-size: 26px;
font-weight: 600;
color: var(--walnut);
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 4px;
border-radius: 999px;
background: linear-gradient(var(--clay), var(--clay)) no-repeat,
var(--line);
background-size: 40% 100%;
outline: none;
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--white);
border: 2px solid var(--walnut);
box-shadow: var(--shadow);
transition: transform 0.12s;
}
input[type="range"]::-webkit-slider-thumb:active { transform: scale(1.15); }
input[type="range"]::-moz-range-thumb {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--white);
border: 2px solid var(--walnut);
box-shadow: var(--shadow);
}
input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--clay); outline-offset: 2px; }
.range-scale {
display: flex;
justify-content: space-between;
margin-top: 8px;
font-size: 12px;
color: var(--muted);
}
/* Style chips */
.chips {
display: flex;
flex-wrap: wrap;
gap: 9px;
}
.chip {
padding: 9px 15px;
border-radius: 999px;
border: 1px solid var(--line);
background: var(--paper);
color: var(--ink-2);
font-family: inherit;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.16s;
}
.chip:hover { border-color: var(--clay); color: var(--ink); }
.chip:active { transform: scale(0.96); }
.chip:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.chip[aria-pressed="true"] {
background: var(--sage);
border-color: var(--sage);
color: #2f3a24;
font-weight: 600;
}
.chip.is-locked { opacity: 0.45; cursor: not-allowed; }
/* Segmented timeline */
.segmented {
display: inline-flex;
flex-wrap: wrap;
gap: 4px;
padding: 4px;
background: var(--paper);
border: 1px solid var(--line);
border-radius: 999px;
}
.seg { position: relative; }
.seg input { position: absolute; opacity: 0; pointer-events: none; }
.seg span {
display: block;
padding: 8px 16px;
border-radius: 999px;
font-size: 13px;
font-weight: 500;
color: var(--ink-2);
cursor: pointer;
transition: background 0.16s, color 0.16s;
}
.seg:hover span { color: var(--ink); }
.seg input:checked + span {
background: var(--walnut);
color: var(--paper);
font-weight: 600;
}
.seg input:focus-visible + span { outline: 2px solid var(--clay); outline-offset: 2px; }
/* Contact inputs */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.input-wrap { margin-bottom: 16px; display: flex; flex-direction: column; }
.input-wrap:last-child { margin-bottom: 0; }
.input-wrap label {
font-size: 13px;
font-weight: 600;
margin-bottom: 6px;
color: var(--ink);
}
input[type="text"], input[type="email"], textarea {
width: 100%;
padding: 12px 14px;
font-family: inherit;
font-size: 15px;
color: var(--ink);
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
transition: border-color 0.16s, box-shadow 0.16s;
}
textarea { resize: vertical; }
input:focus, textarea:focus {
outline: none;
border-color: var(--clay);
box-shadow: 0 0 0 3px rgba(176, 137, 104, 0.18);
}
.input-wrap.invalid input { border-color: #b4472f; }
.err {
font-size: 12px;
color: #b4472f;
margin-top: 5px;
min-height: 0;
transition: min-height 0.15s;
}
.input-wrap.invalid .err { min-height: 15px; }
/* Submit */
.submit {
margin-top: 30px;
width: 100%;
padding: 16px;
border: 0;
border-radius: var(--r-md);
background: var(--walnut);
color: var(--paper);
font-family: inherit;
font-size: 15px;
font-weight: 600;
letter-spacing: 0.02em;
cursor: pointer;
transition: background 0.18s, transform 0.08s;
}
.submit:hover { background: var(--clay-d); }
.submit:active { transform: translateY(1px); }
.submit:focus-visible { outline: 2px solid var(--clay); outline-offset: 3px; }
/* Estimate rail */
.rail { position: sticky; top: 28px; }
.rail-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 24px 22px;
box-shadow: var(--shadow);
}
.rail-eyebrow {
margin: 0 0 6px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--clay-d);
}
.rail-range {
margin: 0 0 4px;
font-family: "Cormorant Garamond", serif;
font-size: 30px;
font-weight: 600;
color: var(--walnut);
transition: transform 0.2s;
}
.rail-range.bump { transform: scale(1.05); }
.rail-sub {
margin: 0 0 18px;
font-size: 12px;
color: var(--muted);
}
.rail-list {
list-style: none;
margin: 0 0 18px;
padding: 18px 0 0;
border-top: 1px solid var(--line);
}
.rail-list li {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 7px 0;
font-size: 13px;
}
.rail-list span { color: var(--muted); }
.rail-list b {
color: var(--ink);
font-weight: 600;
text-align: right;
}
.rail-foot {
margin: 0;
padding-top: 16px;
border-top: 1px solid var(--line);
font-size: 12px;
color: var(--sage);
font-weight: 600;
}
/* Confirmation */
.confirm { margin-top: 8px; }
.confirm-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: clamp(28px, 5vw, 48px);
box-shadow: var(--shadow);
text-align: center;
animation: rise 0.4s ease;
}
@keyframes rise {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: none; }
}
.confirm-check {
display: grid;
place-items: center;
width: 56px;
height: 56px;
margin: 0 auto 18px;
border-radius: 50%;
background: var(--sage);
color: #2f3a24;
font-size: 28px;
}
.confirm-card h2 { margin: 0 0 10px; font-size: clamp(34px, 6vw, 46px); }
.confirm-lede { margin: 0 auto 26px; max-width: 42ch; color: var(--ink-2); }
.confirm-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 1px;
margin: 0 0 26px;
background: var(--line);
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
text-align: left;
}
.confirm-grid > div {
background: var(--paper);
padding: 14px 16px;
}
.confirm-grid dt {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 4px;
}
.confirm-grid dd {
margin: 0;
font-size: 15px;
font-weight: 600;
color: var(--ink);
}
.ghost {
padding: 12px 24px;
border: 1px solid var(--walnut);
border-radius: var(--r-md);
background: transparent;
color: var(--walnut);
font-family: inherit;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.16s, color 0.16s;
}
.ghost:hover { background: var(--walnut); color: var(--paper); }
.ghost:focus-visible { outline: 2px solid var(--clay); outline-offset: 3px; }
/* Toast */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 20px);
padding: 13px 22px;
background: var(--ink);
color: var(--paper);
border-radius: 999px;
font-size: 14px;
font-weight: 500;
box-shadow: var(--shadow);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s, transform 0.25s;
z-index: 50;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* Responsive */
@media (max-width: 860px) {
.layout { grid-template-columns: 1fr; }
.rail { position: static; order: -1; }
}
@media (max-width: 520px) {
.type-grid { grid-template-columns: 1fr; }
.grid-2 { grid-template-columns: 1fr; }
h1.display { font-size: 40px; }
.segmented { display: flex; width: 100%; }
.seg { flex: 1; }
.seg span { text-align: center; padding: 9px 8px; }
.page { padding-bottom: 100px; }
}(function () {
"use strict";
var MAX_STYLES = 3;
var MIN_ROOMS = 1;
var MAX_ROOMS = 12;
var state = {
ptype: "Full home",
base: 24000,
rooms: 3,
budget: 40000,
styles: [],
timeline: "Flexible",
tmult: 0.94,
};
// --- helpers ---
var fmt = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0,
});
function money(n) {
return fmt.format(Math.round(n));
}
function $(sel) {
return document.querySelector(sel);
}
function $all(sel) {
return Array.prototype.slice.call(document.querySelectorAll(sel));
}
var toastEl = $("#toast");
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
// --- estimate math ---
function computeRange() {
// room factor scales sub-linearly for larger scopes
var roomFactor = 0.55 + state.rooms * 0.18;
var budgetTier = state.budget / 42000; // ~1 at the mid mark
var styleFactor = 1 + state.styles.length * 0.04;
var core = state.base * roomFactor * budgetTier * styleFactor * state.tmult;
// blend with the stated budget so the range stays believable
var blended = core * 0.6 + state.budget * 0.4;
var low = blended * 0.82;
var high = blended * 1.18;
return { low: low, high: high };
}
var railRange = $("#estRange");
function bumpRange() {
railRange.classList.remove("bump");
// force reflow to restart the transition
void railRange.offsetWidth;
railRange.classList.add("bump");
}
function render() {
var r = computeRange();
railRange.textContent = money(r.low) + " – " + money(r.high);
bumpRange();
$("#sumType").textContent = state.ptype;
$("#sumRooms").textContent = state.rooms;
$("#sumBudget").textContent = money(state.budget);
$("#sumStyles").textContent = state.styles.length
? state.styles.join(", ")
: "—";
$("#sumTimeline").textContent = state.timeline;
}
// --- project type ---
$all('input[name="ptype"]').forEach(function (input) {
input.addEventListener("change", function () {
state.ptype = input.value;
state.base = parseInt(input.dataset.base, 10);
render();
});
});
// --- rooms stepper ---
var roomVal = $("#roomVal");
var minusBtn = $("#roomMinus");
var plusBtn = $("#roomPlus");
function setRooms(n) {
state.rooms = Math.max(MIN_ROOMS, Math.min(MAX_ROOMS, n));
roomVal.textContent = state.rooms;
minusBtn.disabled = state.rooms <= MIN_ROOMS;
plusBtn.disabled = state.rooms >= MAX_ROOMS;
render();
}
minusBtn.addEventListener("click", function () {
setRooms(state.rooms - 1);
});
plusBtn.addEventListener("click", function () {
setRooms(state.rooms + 1);
});
// --- budget slider ---
var budget = $("#budget");
var budgetRead = $("#budgetRead");
function paintSlider() {
var pct =
((budget.value - budget.min) / (budget.max - budget.min)) * 100;
budget.style.backgroundSize = pct + "% 100%";
}
budget.addEventListener("input", function () {
state.budget = parseInt(budget.value, 10);
budgetRead.textContent =
state.budget >= 150000 ? "$150,000+" : money(state.budget);
paintSlider();
render();
});
// --- style chips ---
var chips = $all(".chip");
var styleCount = $("#styleCount");
function refreshChipLocks() {
var atMax = state.styles.length >= MAX_STYLES;
chips.forEach(function (chip) {
var on = chip.getAttribute("aria-pressed") === "true";
chip.classList.toggle("is-locked", atMax && !on);
});
styleCount.textContent = state.styles.length
? state.styles.length + " of " + MAX_STYLES + " selected"
: "Pick up to " + MAX_STYLES;
}
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
var name = chip.dataset.style;
var on = chip.getAttribute("aria-pressed") === "true";
if (on) {
chip.setAttribute("aria-pressed", "false");
state.styles = state.styles.filter(function (s) {
return s !== name;
});
} else {
if (state.styles.length >= MAX_STYLES) {
toast("Up to " + MAX_STYLES + " styles — deselect one first");
return;
}
chip.setAttribute("aria-pressed", "true");
state.styles.push(name);
}
refreshChipLocks();
render();
});
});
// --- timeline ---
$all('input[name="timeline"]').forEach(function (input) {
input.addEventListener("change", function () {
state.timeline = input.value;
state.tmult = parseFloat(input.dataset.mult);
render();
});
});
// --- validation ---
function validateField(input, msg, test) {
var wrap = input.closest(".input-wrap");
var err = wrap.querySelector(".err");
var ok = test(input.value.trim());
wrap.classList.toggle("invalid", !ok);
err.textContent = ok ? "" : msg;
return ok;
}
var nameInput = $("#name");
var emailInput = $("#email");
function checkName() {
return validateField(nameInput, "Please enter your name", function (v) {
return v.length >= 2;
});
}
function checkEmail() {
return validateField(emailInput, "Enter a valid email", function (v) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
});
}
nameInput.addEventListener("blur", checkName);
emailInput.addEventListener("blur", checkEmail);
nameInput.addEventListener("input", function () {
if (nameInput.closest(".input-wrap").classList.contains("invalid")) checkName();
});
emailInput.addEventListener("input", function () {
if (emailInput.closest(".input-wrap").classList.contains("invalid")) checkEmail();
});
// --- submit ---
var form = $("#quoteForm");
var confirmSec = $("#confirm");
var confirmGrid = $("#confirmGrid");
form.addEventListener("submit", function (e) {
e.preventDefault();
var okName = checkName();
var okEmail = checkEmail();
if (!okName || !okEmail) {
toast("Please fix the highlighted fields");
(okName ? emailInput : nameInput).focus();
return;
}
var r = computeRange();
var rows = [
["Project", state.ptype],
["Rooms", String(state.rooms)],
["Budget", money(state.budget)],
["Styles", state.styles.length ? state.styles.join(", ") : "Open to ideas"],
["Timeline", state.timeline],
["Estimate", money(r.low) + " – " + money(r.high)],
];
confirmGrid.innerHTML = rows
.map(function (row) {
return (
"<div><dt>" + row[0] + "</dt><dd>" + row[1] + "</dd></div>"
);
})
.join("");
$("#cName").textContent = nameInput.value.trim().split(" ")[0];
$("#cEmail").textContent = emailInput.value.trim();
form.hidden = true;
$(".rail").hidden = true;
confirmSec.hidden = false;
confirmSec.scrollIntoView({ behavior: "smooth", block: "start" });
toast("Request queued — we'll be in touch");
});
$("#editBtn").addEventListener("click", function () {
confirmSec.hidden = true;
form.hidden = false;
$(".rail").hidden = false;
form.scrollIntoView({ behavior: "smooth", block: "start" });
});
// --- init ---
paintSlider();
setRooms(state.rooms);
refreshChipLocks();
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Marlowe & Fern — Quote Request</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&F</span>
<span class="brand-name">Marlowe & Fern</span>
</div>
<p class="eyebrow">Interior Studio · Est. 2014</p>
<h1 class="display">Request a quote</h1>
<p class="lede">Tell us about the space. We’ll shape a considered proposal within two business days — no obligation, just a clear starting point.</p>
</header>
<div class="layout">
<form id="quoteForm" class="form" novalidate>
<!-- Project type -->
<section class="field" aria-labelledby="lbl-type">
<h2 class="field-title" id="lbl-type">Project type</h2>
<div class="type-grid" role="radiogroup" aria-labelledby="lbl-type">
<label class="type-card">
<input type="radio" name="ptype" value="Full home" data-base="24000" checked />
<span class="type-body">
<span class="type-name">Full home</span>
<span class="type-desc">Whole-property design & build</span>
</span>
</label>
<label class="type-card">
<input type="radio" name="ptype" value="Single room" data-base="6500" />
<span class="type-body">
<span class="type-name">Single room</span>
<span class="type-desc">One space, start to finish</span>
</span>
</label>
<label class="type-card">
<input type="radio" name="ptype" value="Staging" data-base="4200" />
<span class="type-body">
<span class="type-name">Staging</span>
<span class="type-desc">Styling to sell or let</span>
</span>
</label>
<label class="type-card">
<input type="radio" name="ptype" value="Consult" data-base="1800" />
<span class="type-body">
<span class="type-name">Consult</span>
<span class="type-desc">Advice session & moodboard</span>
</span>
</label>
</div>
</section>
<!-- Rooms -->
<section class="field">
<h2 class="field-title">Rooms in scope</h2>
<div class="stepper" role="group" aria-label="Number of rooms">
<button type="button" class="step-btn" id="roomMinus" aria-label="Fewer rooms">−</button>
<output class="step-val" id="roomVal" aria-live="polite">3</output>
<button type="button" class="step-btn" id="roomPlus" aria-label="More rooms">+</button>
<span class="step-hint">rooms</span>
</div>
</section>
<!-- Budget -->
<section class="field">
<div class="field-head">
<h2 class="field-title">Indicative budget</h2>
<span class="budget-read" id="budgetRead">$40,000</span>
</div>
<input type="range" id="budget" min="2000" max="150000" step="1000" value="40000"
aria-label="Indicative budget" />
<div class="range-scale">
<span>$2k</span><span>$150k+</span>
</div>
</section>
<!-- Style -->
<section class="field" aria-labelledby="lbl-style">
<div class="field-head">
<h2 class="field-title" id="lbl-style">Preferred styles</h2>
<span class="field-note" id="styleCount">Pick up to 3</span>
</div>
<div class="chips" id="styleChips" role="group" aria-labelledby="lbl-style">
<button type="button" class="chip" data-style="Warm minimal" aria-pressed="false">Warm minimal</button>
<button type="button" class="chip" data-style="Mid-century" aria-pressed="false">Mid-century</button>
<button type="button" class="chip" data-style="Japandi" aria-pressed="false">Japandi</button>
<button type="button" class="chip" data-style="Rustic" aria-pressed="false">Rustic</button>
<button type="button" class="chip" data-style="Coastal" aria-pressed="false">Coastal</button>
<button type="button" class="chip" data-style="Art deco" aria-pressed="false">Art deco</button>
<button type="button" class="chip" data-style="Industrial" aria-pressed="false">Industrial</button>
<button type="button" class="chip" data-style="Maximal" aria-pressed="false">Maximal</button>
</div>
</section>
<!-- Timeline -->
<section class="field" aria-labelledby="lbl-time">
<h2 class="field-title" id="lbl-time">Ideal timeline</h2>
<div class="segmented" role="radiogroup" aria-labelledby="lbl-time">
<label class="seg">
<input type="radio" name="timeline" value="Flexible" data-mult="0.94" checked />
<span>Flexible</span>
</label>
<label class="seg">
<input type="radio" name="timeline" value="3–6 months" data-mult="1" />
<span>3–6 mo</span>
</label>
<label class="seg">
<input type="radio" name="timeline" value="Under 3 months" data-mult="1.12" />
<span>< 3 mo</span>
</label>
<label class="seg">
<input type="radio" name="timeline" value="Rush" data-mult="1.28" />
<span>Rush</span>
</label>
</div>
</section>
<!-- Contact -->
<section class="field">
<h2 class="field-title">Your details</h2>
<div class="grid-2">
<div class="input-wrap">
<label for="name">Full name</label>
<input type="text" id="name" name="name" placeholder="Elena Marsh" autocomplete="name" required />
<span class="err" data-for="name"></span>
</div>
<div class="input-wrap">
<label for="email">Email</label>
<input type="email" id="email" name="email" placeholder="[email protected]" autocomplete="email" required />
<span class="err" data-for="email"></span>
</div>
</div>
<div class="input-wrap">
<label for="notes">Anything else? <span class="opt">(optional)</span></label>
<textarea id="notes" name="notes" rows="3" placeholder="North-facing flat, keep the parquet, love brass accents…"></textarea>
</div>
</section>
<button type="submit" class="submit">Send request</button>
</form>
<!-- Live estimate rail -->
<aside class="rail" aria-label="Live estimate summary">
<div class="rail-card">
<p class="rail-eyebrow">Live estimate</p>
<p class="rail-range" id="estRange">$18,000 – $27,600</p>
<p class="rail-sub">Indicative range · refined after consult</p>
<ul class="rail-list" id="railList">
<li><span>Project</span><b id="sumType">Full home</b></li>
<li><span>Rooms</span><b id="sumRooms">3</b></li>
<li><span>Budget</span><b id="sumBudget">$40,000</b></li>
<li><span>Styles</span><b id="sumStyles">—</b></li>
<li><span>Timeline</span><b id="sumTimeline">Flexible</b></li>
</ul>
<p class="rail-foot">Fixed-fee proposal · no obligation</p>
</div>
</aside>
</div>
<!-- Confirmation (hidden until submit) -->
<section class="confirm" id="confirm" hidden aria-live="polite">
<div class="confirm-card">
<span class="confirm-check" aria-hidden="true">✓</span>
<h2 class="display">Request received</h2>
<p class="confirm-lede">Thanks, <b id="cName">there</b> — a designer will reply to <b id="cEmail">you</b> within two business days.</p>
<dl class="confirm-grid" id="confirmGrid"></dl>
<button type="button" class="ghost" id="editBtn">Edit request</button>
</div>
</section>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A single-column editorial quote request for the fictional Marlowe & Fern interior studio. The form walks a prospect through the shape of their project — choosing a project type card (full home, single room, staging, or consult), stepping the room count up and down, dragging a budget slider, tapping style chips, and picking a timeline — while a sticky summary rail keeps a live estimate range visible the whole time.
Every control recomputes the estimate as you go: the base is driven by project type and room count, then scaled by budget tier and style breadth, with a timeline surcharge for rush jobs. The range animates when it changes, style chips toggle with a soft press, and the room stepper and slider are fully keyboard-usable with visible focus rings. Contact fields validate inline before you can send.
On submit the form collapses into a tidy confirmation card that echoes back the selected project type, rooms, budget, styles, timeline, and estimate range, plus a toast confirming the request was queued — all in the studio’s muted clay-and-sage palette with a Cormorant Garamond display face for headings.