Made to Measure Calculators
:root {
–ink: #252126;
–muted: #6b6269;
–line: #ded6dd;
–soft: #f8f5f7;
–accent: #77566d;
–accent-dark: #55384e;
–white: #fff;
}
body {
margin: 0;
padding: 24px;
color: var(–ink);
background: #f2f0f2;
font-family: Arial, Helvetica, sans-serif;
}
.mtm-calcs {
max-width: 1040px;
margin: 0 auto;
background: var(–white);
border: 1px solid var(–line);
border-radius: 8px;
overflow: hidden;
}
.curtain-hero {
min-height: 180px;
background: #f6f0f3;
border-bottom: 1px solid var(–line);
}
.curtain-hero svg {
display: block;
width: 100%;
height: auto;
}
.tabs {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
border-bottom: 1px solid var(–line);
background: var(–soft);
}
.tabs button {
min-height: 52px;
padding: 10px;
border: 0;
border-right: 1px solid var(–line);
color: var(–ink);
background: transparent;
font-weight: 700;
cursor: pointer;
}
.tabs button:last-child { border-right: 0; }
.tabs button.active {
color: #fff;
background: var(–accent);
}
.panel {
display: none;
padding: 24px;
}
.panel.active { display: block; }
h2 {
margin: 0 0 18px;
font-size: 24px;
line-height: 1.25;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.field {
display: grid;
gap: 6px;
}
.field label {
font-size: 14px;
font-weight: 700;
}
input, select {
min-height: 44px;
width: 100%;
box-sizing: border-box;
padding: 10px 12px;
border: 1px solid var(–line);
border-radius: 6px;
background: #fff;
color: var(–ink);
font-size: 16px;
}
.result {
margin-top: 20px;
padding: 18px;
display: grid;
gap: 8px;
background: var(–soft);
border-radius: 8px;
}
.price {
color: var(–accent-dark);
font-size: 30px;
font-weight: 800;
}
.note {
margin: 0;
color: var(–muted);
font-size: 13px;
line-height: 1.45;
}
.quote {
display: inline-flex;
align-items: center;
justify-content: center;
margin-top: 14px;
min-height: 42px;
padding: 0 16px;
border-radius: 6px;
color: #fff;
background: var(–accent);
text-decoration: none;
font-weight: 700;
}
@media (max-width: 760px) {
body { padding: 12px; }
.tabs { grid-template-columns: 1fr 1fr; }
.panel { padding: 18px; }
.grid { grid-template-columns: 1fr; }
}
Curtain Making Service
Roman Blind Making Service
Curtain Fabric Calculator
Blind Fabric Calculator
(function () {
const money = (n) => “GBP ” + Math.max(0, n).toFixed(2);
const metres = (cm) => (Math.ceil(cm / 10) / 100).toFixed(2).replace(/\.00$/, “”) + ” m”;
const num = (panel, name) => Number(panel.querySelector(‘[data-field=”‘ + name + ‘”]’).value) || 0;
const val = (panel, name) => panel.querySelector(‘[data-field=”‘ + name + ‘”]’).value;
const fullness = { pencil: 2, double: 2.5, eyelet: 2, wave: 2.3 };
const emailAddress = “charmshape@gmail.com”;
const curtainWidthBands = [36, 105, 173, 242, 310, 379, 447, 516, 584, 653, 721, 790, 858, 927, 995, 1064, 1132, 1201];
const curtainBaseAt100 = [123, 185, 248, 310, 372, 435, 497, 559, 622, 684, 746, 809, 871, 933, 996, 1058, 1120, 1183];
const romanWidths = [50, 100, 150, 200, 250];
const romanDrops = [50, 100, 150, 200, 250, 300];
const romanStandard = [
[155, 180, 216, 252, 292, 333],
[178, 212, 259, 316, 370, 416],
[235, 276, 348, 419, 511, 601],
[267, 330, 420, 510, 590, 716],
[298, 369, 471, 627, 777, 804]
];
function repeatLength(length, repeat) {
return repeat > 0 ? Math.ceil(length / repeat) * repeat : length;
}
function curtainFabric(panel) {
const width = num(panel, “width”);
const drop = num(panel, “drop”);
const fabricWidth = Math.max(1, num(panel, “fabricWidth”));
const repeat = num(panel, “repeat”);
const pair = val(panel, “pair”) === “pair” ? 2 : 1;
const f = fullness[val(panel, “heading”)];
const widths = Math.max(pair, Math.ceil((width * f) / fabricWidth));
const cutDrop = repeatLength(drop + 25, repeat);
const requiredCm = widths * cutDrop;
const cost = requiredCm / 100 * num(panel, “ppm”);
panel.querySelector(‘[data-output=”required”]’).textContent = metres(requiredCm);
panel.querySelector(‘[data-output=”note”]’).textContent =
“Fabric cost ” + money(cost) + “. ” + widths + ” fabric width(s), cut drop ” + cutDrop + ” cm, fullness ” + f.toFixed(2) + “.”;
panel.querySelector(‘[data-output=”email”]’).href = makeMailto(“Curtain fabric result”, panel.querySelector(‘[data-output=”note”]’).textContent);
}
function blindFabric(panel) {
const width = num(panel, “width”);
const drop = num(panel, “drop”);
const fabricWidth = Math.max(1, num(panel, “fabricWidth”));
const repeat = num(panel, “repeat”);
const type = val(panel, “type”);
const allowance = type === “roman” ? 20 : 30;
const widths = Math.max(1, Math.ceil(width / fabricWidth));
const cutDrop = repeatLength(drop + allowance, repeat);
const requiredCm = widths * cutDrop;
const cost = requiredCm / 100 * num(panel, “ppm”);
panel.querySelector(‘[data-output=”required”]’).textContent = metres(requiredCm);
panel.querySelector(‘[data-output=”note”]’).textContent =
“Fabric cost ” + money(cost) + “. ” + widths + ” fabric width(s), cut drop ” + cutDrop + ” cm.”;
panel.querySelector(‘[data-output=”email”]’).href = makeMailto(“Blind fabric result”, panel.querySelector(‘[data-output=”note”]’).textContent);
}
function lookupIndex(maxes, value) {
const idx = maxes.findIndex((max) => value x y {
button.addEventListener(“click”, () => {
document.querySelectorAll(“.tabs button”).forEach((b) => b.classList.remove(“active”));
document.querySelectorAll(“.panel”).forEach((panel) => panel.classList.remove(“active”));
button.classList.add(“active”);
document.getElementById(button.dataset.tab).classList.add(“active”);
});
});
Object.keys(calculators).forEach((id) => {
const panel = document.getElementById(id);
panel.querySelectorAll(“input, select”).forEach((field) => {
field.addEventListener(“input”, () => calculators[id](panel));
field.addEventListener(“change”, () => calculators[id](panel));
});
calculators[id](panel);
});
})();