`;
const posEntries = [];
techPlaced[tech].forEach(entry => {
const desc = escapeHtml(entry.beskrivelse || entry.description || 'Ingen beskrivelse');
const startObj = new Date(entry.start_tid);
let durMin = 30;
if (entry.faktisk_tid_min) durMin = parseInt(entry.faktisk_tid_min);
else if (entry.original_hours || entry.timer) durMin = Math.round(parseFloat(entry.original_hours || entry.timer) * 60);
let sH = startObj.getHours(), sM = startObj.getMinutes();
if (sH < START_HOUR) { durMin -= (START_HOUR * 60 - sH * 60 - sM); sH = START_HOUR; sM = 0; }
let topPx = ((sH - START_HOUR) + sM / 60) * HOUR_HEIGHT;
let heightPx = (durMin / 60) * HOUR_HEIGHT;
if (topPx < 0) topPx = 0;
if (topPx + heightPx > TOTAL_HOURS * HOUR_HEIGHT) heightPx = TOTAL_HOURS * HOUR_HEIGHT - topPx;
if (heightPx > 5 && topPx < TOTAL_HOURS * HOUR_HEIGHT) {
const endObj = new Date(startObj.getTime() + durMin * 60000);
const timeStr = `${String(startObj.getHours()).padStart(2,'0')}:${String(startObj.getMinutes()).padStart(2,'0')} \u2013 ${String(endObj.getHours()).padStart(2,'0')}:${String(endObj.getMinutes()).padStart(2,'0')}`;
posEntries.push({ topPx, heightPx, desc, timeStr, startMin: topPx, endMin: topPx + heightPx });
}
});
posEntries.sort((a, b) => a.startMin - b.startMin);
const lanes = [];
posEntries.forEach(e => {
let placed = false;
for (let li = 0; li < lanes.length; li++) {
if (lanes[li] <= e.startMin) { e.lane = li; lanes[li] = e.endMin; placed = true; break; }
}
if (!placed) { e.lane = lanes.length; lanes.push(e.endMin); }
});
const numLanes = lanes.length || 1;
posEntries.forEach(e => {
e.laneSpan = 1;
for (let li = e.lane + 1; li < numLanes; li++) {
if (!posEntries.some(o => o !== e && o.lane === li && o.startMin < e.endMin && o.endMin > e.startMin)) e.laneSpan++;
else break;
}
const lW = 100 / numLanes;
html += `
`;
});
html += `