* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
background: #f6f6f6;
color: #111;
}
.wrap {
max-width: 900px;
margin: 0 auto;
padding: 14px;
}
.main-card {
border: 1px solid #e5e5e5;
border-radius: 16px;
padding: 16px;
background: #fff;
box-sizing: border-box;
}
h2 {
margin: 0 0 8px 0;
font-size: 26px;
line-height: 1.2;
}
.intro {
margin: 0 0 18px 0;
font-size: 14px;
line-height: 1.6;
color: #444;
}
.box {
border: 1px solid #ececec;
border-radius: 14px;
padding: 14px;
background: #fafafa;
margin-bottom: 16px;
}
.box.white {
background: #fff;
}
.box h3 {
margin: 0 0 10px 0;
font-size: 18px;
}
.muted {
margin: 0 0 14px 0;
font-size: 13px;
line-height: 1.6;
color: #555;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
.field label {
display: block;
font-size: 14px;
font-weight: 700;
margin-bottom: 6px;
}
.field input,
.field select {
width: 100%;
padding: 13px;
border: 1px solid #d5d5d5;
border-radius: 12px;
font-size: 16px;
background: #fff;
box-sizing: border-box;
}
.checkbox-row {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 700;
min-height: 48px;
}
.checkbox-row input {
width: 18px;
height: 18px;
}
.btn-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 14px;
}
.btn {
padding: 13px 14px;
border-radius: 12px;
font-size: 16px;
font-weight: 700;
cursor: pointer;
}
.btn-primary {
border: none;
background: #14324d;
color: #fff;
}
.btn-secondary {
border: none;
background: #14324d;
color: #fff;
}
.error {
display: none;
margin-top: 12px;
padding: 11px 12px;
border: 1px solid #e1bcbc;
background: #fff5f5;
color: #9d2020;
border-radius: 10px;
font-size: 14px;
}
.result {
display: none;
margin-top: 16px;
}
.result-box {
background: #f7f7f7;
border-radius: 14px;
padding: 14px;
background: #fafafa;
}
.result-label {
font-size: 13px;
color: #555;
margin-bottom: 8px;
}
.result-cards {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
.result-card {
border: 1px solid #e5e5e5;
border-radius: 12px;
padding: 12px;
background: #fff;
}
.result-name {
font-size: 14px;
font-weight: 700;
line-height: 1.4;
margin-bottom: 6px;
}
.result-distance {
font-size: 13px;
color: #555;
margin-bottom: 6px;
}
.result-delay {
font-size: 24px;
font-weight: 800;
line-height: 1.1;
}
.result-meta {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #e3e3e3;
font-size: 14px;
line-height: 1.7;
}
.note {
margin-top: 12px;
font-size: 13px;
line-height: 1.6;
color: #555;
}
@media (min-width: 760px) {
.settings-grid {
grid-template-columns: repeat(3, 1fr);
align-items: end;
}
.inputs-grid {
grid-template-columns: 1fr 1fr;
}
.result-cards {
grid-template-columns: 1fr 1fr;
}
}
function parseNumber(value) {
return parseFloat(String(value).replace(",", "."));
}
// Snabbkalkylator
function calcQuickCmToMs() {
const input = document.getElementById("quickCm").value.trim();
const errorBox = document.getElementById("quickError");
const resultBox = document.getElementById("quickResultBox");
const result = document.getElementById("quickResult");
errorBox.style.display = "none";
errorBox.textContent = "";
resultBox.style.display = "none";
if (input === "") {
errorBox.textContent = "Ange ett värde i cm.";
errorBox.style.display = "block";
return;
}
const cm = parseNumber(input);
if (isNaN(cm) || cm <= 0) {
errorBox.textContent = "Värdet måste vara ett positivt tal.";
errorBox.style.display = "block";
return;
}
const ms = cm / 34.3;
result.textContent = ms.toFixed(3);
resultBox.style.display = "block";
}
function resetQuickCmToMs() {
document.getElementById("quickCm").value = "";
document.getElementById("quickError").style.display = "none";
document.getElementById("quickError").textContent = "";
document.getElementById("quickResultBox").style.display = "none";
document.getElementById("quickResult").textContent = "-";
}
// Namn beroende på val
function getFrontFields(type) {
if (type === "1way") {
return [
{ id: "ftl", label: "Fram vänster" },
{ id: "ftr", label: "Fram höger" }
];
}
if (type === "2way") {
return [
{ id: "ftl", label: "Fram vänster diskant" },
{ id: "ftr", label: "Fram höger diskant" },
{ id: "fml", label: "Fram vänster mid" },
{ id: "fmr", label: "Fram höger mid" }
];
}
return [
{ id: "ftl", label: "Fram vänster diskant" },
{ id: "ftr", label: "Fram höger diskant" },
{ id: "fml", label: "Fram vänster mid" },
{ id: "fmr", label: "Fram höger mid" },
{ id: "fwl", label: "Fram vänster bas" },
{ id: "fwr", label: "Fram höger bas" }
];
}
function getRearFields(type) {
if (type === "none") {
return [];
}
if (type === "1way") {
return [
{ id: "rwl", label: "Bak vänster" },
{ id: "rwr", label: "Bak höger" }
];
}
return [
{ id: "rtl", label: "Bak vänster diskant" },
{ id: "rtr", label: "Bak höger diskant" },
{ id: "rwl", label: "Bak vänster bas" },
{ id: "rwr", label: "Bak höger bas" }
];
}
function renderInput(field) {
return `
`;
}
function updateSpeakerLayout() {
const frontType = document.getElementById("frontType").value;
const rearType = document.getElementById("rearType").value;
const hasSub = document.getElementById("hasSub").checked;
const fields = [
...getFrontFields(frontType),
...getRearFields(rearType)
];
if (hasSub) {
fields.push({ id: "sub", label: "Subwoofer" });
}
document.getElementById("inputsWrap").innerHTML = fields.map(renderInput).join("");
document.getElementById("dspError").style.display = "none";
document.getElementById("dspError").textContent = "";
document.getElementById("dspResult").style.display = "none";
document.getElementById("resultCards").innerHTML = "";
document.getElementById("maxDistance").textContent = "-";
}
function getActiveFields() {
const frontType = document.getElementById("frontType").value;
const rearType = document.getElementById("rearType").value;
const hasSub = document.getElementById("hasSub").checked;
const fields = [
...getFrontFields(frontType),
...getRearFields(rearType)
];
if (hasSub) {
fields.push({ id: "sub", label: "Subwoofer" });
}
return fields;
}
function calcDSP() {
const errorBox = document.getElementById("dspError");
const resultBox = document.getElementById("dspResult");
const resultCards = document.getElementById("resultCards");
const maxDistanceEl = document.getElementById("maxDistance");
errorBox.style.display = "none";
errorBox.textContent = "";
resultBox.style.display = "none";
resultCards.innerHTML = "";
const fields = getActiveFields();
const values = [];
for (const field of fields) {
const input = document.getElementById(field.id);
if (!input) continue;
const raw = input.value.trim();
if (raw === "") continue;
const cm = parseNumber(raw);
if (isNaN(cm) || cm <= 0) {
errorBox.textContent = "Alla ifyllda värden måste vara positiva tal.";
errorBox.style.display = "block";
return;
}
values.push({
id: field.id,
name: field.label,
cm: cm
});
}
if (values.length < 2) {
errorBox.textContent = "Fyll i minst två högtalare för att räkna relativ delay.";
errorBox.style.display = "block";
return;
}
const maxDistance = Math.max(...values.map(v => v.cm));
values.forEach(v => {
v.delay = (maxDistance - v.cm) / 34.3;
});
values.forEach(v => {
const card = document.createElement("div");
card.className = "result-card";
card.innerHTML = `
${v.name}
Avstånd: ${v.cm.toFixed(1)} cm
${v.delay.toFixed(3)} ms
`;
resultCards.appendChild(card);
});
maxDistanceEl.textContent = maxDistance.toFixed(1);
resultBox.style.display = "block";
}
function resetDSP() {
updateSpeakerLayout();
}
updateSpeakerLayout();