<div class="wrap">
<header>
<div>
<h1>Stabilitetsark – KG & GM (med last-tillegg)</h1>
<p>Regner ΣW, Σ(W·KG), KG = Σ(W·KG)/ΣW, GM = KM − KG. Støtter 1,5 og 1.5. Minus brukes for lossing.</p>
</div>
<div class="header-right">
<div class="mono" style="font-size:12px;opacity:.9">
Moment = W × KG
</div>
<div class="vclock" aria-label="Digital klokke">
<div class="vclock-label">KLOKKE</div>
<div class="vclock-time mono" id="vClock">--:--:--</div>
</div>
</div>
</header>
<div class="bar">
<div class="controls">
<div class="field">
<strong style="color:#0f172a">KM (m):</strong>
<input id="km" value="4,0" inputmode="decimal">
</div>
<button class="primary" id="addRowBtn" type="button">+ Legg til last</button>
<button id="resetBtn" type="button">Nullstill</button>
</div>
<div class="controls">
<button id="advToggleBtn" type="button">Avansert: AV</button>
</div>
</div>
<table>
<thead>
<tr>
<th>Beskrivelse</th>
<th class="center">Type</th>
<th>Vekt W (t)</th>
<th>KG (m)</th>
<th>Moment (t·m)</th>
<th class="center">Handling</th>
</tr>
</thead>
<tbody id="tbody">
<!-- Start row (locked) -->
<tr class="row locked">
<td class="label">
<input class="desc" value="Start deplasement" disabled>
</td>
<td class="center">
<select class="sign" disabled>
<option value="1" selected>+</option>
<option value="-1">−</option>
</select>
</td>
<td class="right"><input class="w" value="570" inputmode="decimal"></td>
<td class="right"><input class="kg" value="3" inputmode="decimal"></td>
<td class="right mono moment">= 0,000</td>
<td class="center mono">—</td>
</tr>
<!-- Load rows -->
<tr class="row">
<td class="label">
<input class="desc" value="Lasting #1" placeholder="Skriv navn (f.eks. Lossing/Lasting)">
</td>
<td class="center">
<select class="sign">
<option value="1" selected>+</option>
<option value="-1">−</option>
</select>
</td>
<td class="right"><input class="w" value="20" inputmode="decimal"></td>
<td class="right"><input class="kg" value="1,5" inputmode="decimal"></td>
<td class="right mono moment">= 0,000</td>
<td class="center row-actions"><button type="button" class="delBtn">Slett</button></td>
</tr>
<tr class="row">
<td class="label">
<input class="desc" value="Lasting #2" placeholder="Skriv navn (f.eks. Lossing/Lasting)">
</td>
<td class="center">
<select class="sign">
<option value="1" selected>+</option>
<option value="-1">−</option>
</select>
</td>
<td class="right"><input class="w" value="30" inputmode="decimal"></td>
<td class="right"><input class="kg" value="3,9" inputmode="decimal"></td>
<td class="right mono moment">= 0,000</td>
<td class="center row-actions"><button type="button" class="delBtn">Slett</button></td>
</tr>
<!-- Totals -->
<tr class="totals">
<td class="label">SUM</td>
<td></td>
<td class="right mono" id="sumW">= 0,000 t</td>
<td></td>
<td class="right mono" id="sumM">= 0,000 t·m</td>
<td></td>
</tr>
</tbody>
</table>
<div class="kpi">
<div class="box">
<div class="k">KG (ny)</div>
<div class="v mono" id="kgNew">0,000</div>
<div class="u">meter</div>
</div>
<div class="box">
<div class="k">GM (ny)</div>
<div class="v mono" id="gmNew">0,000</div>
<div class="u">meter</div>
</div>
<div class="box">
<div class="k">Kontroll</div>
<div class="v mono" id="checkLine">KG = ΣM / ΣW</div>
<div class="u">visning</div>
</div>
</div>
<!-- Advanced -->
<div id="advanced" class="advanced">
<div class="advanced-head">
<div class="title">Avansert</div>
<div class="field">Min GM-krav (m): <input id="minGM" value="0,15" inputmode="decimal"></div>
</div>
<div class="advanced-grid">
<div class="box">
<div class="k">Start KG</div>
<div class="v mono" id="startKG">0,000</div>
<div class="u">meter</div>
</div>
<div class="box">
<div class="k">ΔKG (ny − start)</div>
<div class="v mono" id="deltaKG">0,000</div>
<div class="u">meter</div>
</div>
<div class="box">
<div class="k">Status GM</div>
<div class="v mono" id="gmStatus">—</div>
<div class="u">OK / Ikke OK</div>
</div>
<div class="box">
<div class="k">Formel</div>
<div class="v mono" id="formulaLine">ΣM / ΣW</div>
<div class="u">tall-linje</div>
</div>
</div>
<div class="note">
“Lovlig” avgjøres av kravene du bruker (stabilitetshefte/IS Code/flaggekrav). Her sjekker vi kun mot en valgt min GM.
</div>
</div>
</div>
<script>
// ---------- Helpers ----------
function parseNum(v){
v = String(v ?? "").trim().replace(/\s+/g,"");
if (!v) return 0;
v = v.replace(",", ".");
const n = Number(v);
return Number.isFinite(n) ? n : 0;
}
function fmt(n, decimals=3){
return n.toFixed(decimals).replace(".", ",");
}
// ---------- Main calculation ----------
function calculateMain(){
const rows = document.querySelectorAll("#tbody tr.row");
let sumW = 0;
let sumM = 0;
rows.forEach(r => {
const sign = parseNum(r.querySelector(".sign")?.value) || 1; // +1 eller -1
const wRaw = parseNum(r.querySelector(".w")?.value);
const kg = parseNum(r.querySelector(".kg")?.value);
const w = sign * wRaw; // minus for lossing
const m = w * kg;
const momentCell = r.querySelector(".moment");
if (momentCell) momentCell.textContent = "= " + fmt(m, 3);
sumW += w;
sumM += m;
});
document.getElementById("sumW").textContent = "= " + fmt(sumW, 3) + " t";
document.getElementById("sumM").textContent = "= " + fmt(sumM, 3) + " t·m";
const KGnew = (sumW !== 0) ? (sumM / sumW) : 0;
const KM = parseNum(document.getElementById("km").value);
const GMnew = KM - KGnew;
document.getElementById("kgNew").textContent = fmt(KGnew, 3);
document.getElementById("gmNew").textContent = fmt(GMnew, 3);
document.getElementById("checkLine").textContent =
"KG = " + fmt(sumM, 3) + " / " + fmt(sumW, 3) + " = " + fmt(KGnew, 3);
calculateAdvanced();
}
// ---------- Advanced ----------
function calculateAdvanced(){
const adv = document.getElementById("advanced");
if (!adv.classList.contains("show")) return;
const startRow = document.querySelector("#tbody tr.row.locked");
const startKG = parseNum(startRow.querySelector(".kg").value);
const rows = document.querySelectorAll("#tbody tr.row");
let sumW = 0, sumM = 0;
rows.forEach(r=>{
const sign = parseNum(r.querySelector(".sign")?.value) || 1;
const wRaw = parseNum(r.querySelector(".w")?.value);
const kg = parseNum(r.querySelector(".kg")?.value);
const w = sign * wRaw;
sumW += w;
sumM += w * kg;
});
const KGnew = (sumW !== 0) ? (sumM / sumW) : 0;
const KM = parseNum(document.getElementById("km").value);
const GMnew = KM - KGnew;
const delta = KGnew - startKG;
const minGM = parseNum(document.getElementById("minGM").value);
document.getElementById("startKG").textContent = fmt(startKG, 3);
document.getElementById("deltaKG").textContent = fmt(delta, 3);
const statusEl = document.getElementById("gmStatus");
if (GMnew >= minGM){
statusEl.textContent = "OK (GM ≥ min)";
statusEl.className = "v mono status ok";
} else {
statusEl.textContent = "IKKE OK (GM < min)";
statusEl.className = "v mono status bad";
}
document.getElementById("formulaLine").textContent =
"ΣW=" + fmt(sumW,3) + " | ΣM=" + fmt(sumM,3) + " | KG=" + fmt(KGnew,3) + " | GM=" + fmt(GMnew,3);
}
function toggleAdvanced(){
const adv = document.getElementById("advanced");
const btn = document.getElementById("advToggleBtn");
const isOn = adv.classList.toggle("show");
btn.textContent = "Avansert: " + (isOn ? "PÅ" : "AV");
if (isOn) calculateAdvanced();
}
// ---------- Row management ----------
function renumberLoads(){
const rows = document.querySelectorAll("#tbody tr.row:not(.locked)");
let i = 1;
rows.forEach(r=>{
const desc = r.querySelector(".desc");
if (desc){
const v = (desc.value || "").trim();
if (!v || /^Lasting\s+#\d+$/i.test(v)){
desc.value = "Lasting #" + i;
}
}
i++;
});
}
function addLoadRow(){
const tbody = document.getElementById("tbody");
const totalsRow = tbody.querySelector("tr.totals");
const tr = document.createElement("tr");
tr.className = "row";
tr.innerHTML = `
<td class="label">
<input class="desc" value="Lasting #X" placeholder="Skriv navn (f.eks. Lossing/Lasting)" />
</td>
<td class="center">
<select class="sign">
<option value="1" selected>+</option>
<option value="-1">−</option>
</select>
</td>
<td class="right"><input class="w" value="0" inputmode="decimal"></td>
<td class="right"><input class="kg" value="0" inputmode="decimal"></td>
<td class="right mono moment">= 0,000</td>
<td class="center row-actions"><button type="button" class="delBtn">Slett</button></td>
`;
tbody.insertBefore(tr, totalsRow);
hookRow(tr);
renumberLoads();
calculateMain();
}
function hookRow(tr){
tr.querySelectorAll("input, select").forEach(el => el.addEventListener("input", calculateMain));
const del = tr.querySelector(".delBtn");
if (del){
del.addEventListener("click", () => {
tr.remove();
renumberLoads();
calculateMain();
});
}
}
function resetDefaults(){
const start = document.querySelector("#tbody tr.row.locked");
start.querySelector(".w").value = "570";
start.querySelector(".kg").value = "3";
document.querySelectorAll("#tbody tr.row:not(.locked)").forEach(r => r.remove());
addLoadRow();
const loads = document.querySelectorAll("#tbody tr.row:not(.locked)");
if (loads[0]){
loads[0].querySelector(".desc").value = "Lasting #1";
loads[0].querySelector(".sign").value = "1";
loads[0].querySelector(".w").value = "20";
loads[0].querySelector(".kg").value = "1,5";
}
addLoadRow();
const loads2 = document.querySelectorAll("#tbody tr.row:not(.locked)");
if (loads2[1]){
loads2[1].querySelector(".desc").value = "Lasting #2";
loads2[1].querySelector(".sign").value = "1";
loads2[1].querySelector(".w").value = "30";
loads2[1].querySelector(".kg").value = "3,9";
}
document.getElementById("km").value = "4,0";
document.getElementById("minGM").value = "0,15";
calculateMain();
}
// ---------- Horizontal digital clock ----------
function pad2(n){ return String(n).padStart(2,"0"); }
function updateVClock(){
const el = document.getElementById("vClock");
if (!el) return;
const now = new Date();
const t = `${pad2(now.getHours())}:${pad2(now.getMinutes())}:${pad2(now.getSeconds())}`;
el.textContent = t; // horisontal
}
updateVClock();
setInterval(updateVClock, 250);
function toggleTheme(){
const isPro = document.documentElement.getAttribute("data-theme") === "pro";
applyTheme(isPro ? "standard" : "pro");
}
// ---------- Init ----------
document.getElementById("addRowBtn").addEventListener("click", addLoadRow);
document.getElementById("resetBtn").addEventListener("click", resetDefaults);
document.getElementById("advToggleBtn").addEventListener("click", toggleAdvanced);
document.querySelectorAll("#tbody tr.row").forEach(hookRow);
document.querySelectorAll("#km, #minGM").forEach(inp => inp.addEventListener("input", calculateMain));
// last valgt tema
(function(){
const saved = localStorage.getItem("stabilityTheme");
applyTheme(saved === "pro" ? "pro" : "standard");
})();
// first calc
calculateMain();
</script>