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: 28px;
}
.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;
}
.ll-section-title {
display: flex;
align-items: center;
gap: 12px;
margin: 18px 0 8px;
color: #171217;
font-size: 15px;
letter-spacing: .02em;
text-transform: uppercase;
}
.ll-section-title::after {
content: “”;
height: 1px;
flex: 1;
background: #cfc9ce;
}
.ll-table {
width: 100%;
border-collapse: collapse;
background: #eee;
margin-bottom: 18px;
font-size: 13px;
}
.ll-table th,
.ll-table td {
padding: 11px 16px;
text-align: left;
vertical-align: top;
}
.ll-table th {
width: 48%;
font-weight: 800;
text-transform: uppercase;
}
.ll-email-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 140px;
gap: 16px;
margin-bottom: 18px;
}
.ll-email-row input {
border-radius: 0;
background: #fff;
}
.ll-email-row button {
border: 0;
background: #f7c72c;
color: #000;
font-weight: 800;
cursor: pointer;
}
.making-up-copy {
margin: 0 0 14px;
font-size: 13px;
line-height: 1.5;
}
.text-link {
display: inline-block;
color: #000;
border-bottom: 2px solid #f7c72c;
text-decoration: none;
font-weight: 800;
text-transform: uppercase;
font-size: 13px;
padding-bottom: 4px;
}
@media (max-width: 760px) {
body { padding: 12px; }
.tabs { grid-template-columns: 1fr 1fr; }
.panel { padding: 18px; }
.grid { grid-template-columns: 1fr; }
.ll-email-row { 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 fieldText(panel, name) {
const field = panel.querySelector(‘[data-field=”‘ + name + ‘”]’);
return field && field.selectedOptions ? field.selectedOptions[0].textContent : field.value;
}
function rows(items) {
return items.map((item) => “
“).join(“”);
}
function renderResult(panel, calculationRows, specificationRows, subject, body, quoteLabel) {
const mail = makeMailto(subject, body);
panel.querySelector(‘[data-output=”result”]’).innerHTML =
‘
‘ +
‘
‘ +
‘
‘ +
‘
‘ +
‘
‘ +
” +
” +
‘‘ +
” +
‘
‘ +
‘
Want to use your own fabric? Not a problem. Our making up service is quick, simple and our prices are competitive.
‘ +
‘‘ + (quoteLabel || “Get a quote”) + ‘‘;
}
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”);
const body = “Curtain fabric required: ” + metres(requiredCm) + “\nFabric cost: ” + money(cost);
renderResult(panel, [
[“Fabric required”, metres(requiredCm)],
[“Fabric cost”, money(cost)],
[“Lining required”, val(panel, “interlined”) === “yes” ? metres(requiredCm) : “Not selected”]
], [
[“Fabric width”, fabricWidth + ” cm”],
[“Pattern repeat”, repeat + ” cm”],
[“Price per metre”, money(num(panel, “ppm”))],
[“Width”, width + ” cm”],
[“Drop”, drop + ” cm”],
[“Pole or track”, fieldText(panel, “fixture”)],
[“Heading type”, fieldText(panel, “heading”)],
[“Style”, fieldText(panel, “pair”)],
[“Fullness”, f.toFixed(2)]
], “Curtain fabric result”, body, “Get a quote”);
}
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”);
const body = “Blind fabric required: ” + metres(requiredCm) + “\nFabric cost: ” + money(cost);
renderResult(panel, [
[“Fabric required”, metres(requiredCm)],
[“Fabric cost”, money(cost)],
[“Lining required”, type === “roman” ? metres(requiredCm) : “Not required”],
[“Interlining required”, type === “roman” ? metres(requiredCm) : “Not required”]
], [
[“Blind type”, fieldText(panel, “type”)],
[“Fabric width”, fabricWidth + ” cm”],
[“Pattern repeat”, repeat + ” cm”],
[“Price per metre”, money(num(panel, “ppm”))],
[“Width”, width + ” cm”],
[“Drop”, drop + ” cm”]
], “Blind fabric result”, body, “Get a quote”);
}
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);
});
})();