{% extends "base.html" %}
{% block content %}
{% if locked_by_name %}
<div style="background:#e74c3c;color:#fff;padding:8px 16px;text-align:center;font-weight:600;font-size:0.85rem;">🔒 {{ locked_by_name }} bearbeitet dieses Aufmaß gerade – Sie haben nur Lesezugriff.</div>
{% endif %}
<div class="level mb-2">
    <div class="level-left">
        <h1 class="title is-4 mb-0">{{ aufmass.name }}</h1>
        {% if aufmass.typ %}<span class="tag is-info is-light ml-2">{{ aufmass.typ }}</span>{% endif %}
        <span class="tag is-medium {{ 'is-success' if aufmass.status == 'aktiv' else ('is-warning' if aufmass.status == 'abgeschlossen' else 'is-light') }} ml-1">{{ aufmass.status }}</span>
        <span class="ml-3 has-text-grey is-size-6">{{ project.bezeichnung or project.sm_nr }}</span>
    </div>
    <div class="level-right" style="display:flex;align-items:center;gap:4px">
        <button class="button is-small is-dark" id="btn-export-menu" onclick="toggleExportMenu()">📤 Export ▾</button>
        <div id="export-dropdown" style="display:none;position:absolute;z-index:1000;background:#fff;border:1px solid #ddd;border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);padding:8px;min-width:220px;margin-top:120px">
            <div style="padding:4px 6px;font-size:0.75rem;color:#888;border-bottom:1px solid #eee;margin-bottom:4px">
                <label style="display:flex;align-items:center;gap:6px;cursor:pointer">
                    <input type="checkbox" id="chk-aktuelle-ansicht"> Nur aktuelle Ansicht
                </label>
            </div>
            <div style="display:flex;flex-direction:column;gap:6px">
                <label style="display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 6px;border-radius:4px" onmouseover="this.style.background='#f5f5f5'" onmouseout="this.style.background=''">
                    <input type="checkbox" id="export-excel" onchange="updateExportZipButton()"> 📊 Excel
                </label>
                <label style="display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 6px;border-radius:4px" onmouseover="this.style.background='#f5f5f5'" onmouseout="this.style.background=''">
                    <input type="checkbox" id="export-pdf" onchange="updateExportZipButton()"> 📄 PDF
                </label>
                <label style="display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 6px;border-radius:4px" onmouseover="this.style.background='#f5f5f5'" onmouseout="this.style.background=''">
                    <input type="checkbox" id="export-txt" onchange="updateExportZipButton()"> 📄 TXT
                </label>
                <label style="display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 6px;border-radius:4px" onmouseover="this.style.background='#f5f5f5'" onmouseout="this.style.background=''">
                    <input type="checkbox" id="export-x31" onchange="updateExportZipButton()"> 📋 X31
                </label>
                <label style="display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 6px;border-radius:4px" onmouseover="this.style.background='#f5f5f5'" onmouseout="this.style.background=''">
                    <input type="checkbox" id="export-x31ca" onchange="updateExportZipButton()"> 📋 X31 California
                </label>
            </div>
            <div id="export-zip-section" style="display:none;margin-top:10px;padding-top:10px;border-top:1px solid #eee">
                <button class="button is-small is-success" style="width:100%" onclick="downloadZipExport()">📦 ZIP Download</button>
            </div>
        </div>
        <button class="button is-small is-info" id="btn-history-log" onclick="showHistoryLog()">📜 History</button>
        <a class="button is-small is-light" href="{{ url_for('aufmass.aufmass_list', project_id=project.id) }}">← Aufmaß-Übersicht</a>
    </div>
</div>

<div class="box mb-2" style="padding:0.4rem 0.75rem">
    <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
        {% set ns_vis = namespace(val=0) %}
        {% for mod in modules %}{% if ('module', mod.id|string) not in hidden_set and ('module', mod.id) not in hidden_set %}{% set ns_vis.val = ns_vis.val + 1 %}{% endif %}{% endfor %}
        {% for cm in custom_modules %}{% if ('custom', cm.id|string) not in hidden_set and ('custom', cm.id) not in hidden_set %}{% set ns_vis.val = ns_vis.val + 1 %}{% endif %}{% endfor %}
        <details id="zusatz-details" style="font-size:0.95rem">
            <summary style="cursor:pointer;font-weight:600;color:#2F5496;user-select:none">⚡ Zusatzmodule ({{ ns_vis.val }})</summary>
            <div id="modul-sortable" style="display:flex;flex-wrap:wrap;gap:4px;margin:4px 0 0 0">
                {% for mod in modules %}
                {% if ('module', mod.id|string) not in hidden_set and ('module', mod.id) not in hidden_set %}
                <a class="button is-small is-outlined js-mod-btn" style="color:#2F5496;cursor:grab" data-type="module" data-id="{{ mod.id }}" hx-get="{{ url_for('modules.formular', module_name=mod.name, aufmass_id=aufmass.id) }}"
                   hx-target="#modul-modal-body" hx-trigger="click[!event.ctrlKey]" hx-on::after-request="document.getElementById('modul-modal').classList.add('is-active');document.getElementById('modul-modal-title').textContent='{{ mod.icon }} {{ mod.titel }}';setTimeout(function(){restoreModulFormState()},0)">{{ mod.icon }} {{ mod.titel }}</a>
                {% endif %}
                {% endfor %}
                {% for cm in custom_modules %}
                {% if ('custom', cm.id|string) not in hidden_set and ('custom', cm.id) not in hidden_set %}
                <a class="button is-small is-outlined js-mod-btn" style="color:#7C3AED;background:rgba(124,58,237,0.06);border-color:#7C3AED;cursor:grab" data-type="custom" data-id="{{ cm.id }}" hx-get="{{ url_for('custom_modules.formular', module_id=cm.id, aufmass_id=aufmass.id) }}"
                   hx-target="#modul-modal-body" hx-trigger="click[!event.ctrlKey]" hx-on::after-request="document.getElementById('modul-modal').classList.add('is-active');document.getElementById('modul-modal-title').textContent='{{ cm.icon }} {{ cm.name }}';setTimeout(function(){restoreModulFormState()},0)">{{ cm.icon }} {{ cm.name }}</a>
                {% endif %}
                {% endfor %}
            </div>
        </details>
        <span id="batch-info" style="display:none;font-size:0.75rem;color:#555">
            <span id="sel-count">0</span> ausgewählt
            <button class="button is-small is-danger" id="btn-hide-selected" style="font-size:0.7rem;padding:2px 8px">Ausblenden</button>
            <button class="button is-small is-info" id="btn-show-selected" style="font-size:0.7rem;padding:2px 8px">Einblenden</button>
            <button class="button is-small is-light" id="btn-clear-selection" style="font-size:0.7rem;padding:2px 8px">✕</button>
        </span>
    </div>
        {% set ns = namespace(hc=0) %}
        {% for mod in modules %}{% if ('module', mod.id|string) in hidden_set or ('module', mod.id) in hidden_set %}{% set ns.hc = ns.hc + 1 %}{% endif %}{% endfor %}
        {% for cm in custom_modules %}{% if ('custom', cm.id|string) in hidden_set or ('custom', cm.id) in hidden_set %}{% set ns.hc = ns.hc + 1 %}{% endif %}{% endfor %}
        {% if ns.hc > 0 %}
        <details id="hidden-details" style="font-size:0.8rem;margin-top:4px">
            <summary style="cursor:pointer;color:#888;font-weight:600">👁 Ausgeblendete ({{ ns.hc }})</summary>
            <div id="modul-sortable-hidden" style="display:flex;flex-wrap:wrap;gap:4px;margin-top:4px">
            {% for mod in modules %}{% if ('module', mod.id|string) in hidden_set or ('module', mod.id) in hidden_set %}
                <a class="button is-small is-light is-outlined js-mod-btn" style="font-size:0.7rem;color:#2F5496;opacity:0.45;border-color:#2F5496" data-type="module" data-id="{{ mod.id }}">{{ mod.icon }} {{ mod.titel }}</a>
            {% endif %}{% endfor %}
            {% for cm in custom_modules %}{% if ('custom', cm.id|string) in hidden_set or ('custom', cm.id) in hidden_set %}
                <a class="button is-small is-light is-outlined js-mod-btn" style="font-size:0.7rem;color:#7C3AED;opacity:0.7;border-color:#7C3AED;background:rgba(124,58,237,0.08)" data-type="custom" data-id="{{ cm.id }}">{{ cm.icon }} {{ cm.name }}</a>
            {% endif %}{% endfor %}
            </div>
        </details>
        {% endif %}
</div>
<style>
#modul-sortable .js-mod-btn.selected,#modul-sortable-hidden .js-mod-btn.selected{outline:2px solid #3273dc;outline-offset:1px}
</style>

<details class="box mb-2" style="padding:0.4rem 0.75rem">
    <summary class="has-text-weight-bold has-text-link" style="cursor:pointer">📋 Kopfdaten</summary>
    <form method="POST" action="{{ url_for('aufmass.project_kopfdaten_save', project_id=project.id) }}" class="mt-2">
        <input type="hidden" name="aufmass_id" value="{{ aufmass.id }}">
        <div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;gap:4px 8px;font-size:0.8rem">
            <div style="grid-column:1/3"><label class="label is-small">Vertrag</label><input class="input is-small" name="vertrag" value="{{ project.vertrag or '' }}"></div>
            <div style="grid-column:3/5"><label class="label is-small">LV-Name</label><input class="input is-small" name="lv_name" value="{{ project.lv_name or '' }}"></div>
            <div style="grid-column:5/7"><label class="label is-small">Typ</label><input class="input is-small" name="typ" value="{{ aufmass.typ or '' }}"></div>
            <div style="grid-column:7/9"><label class="label is-small">Aufmaß-Datum</label><input class="input is-small" name="datum" type="date" value="{{ project.datum.strftime('%Y-%m-%d') if project.datum else '' }}"></div>
            <div style="grid-column:1/5"><label class="label is-small">Projekt</label><input class="input is-small" name="bezeichnung" value="{{ project.bezeichnung or '' }}"></div>
            <div style="grid-column:5/9"><label class="label is-small">Baustelle</label><input class="input is-small" name="baustelle" value="{{ project.baustelle or '' }}"></div>
            <div style="grid-column:9/13"><label class="label is-small">Bauabschnitt</label><input class="input is-small" name="bauabschnitt" value="{{ project.bauabschnitt or '' }}"></div>
            <div style="grid-column:1/4"><label class="label is-small">SM-Nr.</label><input class="input is-small" name="sm_nr" value="{{ project.sm_nr or '' }}"></div>
            <div style="grid-column:4/7"><label class="label is-small">Abruf-Nr.</label><input class="input is-small" name="abruf_nr" value="{{ project.abruf_nr or '' }}"></div>
            <div style="grid-column:7/10"><label class="label is-small">Startdatum</label><input class="input is-small" name="datum_start" type="date" value="{{ project.datum_start.strftime('%Y-%m-%d') if project.datum_start else '' }}"></div>
            <div style="grid-column:10/13"><label class="label is-small">Enddatum</label><input class="input is-small" name="datum_ende" type="date" value="{{ project.datum_ende.strftime('%Y-%m-%d') if project.datum_ende else '' }}"></div>
            <div style="grid-column:1/3"><label class="label is-small">Ansprechpartner Name</label><input class="input is-small" name="ansprechpartner_vorname" placeholder="Vorname" value="{{ project.ansprechpartner_vorname or '' }}"></div>
            <div style="grid-column:3/5"><input class="input is-small" name="ansprechpartner_nachname" placeholder="Nachname" value="{{ project.ansprechpartner_nachname or '' }}" style="margin-top:1.6rem"></div>
            <div style="grid-column:5/8"><label class="label is-small">Telefon</label><input class="input is-small" name="ansprechpartner_tel" value="{{ project.ansprechpartner_tel or '' }}"></div>
            <div style="grid-column:8/13"><label class="label is-small">Email</label><input class="input is-small" name="ansprechpartner_email" value="{{ project.ansprechpartner_email or '' }}"></div>
        </div>
        <div class="mt-2">
            <button class="button is-small is-primary" type="submit">Kopfdaten speichern</button>
        </div>
    </form>
</details>

<style>
    /* === LV-Tabelle links === */
    #lv-table{width:100%;table-layout:fixed;font-size:0.75rem;border-collapse:collapse}
    #lv-table thead{position:sticky;top:0;z-index:2}
    #lv-table thead tr{background:#fff;color:#333}
    #lv-table thead th{padding:5px 6px;font-weight:600;font-size:0.7rem;text-transform:uppercase;letter-spacing:0.5px;border:1px solid #ccc}
    #lv-table tbody td{border:1px solid #ddd;padding:4px 6px}
    #lv-table .lv-item{cursor:pointer}
    #lv-table .lv-item:active{cursor:grabbing}
    #lv-table .lv-item:hover{background:#f0f4ff}
    #lv-table .lv-item.is-selected{background:#cfe2ff;outline:2px solid #2F5496;outline-offset:-2px}
    #lv-table .lv-item.is-selected-multi{background:#e8f5e9}
    /* === Positionen-Tabelle rechts === */
    #pos-table{width:100%;font-size:0.75rem;border-collapse:collapse}
    #pos-table thead{position:sticky;top:0;z-index:2}
    #pos-table thead tr{background:#fff;color:#333}
    #pos-table thead th{padding:5px 6px;font-weight:600;font-size:0.65rem;text-transform:uppercase;letter-spacing:0.5px;border:1px solid #ccc}
    #pos-table tbody td{border:1px solid #ddd;padding:4px 6px}
    #pos-table tbody tr.is-selected-right{background:#cfe2ff;outline:2px solid #2F5496;outline-offset:-2px}
    #pos-table tbody tr:hover{background:#f0f4ff}
    /* === Formelart-Buttons (schwarze Schrift) === */
    .btn-formel{padding:2px 8px;font-size:0.7rem;border:1px solid #aaa;cursor:pointer;background:#fff;color:#000}
    .btn-formel.is-active{background:#cfe2ff;border-color:#2F5496;color:#000;font-weight:600}
    /* === Drag-Klon-Stil === */
    .sortable-ghost{opacity:0.4}
    .sortable-chosen{background:#d4e3ff}
    /* === Drag & Drop === */
    .lv-item.dragging{opacity:0.3}
    #pos-sortable.drop-active{outline:2px dashed #2F5496;outline-offset:-2px;background:#f0f7ff}
    /* === Leere Zeile (Trenner) – keine Inhalte === */
    #pos-sortable tr.is-trenner td{border-top:2px dashed #ccc;border-bottom:2px dashed #ccc;background:#f9f9f9;color:#bbb;height:20px}
    /* === Drop-Indicator (dicke rote Linie, keine Zeilen-Markierung) === */
    #drop-indicator{position:fixed;left:0;top:0;height:6px;background:#FF1744;pointer-events:none;z-index:10000;display:none;border-radius:3px;box-shadow:0 0 12px rgba(255,23,68,0.8),0 2px 4px rgba(0,0,0,0.3)}
    #drop-indicator::before{content:'';position:absolute;left:0;top:-7px;width:14px;height:20px;background:#FF1744;clip-path:polygon(0 50%,100% 0,100% 100%);border-radius:1px}
    #drop-indicator::after{content:'';position:absolute;right:0;top:-7px;width:14px;height:20px;background:#FF1744;clip-path:polygon(100% 50%,0 0,0 100%);border-radius:1px}
    /* === Number-Spinner ausblenden === */
    input[type=number].hide-spinner::-webkit-outer-spin-button,
    input[type=number].hide-spinner::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
    input[type=number].hide-spinner{-moz-appearance:textfield}
    /* === Spalten-Resize === */
    .resize-handle{position:absolute;top:0;right:0;width:4px;height:100%;cursor:col-resize;z-index:3;opacity:0;pointer-events:none}
    .resize-hit{position:absolute;top:0;right:-5px;width:10px;height:100%;cursor:col-resize;z-index:3;opacity:0}
    .resize-hit:hover ~ .resize-handle,.resize-handle.dragging{opacity:1;background:#2F5496}
    #pos-table thead th,#lv-table thead th{position:relative}
    /* === LV-Filter (wie POS-Filter) === */
    #lv-table thead th .fbtn{margin-left:2px;font-size:0.6rem;opacity:0.4;cursor:pointer}
    #lv-table thead th:hover .fbtn{opacity:0.8}
    #lv-table thead th .fbtn.active{opacity:1;color:#2F5496;font-weight:bold}
    #lv-table tbody tr.filter-hidden{display:none}
    /* === Spalten-Filter === */
    #pos-table thead th .fbtn{margin-left:2px;font-size:0.6rem;opacity:0.4;cursor:pointer}
    #pos-table thead th:hover .fbtn{opacity:0.8}
    #pos-table thead th .fbtn.active{opacity:1;color:#2F5496;font-weight:bold}
    #filter-checkboxes label{display:flex;align-items:center;gap:4px;padding:2px 4px;cursor:pointer;border-radius:3px}
    #filter-checkboxes label:hover{background:#f0f4ff}
    #filter-checkboxes input[type=checkbox]{margin:0}
    #pos-table tbody tr.filter-hidden{display:none}
</style>
<div id="am-layout" style="display:flex;gap:0;height:calc(100vh - 240px);overflow:hidden">
    <!-- === LINKS: LV-Auswahl + Eingabe-Group + Langtext === -->
    <div id="am-left" style="width:40%;min-width:360px;display:flex;flex-direction:column;background:#fff;border:1px solid #ddd;border-radius:4px;overflow:hidden;flex-shrink:0">
        <!-- Suche -->
        <div style="padding:5px 6px;background:#f0f2f5;border-bottom:1px solid #ddd;flex-shrink:0">
            <div style="display:flex;align-items:center;gap:4px;margin-bottom:2px">
                {% if project.lv_name %}
                <span style="margin-left:auto;font-size:0.65rem;color:#888">LV: {{ project.lv_name }}</span>
                {% elif lv_names %}
                <form method="POST" action="{{ url_for('aufmass.project_lv_set', project_id=project.id) }}" style="margin-left:auto;display:flex;gap:3px;align-items:center">
                    <span style="font-size:0.65rem;color:#888">LV:</span>
                    <select name="lv_name" class="is-small" style="font-size:0.65rem;padding:1px 4px;max-width:150px">
                        <option value="">– wählen –</option>
                        {% for n in lv_names %}
                        <option value="{{ n }}">{{ n }}</option>
                        {% endfor %}
                    </select>
                    <button class="button is-small is-link" style="font-size:0.6rem;padding:1px 6px">Laden</button>
                </form>
                {% endif %}
            </div>
            <input class="input is-small" id="lv-search" placeholder="Suche in LV-Positionen..." autocomplete="off" style="width:100%">
        </div>
        <!-- Multi-Select Toolbar -->
        <div style="display:flex;align-items:center;gap:4px;padding:2px 6px;background:#f5f5f5;border-bottom:1px solid #ddd;flex-shrink:0;font-size:0.65rem">
            <span id="lv-count" style="color:#888;cursor:pointer" onclick="toggleAllLV()">0 ausgewählt</span>
            <span style="color:#bbb">|</span>
            <span style="color:#888">{{ lv_positionen|length }} Pos.</span>
            <span style="color:#bbb;margin:0 4px;font-size:0.6rem;color:#999">Ctrl+Klick → Mehrfach | Shift+Klick → Bereich</span>
            <button class="button is-small is-link" style="font-size:0.6rem;padding:1px 6px;margin-left:auto" onclick="addSelectedLV()">+ Auswahl hinzufügen</button>
        </div>
        <!-- LV-Liste als Tabelle (höhenverstellbar per Divider) -->
        <div id="lv-table-wrap" style="flex-shrink:0;height:400px;min-height:80px;overflow-y:auto;padding:0">
            <table id="lv-table">
                <thead>
                    <tr>
                        <th style="width:80px;cursor:pointer" data-field="posnr" onclick="showLVFilterMenu('posnr',this)">Pos-Nr <span class="fbtn">▾</span></th>
                        <th style="cursor:pointer" data-field="text" onclick="showLVFilterMenu('text',this)">Kurztext <span class="fbtn">▾</span></th>
                        <th style="width:50px;cursor:pointer" data-field="eh" onclick="showLVFilterMenu('eh',this)">EH <span class="fbtn">▾</span></th>
                        <th style="width:75px;cursor:pointer" data-field="ep" onclick="showLVFilterMenu('ep',this)">EP (€) <span class="fbtn">▾</span></th>
                    </tr>
                </thead>
                <tbody id="lv-list">
                    {% if lv_positionen %}
                    {% for lp in lv_positionen %}
                    <tr class="lv-item" data-id="{{ lp.id }}" data-posnr="{{ lp.pos_nr }}" data-text="{{ lp.kurztext or '' }}"
                        data-lang="{{ lp.langtext or '' }}" data-eh="{{ lp.einheit }}" data-ep="{{ lp.einzelpreis }}"
                        data-rsa="{{ lp.rsa or '' }}" data-abschnitt="{{ lp.abschnitt or '' }}"
                        draggable="true">
                        <td><code style="font-size:0.7rem">{{ lp.pos_nr }}</code></td>
                        <td style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ lp.kurztext or '' }}</td>
                        <td style="text-align:right;color:#666">{{ lp.einheit }}</td>
                        <td style="text-align:right;white-space:nowrap">{{ lp.einzelpreis|german_number }} €</td>
                    </tr>
                    {% endfor %}
                    {% else %}
                    <tr><td colspan="4" style="text-align:center;padding:20px;color:#999;font-size:0.75rem">
                        {% if project.lv_name %}
                        Keine Positionen für LV "{{ project.lv_name }}" gefunden.
                        {% else %}
                        Kein LV zugewiesen. Wählen Sie oben ein LV aus.
                        {% endif %}
                    </td></tr>
                    {% endif %}
                </tbody>
            </table>
        </div>
        <!-- Horizontaler Divider zwischen LV-Tabelle und Eingabe -->
        <div id="lv-divider" style="height:5px;cursor:row-resize;background:#e8e8e8;flex-shrink:0"></div>
        <!-- Eingabe-Group + Langtext (füllt Resthöhe, Langtext wächst mit) -->
        <div style="flex:1;display:flex;flex-direction:column;border-top:2px solid #2F5496;background:#fafafa;min-height:0">
            <div style="flex-shrink:0;padding:5px 6px 4px">
                <div style="font-size:0.65rem;font-weight:700;color:#2F5496;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:3px">Eingabe</div>
                <input type="hidden" id="form-lv-id">
                <div class="columns is-mobile is-variable is-1 mb-1" style="margin-bottom:2px">
                    <div class="column is-6" style="padding:0 2px"><label class="is-size-7 has-text-grey">PosNr</label><input class="input is-small" id="form-posnr" placeholder="PosNr"></div>
                    <div class="column is-6" style="padding:0 2px"><label class="is-size-7 has-text-grey">Kurztext</label><input class="input is-small" id="form-kurztext" placeholder="Kurztext"></div>
                </div>
                <div class="field mb-1" style="margin-bottom:2px"><label class="is-size-7 has-text-grey">RSA-Abschnitt</label>
                    <input class="input is-small" id="form-abschnitt" placeholder="z.B. 2.1.3">
                </div>
                <div class="field mb-1" style="margin-bottom:2px"><label class="is-size-7 has-text-grey">Formelart</label>
                    <div style="display:flex;gap:0">
                        <a class="btn-formel is-active" id="btn-std" onclick="setFormelTyp('standard')">Standard</a>
                        <a class="btn-formel" id="btn-frei" onclick="setFormelTyp('frei')">Freie Formel (Z91)</a>
                    </div>
                </div>
                <div id="form-std">
                    <div class="columns is-mobile is-variable is-1 mb-0" style="margin-bottom:1px">
                        <div class="column" style="padding:0 2px"><label class="is-size-7 has-text-grey">Faktor</label><input class="input is-small" id="form-faktor" type="text" inputmode="decimal" value="1,0" oninput="sanitizeNum(this);berechneStd()"></div>
                        <div class="column" style="padding:0 2px" id="field-laenge"><label class="is-size-7 has-text-grey">Länge (m)</label><input class="input is-small" id="form-laenge" type="text" inputmode="decimal" value="" oninput="sanitizeNum(this);berechneStd()"></div>
                        <div class="column" style="padding:0 2px" id="field-breite"><label class="is-size-7 has-text-grey">Breite (m)</label><input class="input is-small" id="form-breite" type="text" inputmode="decimal" value="" oninput="sanitizeNum(this);berechneStd()"></div>
                        <div class="column" style="padding:0 2px" id="field-tiefe"><label class="is-size-7 has-text-grey">Tiefe (m)</label><input class="input is-small" id="form-tiefe" type="text" inputmode="decimal" value="" oninput="sanitizeNum(this);berechneStd()"></div>
                        <div class="column" style="padding:0 2px"><label class="is-size-7 has-text-grey">Menge</label><input class="input is-small" id="form-menge" type="text" inputmode="decimal" value="" readonly></div>
                    </div>
                </div>
                <div id="form-frei" style="display:none">
                    <div class="field mb-0" style="margin-bottom:1px"><label class="is-size-7 has-text-grey">Formel (z.B. 2*1+1)</label>
                        <input class="input is-small" id="form-formel" placeholder="2*1+1" oninput="berechneFrei()">
                    </div>
                    <div class="field mb-0" style="margin-bottom:1px"><label class="is-size-7 has-text-grey">Menge (berechnet)</label>
                        <input class="input is-small" id="form-menge-frei" type="text" inputmode="decimal" value="" readonly style="background:#eee">
                    </div>
                </div>
                <div class="field mb-0" style="margin-bottom:2px"><label class="is-size-7 has-text-grey">Bemerkung</label>
                    <input class="input is-small" id="form-bemerkung" placeholder="Bemerkung">
                </div>
                <div style="display:flex;gap:4px;margin-top:3px">
                    <button class="button is-small is-primary" onclick="addMitFormular()">+ Hinzufügen</button>
                    <label style="font-size:0.7rem;cursor:pointer;display:flex;align-items:center;gap:3px;white-space:nowrap">
                        <input type="checkbox" id="chk-empty-end" checked>
                        Leere Zeile am Ende anhängen
                    </label>
                    <button class="button is-small is-light" draggable="true" id="btn-leere-zeile" ondragstart="event.dataTransfer.setData('text/plain','__LEERE_ZEILE__');event.dataTransfer.effectAllowed='all'" title="Ziehen zum Einfügen in die Positionstabelle">+ Leere Zeile</button>
                </div>
            </div>
            <div style="flex:1;border-top:1px solid #ddd;padding:3px 6px 4px;overflow-y:scroll;background:#fff;height:0">
                <div style="font-size:0.65rem;font-weight:600;color:#888;margin-bottom:1px;display:flex;align-items:center;justify-content:space-between">
                    <span>Langtext</span>
                    <a id="btn-lang-panel" style="cursor:pointer;font-size:0.7rem;color:#2F5496;display:none;white-space:nowrap" onclick="toggleLangPanel()" title="Langtext im rechten Sidepanel öffnen/schließen">≫ Langtext</a>
                </div>
                <div id="form-langtext" style="font-size:0.75rem;color:#555;white-space:pre-wrap;line-height:1.3"></div>
            </div>
        </div>
    </div>

    <!-- Resizable Divider -->
    <div id="am-divider" style="width:5px;cursor:col-resize;background:#e8e8e8;flex-shrink:0"></div>

    <!-- === RECHTS: Aufmaß-Tabelle === -->
    <div id="am-right" style="flex:1;min-width:500px;display:flex;flex-direction:column;background:#fff;border:1px solid #ddd;border-radius:4px;overflow:hidden">
        <div style="padding:4px 8px;background:#f0f2f5;border-bottom:1px solid #ddd;flex-shrink:0;display:flex;align-items:center;justify-content:space-between">
            <div style="display:flex;align-items:center;gap:6px">
                <span class="has-text-weight-bold is-size-7">Positionen ({{ positionen|length }})</span>
                <button class="button is-small is-warning" id="btn-undo" onclick="doUndo()" title="Rückgängig (Strg+Z)" disabled style="padding:2px 6px;font-size:0.75rem">↩</button>
                <button class="button is-small is-warning" id="btn-redo" onclick="doRedo()" title="Wiederholen (Strg+Y)" disabled style="padding:2px 6px;font-size:0.75rem">↪</button>
            </div>
            <span id="right-count" style="font-size:0.6rem;color:#888;min-width:80px;text-align:center"></span>
            <span class="has-text-weight-bold is-size-7">Gesamt: <span id="summe-gp">{{ positionen|sum(attribute='gesamtpreis')|german_number }}</span> €</span>
        </div>
        <div style="flex:1;overflow-y:auto;padding:2px;min-height:490px">
            <table id="pos-table">
                <thead>
                    <tr>
                        <th style="width:45px" data-field="sort">Zeile</th>
                        <th style="width:40px">Z-Art</th>
                        <th style="width:80px;cursor:pointer" data-field="abschnitt" onclick="showFilterMenu('abschnitt',this)">Abschnitt <span class="fbtn">▾</span></th>
                        <th style="width:65px;cursor:pointer" data-field="posnr" onclick="showFilterMenu('posnr',this)">Pos-Nr <span class="fbtn">▾</span></th>
                        <th style="width:45px;cursor:pointer" data-field="faktor" onclick="showFilterMenu('faktor',this)">Faktor <span class="fbtn">▾</span></th>
                        <th style="width:50px;cursor:pointer" data-field="laenge" onclick="showFilterMenu('laenge',this)">Länge <span class="fbtn">▾</span></th>
                        <th style="width:50px;cursor:pointer" data-field="breite" onclick="showFilterMenu('breite',this)">Breite <span class="fbtn">▾</span></th>
                        <th style="width:50px;cursor:pointer" data-field="tiefe" onclick="showFilterMenu('tiefe',this)">Tiefe <span class="fbtn">▾</span></th>
                        <th style="width:55px;cursor:pointer" data-field="menge" onclick="showFilterMenu('menge',this)">Menge <span class="fbtn">▾</span></th>
                        <th style="width:30px;cursor:pointer" data-field="einheit" onclick="showFilterMenu('einheit',this)">EH <span class="fbtn">▾</span></th>
                        <th style="min-width:80px;cursor:pointer" data-field="kurztext" onclick="showFilterMenu('kurztext',this)">Kurztext <span class="fbtn">▾</span></th>
                        <th style="width:60px;cursor:pointer" data-field="bemerkung" onclick="showFilterMenu('bemerkung',this)">Bemerkung <span class="fbtn">▾</span></th>
                        <th style="width:55px;cursor:pointer" data-field="menge-hinten" onclick="showFilterMenu('menge-hinten',this)">Menge <span class="fbtn">▾</span></th>
                        <th style="width:55px;cursor:pointer" data-field="ep" onclick="showFilterMenu('ep',this)">EP (€) <span class="fbtn">▾</span></th>
                        <th style="width:60px;cursor:pointer" data-field="gp" onclick="showFilterMenu('gp',this)">GP (€) <span class="fbtn">▾</span></th>
                        <th style="width:40px">✕</th>
                    </tr>
                </thead>
                <tbody id="pos-sortable">
                    {% for pos in positionen %}
                    {% set empty = pos.pos_nr == '' and pos.faktor == 0 and pos.laenge == 0 and pos.breite == 0 and pos.tiefe == 0 and pos.menge == 0 and pos.einzelpreis == 0 and pos.gesamtpreis == 0 %}
                    <tr data-id="{{ pos.id }}" data-sort="{{ pos.sortierung }}"
                        data-posnr="{{ pos.pos_nr }}" data-kurztext="{{ pos.kurztext or '' }}"
                        data-langtext="{{ pos.langtext or '' }}" data-einheit="{{ pos.einheit }}"
                        data-ep="{{ pos.einzelpreis }}" data-faktor="{{ pos.faktor }}"
                        data-laenge="{{ pos.laenge }}" data-breite="{{ pos.breite }}" data-tiefe="{{ pos.tiefe }}"
                        data-menge="{{ pos.menge }}" data-gp="{{ pos.gesamtpreis }}"
                        data-menge-hinten="{{ pos.menge_hinten or 0 }}"
                        data-bemerkung="{{ pos.bemerkung or '' }}" data-rsa="{{ pos.rsa or '' }}"
                        data-abschnitt="{{ pos.abschnitt or '' }}"
                        data-formel-typ="{{ pos.formel_typ or 'standard' }}" data-formel="{{ pos.formel or '' }}"
                        draggable="true"{% if empty %} class="is-trenner"{% endif %}>
                        <td>{{ pos.sortierung }}</td>
                        <td data-field="formel_typ"><span class="tag is-light is-small" style="font-size:0.65rem">{{ 'Z91' if pos.formel_typ=='frei' else 'Std' if not empty }}</span></td>
                        <td style="font-size:0.7rem" data-field="abschnitt">{{ pos.abschnitt or '' }}</td>
                        <td><code style="font-size:0.7rem">{{ pos.pos_nr }}</code></td>
                        {% if empty %}
                        <td colspan="11" style="text-align:center;color:#ccc;font-style:italic;padding:4px 0">– Leere Zeile –</td>
                        <td style="text-align:center">
                            <form method="POST" action="{{ url_for('aufmass.position_loeschen', project_id=project.id, aufmass_id=aufmass.id, pos_id=pos.id) }}"
                                  style="display:inline" onsubmit="return confirm('Löschen?')">
                                <button class="button is-small is-danger is-outlined" style="font-size:0.65rem;padding:0 4px;border:none">✕</button>
                            </form>
                        </td>
                        {% else %}
                        <td style="text-align:right" data-field="faktor">{{ '–' if pos.formel_typ=='frei' else pos.faktor|german_number }}</td>
                        {% if pos.formel_typ=='frei' %}
                        <td colspan="3" style="text-align:center;font-family:monospace;font-size:0.75rem;background:#fafafa" data-field="formel" title="Formel: {{ pos.formel }}">{{ pos.formel or '–' }}</td>
                        {% else %}
                        <td style="text-align:right" data-field="laenge" class="{{ 'has-background-warning-light' if (pos.einheit in ('M','M2','M3') and pos.laenge == 0) }}">{{ pos.laenge|german_number(zero_dash=True) }}</td>
                        <td style="text-align:right" data-field="breite" class="{{ 'has-background-warning-light' if (pos.einheit in ('M2','M3') and pos.breite == 0) }}">{{ pos.breite|german_number(zero_dash=True) }}</td>
                        <td style="text-align:right" data-field="tiefe" class="{{ 'has-background-warning-light' if (pos.einheit == 'M3' and pos.tiefe == 0) }}">{{ pos.tiefe|german_number(zero_dash=True) }}</td>
                        {% endif %}
                        <td style="text-align:right" data-field="menge"><strong data-cell="menge">{{ pos.menge|german_number(3, zero_dash=True) }}</strong></td>
                        <td data-field="einheit">{{ pos.einheit }}</td>
                        <td style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:120px" data-field="kurztext" title="{{ pos.kurztext or '' }}">{{ pos.kurztext or '' }}</td>
                        <td style="font-size:0.7rem;max-width:80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" data-field="bemerkung">{{ pos.bemerkung or '' }}</td>
                        <td style="text-align:right" data-field="menge_hinten">{{ pos.menge_hinten|german_number(zero_dash=True) }}</td>
                        <td style="text-align:right;white-space:nowrap">{{ pos.einzelpreis|german_number }}</td>
                        <td style="text-align:right;white-space:nowrap"><strong data-cell="gp">{{ pos.gesamtpreis|german_number }}</strong></td>
                        <td>
                            <a style="cursor:pointer;font-size:0.8rem" onclick="openEdit({{ pos.id }})">✎</a>
                            <form method="POST" action="{{ url_for('aufmass.position_loeschen', project_id=project.id, aufmass_id=aufmass.id, pos_id=pos.id) }}"
                                  style="display:inline" onsubmit="return confirm('Löschen?')">
                                <button class="button is-small is-danger is-outlined" style="font-size:0.65rem;padding:0 4px;border:none">✕</button>
                            </form>
                        </td>
                        {% endif %}
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
            {% if not positionen %}
            <p class="has-text-grey has-text-centered is-size-7 mt-3">Keine Positionen. Wählen Sie links aus dem LV und klicken Hinzufügen.</p>
            {% endif %}
        </div>
        <!-- Filter-Dropdown für Spalten -->
        <div id="filter-menu" style="position:fixed;display:none;z-index:10000;background:#fff;border:1px solid #bbb;border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,.2);padding:8px;min-width:220px;max-width:300px;font-size:0.8rem">
            <div style="display:flex;gap:2px;margin-bottom:6px">
                <button class="button is-small is-light" onclick="filterSort('asc')" style="font-size:0.75rem;flex:1" title="Aufsteigend sortieren">↑ Aufst.</button>
                <button class="button is-small is-light" onclick="filterSort('desc')" style="font-size:0.75rem;flex:1" title="Absteigend sortieren">↓ Abst.</button>
                <button class="button is-small is-light" onclick="filterSort(null)" style="font-size:0.75rem;flex:0.6" title="Sortierung zurücksetzen">✕</button>
            </div>
            <input id="filter-search" type="text" placeholder="Suchen…" style="width:100%;box-sizing:border-box;padding:4px 6px;border:1px solid #ccc;border-radius:4px;font-size:0.8rem;margin-bottom:4px" oninput="filterSearch(this.value)">
            <div id="filter-values" style="max-height:180px;overflow-y:auto;border-top:1px solid #eee;margin-top:4px;padding-top:4px">
                <div style="display:flex;gap:4px;margin-bottom:4px">
                    <button class="button is-small is-light" onclick="filterSelectAll()" style="font-size:0.7rem;flex:1;padding:0 4px">Alles</button>
                    <button class="button is-small is-light" onclick="filterSelectNone()" style="font-size:0.7rem;flex:1;padding:0 4px">Nichts</button>
                </div>
                <div id="filter-checkboxes"></div>
            </div>
        </div>
        <!-- Context-Menu für rechte Tabelle -->
        <div id="pos-context-menu" style="position:fixed;display:none;z-index:9999;background:#fff;border:1px solid #ccc;border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);padding:4px 0;min-width:190px;font-size:0.85rem">
            <div class="ctx-item" onclick="openMultiEdit();hideContextMenu()" style="padding:7px 14px;cursor:pointer;display:flex;align-items:center;gap:8px" onmouseenter="this.style.background='#f0f2f5'" onmouseleave="this.style.background=''">
                <span style="font-size:1rem">✏️</span><span>Markierte bearbeiten</span>
            </div>
            <div class="ctx-item" onclick="copySelectedPos();hideContextMenu()" style="padding:7px 14px;cursor:pointer;display:flex;align-items:center;gap:8px" onmouseenter="this.style.background='#f0f2f5'" onmouseleave="this.style.background=''">
                <span style="font-size:1rem">📋</span><span>Markierte kopieren</span>
            </div>
            <div class="ctx-item" onclick="deleteSelectedPos();hideContextMenu()" style="padding:7px 14px;cursor:pointer;display:flex;align-items:center;gap:8px" onmouseenter="this.style.background='#f0f2f5'" onmouseleave="this.style.background=''">
                <span style="font-size:1rem">✕</span><span>Markierte löschen</span>
            </div>
            <div style="border-top:1px solid #eee;margin:2px 0"></div>
            <div class="ctx-item" onclick="addLeereZeile();hideContextMenu()" style="padding:7px 14px;cursor:pointer;display:flex;align-items:center;gap:8px" onmouseenter="this.style.background='#f0f2f5'" onmouseleave="this.style.background=''">
                <span style="font-size:1rem">📄</span><span>Leere Zeile einfügen</span>
            </div>
        </div>
        <!-- Untere Leiste: Checkbox + Buttons -->
        <div style="flex-shrink:0;padding:4px 8px;background:#f0f2f5;border-top:1px solid #ddd;display:flex;align-items:center;gap:6px">
            <button class="button is-danger is-outlined" style="height:36px;font-size:0.8rem" onclick="deleteSelectedPos()" title="Markierte Einträge löschen">✕ Markierte löschen</button>
            <button class="button is-link is-outlined" style="height:36px;font-size:0.8rem" onclick="copySelectedPos()" title="Markierte Einträge kopieren">📋 Markierte kopieren</button>
            <button class="button is-link is-outlined" style="height:36px;font-size:0.8rem" onclick="document.getElementById('pos-import-file').click()" title="Positionen aus TXT-Datei importieren">📥 Import TXT</button>
            <form method="POST" action="{{ url_for('aufmass.positionen_import_txt', project_id=project.id, aufmass_id=aufmass.id) }}" enctype="multipart/form-data" style="display:none">
                <input type="file" name="file" accept=".txt" id="pos-import-file" onchange="this.form.submit()">
            </form>
            <span style="flex:1"></span>
            <label style="font-size:0.75rem;cursor:pointer;display:flex;align-items:center;gap:4px">
                <input type="checkbox" id="chk-anfuegen" checked>
                Am Ende anfügen
            </label>
            <button class="button is-light is-small" id="btn-reset-filter" onclick="filterReset()" style="height:36px;font-size:0.75rem" title="Filter zurücksetzen">↺ Filter zurücksetzen</button>
            <span style="border-left:1px solid #ccc;height:22px"></span>
            <button class="button is-danger" style="height:36px;font-size:0.8rem" onclick="deleteAllPos()">🗑 Tabelle löschen</button>
        </div>
    </div>
    <!-- Langtext Sidepanel Divider -->
    <div id="lang-divider" style="width:5px;cursor:col-resize;background:#e8e8e8;flex-shrink:0;display:none"></div>
    <!-- Langtext Sidepanel (rechts) -->
    <div id="am-lang-panel" style="width:380px;min-width:28px;display:none;background:#fff;border:1px solid #ddd;border-radius:4px;overflow:hidden;flex-shrink:0;position:relative">
        <!-- Vertical Langtext label + <<-Button (sichtbar im collapsed Zustand) -->
        <div id="lang-collapsed" style="display:flex;flex-direction:column;align-items:center;padding-top:12px;width:28px;background:#f0f2f5;border-right:1px solid #ddd;border-radius:4px 0 0 4px;cursor:pointer;position:absolute;left:0;top:0;bottom:0;z-index:5" onclick="toggleLangPanel()">
            <span style="writing-mode:vertical-rl;text-orientation:mixed;font-size:0.65rem;font-weight:700;color:#2F5496;letter-spacing:2px;transform:rotate(180deg)">Langtext</span>
            <span style="font-size:0.75rem;color:#2F5496;margin-top:6px">≪</span>
        </div>
        <!-- Expanded content -->
        <div id="lang-expanded" style="display:none;flex-direction:column;height:100%">
            <div style="display:flex;align-items:center;padding:4px 8px;background:#f0f2f5;border-bottom:1px solid #ddd;flex-shrink:0">
                <span style="font-size:0.65rem;font-weight:700;color:#2F5496;text-transform:uppercase;letter-spacing:0.5px;cursor:pointer" onclick="toggleLangPanel()" title="Einklappen">Langtext</span>
                <span style="flex:1"></span>
                <a style="cursor:pointer;font-size:0.75rem;color:#888;padding:2px 4px" onclick="toggleLangPanel()" title="Einklappen">≫</a>
                <a style="cursor:pointer;font-size:0.75rem;color:#888;padding:2px 4px;margin-left:2px" onclick="closeLangPanel()" title="Schließen">✕</a>
            </div>
            <div id="lang-panel-content" style="flex:1;padding:6px 8px;overflow-y:scroll;font-size:0.85rem;white-space:pre-wrap;line-height:1.4"></div>
        </div>
    </div>
</div>

<div id="pos-edit-modal" class="modal">
    <div class="modal-background" onclick="closeEdit()"></div>
    <div class="modal-card">
        <header class="modal-card-head"><p class="modal-card-title">Position bearbeiten</p><button class="delete" onclick="closeEdit()"></button></header>
        <section class="modal-card-body" id="pos-edit-content"></section>
    </div>
</div>

<!-- Multi-Edit Modal -->
<div id="multi-edit-modal" class="modal">
    <div class="modal-background" onclick="closeMultiEdit()"></div>
        <div class="modal-card" style="width:98vw;max-width:none">
        <header class="modal-card-head"><p class="modal-card-title">✏️ Markierte Positionen bearbeiten</p><button class="delete" onclick="closeMultiEdit()"></button></header>
        <section class="modal-card-body" id="multi-edit-body" style="max-height:70vh;overflow:auto;padding:0">
            <div id="multi-edit-content">Lade…</div>
        </section>
        <footer class="modal-card-foot" style="justify-content:flex-end;gap:8px">
            <button class="button is-light" onclick="closeMultiEdit()">Abbrechen</button>
            <button class="button is-primary" id="btn-multi-save" onclick="saveMultiEdit()">Speichern</button>
        </footer>
    </div>
</div>

<!-- History Log Modal -->
<div class="modal" id="history-log-modal">
    <div class="modal-background" onclick="document.getElementById('history-log-modal').classList.remove('is-active')"></div>
    <div class="modal-card" style="width:95%;max-width:1100px">
        <header class="modal-card-head">
            <p class="modal-card-title">📜 Änderungshistory</p>
            <button class="delete" onclick="document.getElementById('history-log-modal').classList.remove('is-active')"></button>
        </header>
        <section class="modal-card-body" style="max-height:75vh;overflow-y:auto">
            <div id="history-log-list" style="font-size:0.8rem"></div>
        </section>
        <footer class="modal-card-foot">
            <button class="button" onclick="document.getElementById('history-log-modal').classList.remove('is-active')">Schließen</button>
            <button class="button is-info" onclick="fetchHistoryLog()">↻ Aktualisieren</button>
        </footer>
    </div>
</div>
{% endblock %}

{% block scripts %}
<script>
const PROJECT_ID = {{ project.id }};
const AUFMASS_ID = {{ aufmass.id }};
let currentLVId = null;
let currentEinheit = '';
let formelTyp = 'standard';

/* === History/Undo/Redo - muss VOR saveMultiEdit definiert sein === */
var _undoStack = [];
var _redoStack = [];
var MAX_UNDO = 10;

// Call loadHistoryCache when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
    loadHistoryCache();
});

function saveHistoryCache() {
    var key = 'aufmass_history_' + AUFMASS_ID;
    try {
        localStorage.setItem(key, JSON.stringify({undo: _undoStack, redo: _redoStack}));
    } catch(e) {}
}

function loadHistoryCache() {
    var key = 'aufmass_history_' + AUFMASS_ID;
    try {
        var cached = localStorage.getItem(key);
        if (cached) {
            var data = JSON.parse(cached);
            _undoStack = data.undo || [];
            _redoStack = data.redo || [];
        }
    } catch(e) {}
    updateUndoRedoButtons();
}

function updateUndoRedoButtons() {
    var undoBtn = document.getElementById('btn-undo');
    var redoBtn = document.getElementById('btn-redo');
    if (undoBtn) undoBtn.disabled = _undoStack.length === 0;
    if (redoBtn) redoBtn.disabled = _redoStack.length === 0;
}

function pushUndoState(description, diff) {
    _undoStack.push({description: description, diff: diff, time: new Date().toISOString()});
    if (_undoStack.length > MAX_UNDO) {
        _undoStack.shift();
    }
    _redoStack = [];
    saveHistoryCache();
    updateUndoRedoButtons();
}

function doUndo() {
    if (_undoStack.length === 0) {
        alert('Nichts zum Rückgängig machen.');
        return;
    }
    var state = _undoStack.pop();
    _redoStack.push(state);
    saveHistoryCache();
    var btn = document.getElementById('btn-undo');
    if (btn) btn.classList.add('is-loading');
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/undo', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({})
    }).then(function(r){ return r.json(); }).then(function(data){
        if (data.error) {
            alert('Fehler: ' + data.error);
            _undoStack.push(state);
            _redoStack.pop();
            saveHistoryCache();
        } else {
            location.href = location.pathname + '?t=' + Date.now();
        }
    }).catch(function(e){
        alert('Fehler: ' + e.message);
        _undoStack.push(state);
        _redoStack.pop();
        saveHistoryCache();
    }).finally(function(){
        var btn = document.getElementById('btn-undo');
        if (btn) btn.classList.remove('is-loading');
    });
    updateUndoRedoButtons();
}

function doRedo() {
    if (_redoStack.length === 0) {
        alert('Nichts zum Wiederholen.');
        return;
    }
    var state = _redoStack.pop();
    _undoStack.push(state);
    saveHistoryCache();
    var btn = document.getElementById('btn-redo');
    if (btn) btn.classList.add('is-loading');
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/redo', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({})
    }).then(function(r){ return r.json(); }).then(function(data){
        if (data.error) {
            alert('Fehler: ' + data.error);
            _undoStack.pop();
            _redoStack.push(state);
            saveHistoryCache();
        } else {
            location.href = location.pathname + '?t=' + Date.now();
        }
    }).catch(function(e){
        alert('Fehler: ' + e.message);
        _undoStack.pop();
        _redoStack.push(state);
        saveHistoryCache();
    }).finally(function(){
        var btn = document.getElementById('btn-redo');
        if (btn) btn.classList.remove('is-loading');
    });
    updateUndoRedoButtons();
}

function showHistoryLog() {
    var modal = document.getElementById('history-log-modal');
    if (!modal) return;
    modal.classList.add('is-active');
    fetchHistoryLog();
}

function fetchHistoryLog() {
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/history?limit=50')
        .then(function(r){ return r.json(); })
        .then(function(data){
            var container = document.getElementById('history-log-list');
            if (!container) return;
            container.innerHTML = '';
            data.forEach(function(entry){
                var actionClass = entry.action === 'undo' ? 'is-warning' : (entry.action === 'redo' ? 'is-info' : 'is-success');
                var actionIcon = entry.action === 'undo' ? '↩' : (entry.action === 'redo' ? '↪' : '•');
                var canRevert = entry.action === 'change';
                var html = '<div class="history-entry" style="border:1px solid #ddd;border-radius:4px;margin-bottom:8px;overflow:hidden">';
                html += '<div class="history-header" style="background:#f5f5f5;padding:8px 12px;display:flex;align-items:center;gap:10px;cursor:pointer" onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display===\'none\'?\'block\':\'none\'">';
                html += '<span class="tag ' + actionClass + '" style="min-width:60px;text-align:center">' + actionIcon + ' ' + entry.action + '</span>';
                html += '<span style="color:#666;font-size:0.75rem;flex:1">' + (entry.description || 'no description') + '</span>';
                html += '<span style="color:#888;font-size:0.7rem">' + entry.time + '</span>';
                html += '<span style="color:#555;font-size:0.75rem;font-weight:500">' + entry.user_name + '</span>';
                if (canRevert) {
                    html += '<button class="button is-small is-warning" style="padding:2px 8px;font-size:0.7rem" onclick="event.stopPropagation();revertHistoryEntry('+entry.id+')">↩ Rückgängig</button>';
                }
                html += '</div>';
                html += '<div class="history-details" style="padding:10px 12px;background:#fff;white-space:pre-wrap;font-family:monospace;font-size:0.75rem;line-height:1.5;color:#333;display:none">';
                html += entry.long_description || entry.description || 'Keine Details';
                html += '</div></div>';
                container.insertAdjacentHTML('beforeend', html);
            });
            if (data.length === 0) {
                container.innerHTML = '<p style="text-align:center;color:#888;padding:30px">Keine History-Einträge vorhanden</p>';
            }
        }).catch(function(e){
            console.error('History load failed', e);
        });
}

function revertHistoryEntry(historyId) {
    if (!confirm('Diese Änderung rückgängig machen?')) return;
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/history/'+historyId+'/undo', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'}
    }).then(function(r){ return r.json(); })
    .then(function(data){
        if (data.ok) {
            alert(data.message);
            fetchHistoryLog();
            updateUndoRedoButtons();
        } else {
            alert('Fehler: ' + (data.error || 'Unbekannt'));
        }
    }).catch(function(e){
        alert('Fehler: ' + e);
    });
}

document.addEventListener('keydown', function(e){
    if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
        e.preventDefault();
        doUndo();
    }
    if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.shiftKey && e.key === 'z'))) {
        e.preventDefault();
        doRedo();
    }
});

function formatGerman(n){
    if(n===null||n===undefined||isNaN(n)||n===0)return'–';
    var parts=n.toFixed(2).split('.');
    parts[0]=parts[0].replace(/\B(?=(\d{3})+(?!\d))/g,'.');
    return parts.join(',');
}
function formatGerman3(n){
    if(n===null||n===undefined||isNaN(n)||n===0)return'–';
    var parts=n.toFixed(3).split('.');
    parts[0]=parts[0].replace(/\B(?=(\d{3})+(?!\d))/g,'.');
    return parts.join(',');
}
function germanNum(v, prec){
    if(v===null||v===undefined||v==='')return'';
    var n=parseFloat(String(v).replace(/,/g,'.'));
    if(isNaN(n)||n===0)return'';
    if(prec!==undefined)return n.toFixed(prec).replace('.',',');
    return String(n).replace('.',',');
}
function sanitizeNum(el){
    el.value=el.value.replace(/[^0-9,\-]/g,'');
    validateNumField(el);
}
function validateNumField(el){
    var v=el.value.trim();
    if(v===''||v==='-'||v===','||v==='-,'){
        el.style.borderColor='';
        return true;
    }
    var valid=/^-?\d{1,3}(\.\d{3})*(,\d*)?$/.test(v)||/^-?\d+(,\d*)?$/.test(v);
    el.style.borderColor=valid?'':'#f14668';
    return valid;
}

/* === Export mit Aktueller Ansicht === */
function exportWithFilter(type){
    var chk=document.getElementById('chk-aktuelle-ansicht');
    var base='{{ url_for("export.excel", project_id=project.id) }}'.replace('/excel','');
    var urls={excel:base+'/excel', pdf:base+'/pdf', txt:base+'/txt'};
    var sep='?';
    var params='aufmass_id={{ aufmass.id }}';
    if(chk&&chk.checked){
        var ids=[];
        document.querySelectorAll('#pos-sortable tr[data-id]').forEach(function(r){
            if(r.style.display!=='none')ids.push(r.dataset.id);
        });
        if(ids.length)params+='&visible_ids='+ids.join(',');
    }
    window.location.href=urls[type]+sep+params;
}

function toggleExportMenu() {
    var dropdown = document.getElementById('export-dropdown');
    dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
}

document.addEventListener('click', function(e) {
    var btn = document.getElementById('btn-export-menu');
    var dropdown = document.getElementById('export-dropdown');
    if (btn && dropdown && !btn.contains(e.target) && !dropdown.contains(e.target)) {
        dropdown.style.display = 'none';
    }
});

function updateExportZipButton() {
    var checks = ['excel', 'pdf', 'txt', 'x31', 'x31ca'];
    var anyChecked = checks.some(function(id) {
        var chk = document.getElementById('export-' + id);
        return chk && chk.checked;
    });
    var zipSection = document.getElementById('export-zip-section');
    if (zipSection) {
        zipSection.style.display = anyChecked ? 'block' : 'none';
    }
}

function getVisibleIdsParam() {
    var chk = document.getElementById('chk-aktuelle-ansicht');
    if (chk && chk.checked) {
        var ids = [];
        document.querySelectorAll('#pos-sortable tr[data-id]').forEach(function(r) {
            if (r.style.display !== 'none') ids.push(r.dataset.id);
        });
        if (ids.length) return '&visible_ids=' + ids.join(',');
    }
    return '';
}

function downloadZipExport() {
    var checks = [
        { id: 'excel', suffix: '.xlsx', url: '{{ url_for("export.excel", project_id=project.id) }}' },
        { id: 'pdf', suffix: '.pdf', url: '{{ url_for("export.pdf", project_id=project.id) }}' },
        { id: 'txt', suffix: '.txt', url: '{{ url_for("export.txt", project_id=project.id) }}' },
        { id: 'x31', suffix: '.x31', url: '{{ url_for("export.x31", project_id=project.id) }}' },
        { id: 'x31ca', suffix: '.x31ca', url: '{{ url_for("export.x31_california", project_id=project.id) }}' }
    ];
    var selected = checks.filter(function(c) {
        var chk = document.getElementById('export-' + c.id);
        return chk && chk.checked;
    });
    if (selected.length === 0) {
        alert('Bitte mindestens einen Export auswählen.');
        return;
    }
    var baseParams = 'aufmass_id={{ aufmass.id }}' + getVisibleIdsParam();
    var projectName = '{{ project.bezeichnung or project.sm_nr or "Projekt" }}'.replace(/[^a-zA-Z0-9äöüÄÖÜß_-]/g, '_');
    var aufmassName = '{{ aufmass.name }}'.replace(/[^a-zA-Z0-9äöüÄÖÜß_-]/g, '_');
    var date = new Date().toISOString().split('T')[0].replace(/-/g, '.');
    var zipName = projectName + ' - ' + aufmassName + ' - ' + date + '.zip';
    
    var form = document.createElement('form');
    form.method = 'POST';
    form.action = '{{ url_for("export.zip_download", project_id=project.id) }}';
    form.style.display = 'none';
    
    var input = document.createElement('input');
    input.type = 'hidden';
    input.name = 'exports';
    input.value = JSON.stringify(selected.map(function(s) { return { id: s.id, suffix: s.suffix }; }));
    form.appendChild(input);
    
    var paramsInput = document.createElement('input');
    paramsInput.type = 'hidden';
    paramsInput.name = 'base_params';
    paramsInput.value = baseParams;
    form.appendChild(paramsInput);
    
    var zipNameInput = document.createElement('input');
    zipNameInput.type = 'hidden';
    zipNameInput.name = 'zip_name';
    zipNameInput.value = zipName;
    form.appendChild(zipNameInput);
    
    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);
    
    document.getElementById('export-dropdown').style.display = 'none';
}
/* === Nach Reload: markierte Positionen wiederherstellen + hinscrollen === */
(function(){
    // Checkbox-Zustand wiederherstellen
    var chk=document.getElementById('chk-anfuegen');
    if(chk){
        var saved=localStorage.getItem('chk_anfuegen');
        if(saved!==null)chk.checked=saved==='true';
        chk.addEventListener('change',function(){localStorage.setItem('chk_anfuegen',chk.checked);});
    }
    var raw=sessionStorage.getItem('markedPositions');
    if(raw){
        sessionStorage.removeItem('markedPositions');
        var ids=JSON.parse(raw);
        setTimeout(function(){
            var firstRow=null;
            ids.forEach(function(id){
                var row=document.querySelector('#pos-sortable tr[data-id="'+id+'"]');
                if(row){
                    rightSelectedSet.add(row.dataset.id);
                    row.classList.add('is-selected-right');
                    if(!firstRow)firstRow=row;
                }
            });
            updateRightCount();
            // Zur ersten markierten Zeile scrollen
            if(firstRow){
                var cont=firstRow.closest('#am-right')||firstRow.closest('[style*="overflow"]');
                if(cont)firstRow.scrollIntoView({block:'nearest',behavior:'smooth'});
            }
        },100);
    }
})();
function reloadWithMarked(ids){
    if(ids&&ids.length)sessionStorage.setItem('markedPositions',JSON.stringify(ids));
    location.reload();
}

/* === Resizable Divider (Width in localStorage) === */
(function(){
    const div=document.getElementById('am-divider');
    const left=document.getElementById('am-left');
    if(!div||!left)return;
    // Gespeicherte Breite wiederherstellen
    const saved=localStorage.getItem('am_left_width');
    if(saved&&parseInt(saved)>280)left.style.width=saved+'px';
    let drag=false,sx=0;
    div.addEventListener('mousedown',function(e){drag=true;sx=e.clientX;document.body.style.cursor='col-resize';document.body.style.userSelect='none';});
    document.addEventListener('mousemove',function(e){
        if(!drag)return;
        const nw=left.offsetWidth+(e.clientX-sx);
        if(nw>280)left.style.width=nw+'px';
        sx=e.clientX;
    });
    document.addEventListener('mouseup',function(){
        if(drag)localStorage.setItem('am_left_width',left.offsetWidth);
        drag=false;document.body.style.cursor='';document.body.style.userSelect='';
    });
})();

/* === Horizontaler Divider (LV-Tabelle ↔ Eingabe) === */
(function(){
    const div=document.getElementById('lv-divider');
    const wrap=document.getElementById('lv-table-wrap');
    if(!div||!wrap)return;
    if(location.search.includes('reset_lv=1')){
        localStorage.removeItem('lv_table_height');
        localStorage.removeItem('am_left_width');
        history.replaceState(null,'',location.pathname+location.hash);
    }
    const saved=localStorage.getItem('lv_table_height');
    if(saved&&parseInt(saved)>80)wrap.style.height=saved+'px';
    let drag=false,sy=0;
    div.addEventListener('mousedown',function(e){drag=true;sy=e.clientY;document.body.style.cursor='row-resize';document.body.style.userSelect='none';});
    div.addEventListener('dblclick',function(){wrap.style.height='400px';localStorage.removeItem('lv_table_height');});
    document.addEventListener('mousemove',function(e){
        if(!drag)return;
        const nh=wrap.offsetHeight+(e.clientY-sy);
        if(nh>80){
            const parent=div.parentElement;
            const maxNh=parent.clientHeight - wrap.offsetTop - 5 - 50;
            wrap.style.height=Math.min(nh,maxNh)+'px';
        }
        sy=e.clientY;
    });
    document.addEventListener('mouseup',function(){
        if(drag)localStorage.setItem('lv_table_height',wrap.offsetHeight);
        drag=false;document.body.style.cursor='';document.body.style.userSelect='';
    });
})();

/* === Column-Resize für LV- und Pos-Tabelle === */
(function(){
    function makeResizable(tableId,storageKey){
        var table=document.getElementById(tableId);
        if(!table)return;
        var thead=table.querySelector('thead');
        if(!thead)return;
        var cols=thead.querySelectorAll('th');
        // Gespeicherte Breiten wiederherstellen
        var saved=localStorage.getItem(storageKey);
        if(saved){
            try{
                var widths=JSON.parse(saved);
                cols.forEach(function(th,i){
                    if(widths[i])th.style.width=widths[i];
                });
            }catch(e){}
        }
        // Resize-Handles hinzufügen (Hit-Fläche + Handle) – für alle Spalten
        cols.forEach(function(th){
            var txt=th.textContent.trim();
            if(txt==='✕'||txt==='Z-Art')return; // Aktionsspalten überspringen
            var hit=document.createElement('div');
            hit.className='resize-hit';
            var handle=document.createElement('div');
            handle.className='resize-handle';
            var dragging=false,startX=0,startW=0;
            function onStart(e){
                dragging=true;startX=e.clientX;startW=th.offsetWidth;
                handle.classList.add('dragging');
                document.body.style.cursor='col-resize';document.body.style.userSelect='none';
            }
            hit.addEventListener('mousedown',onStart);
            // Doppelklick = Auto-Fit
            hit.addEventListener('dblclick',function(e){
                var maxW=0;
                var idx=Array.from(cols).indexOf(th);
                table.querySelectorAll('tbody tr').forEach(function(row){
                    var cell=row.children[idx];
                    if(cell){var w=cell.scrollWidth+8;if(w>maxW)maxW=w;}
                });
                var hdrW=th.scrollWidth;
                var newW=Math.max(hdrW,maxW,30);
                th.style.width=newW+'px';
                saveWidths(cols,storageKey);
            });
            th.appendChild(hit);
            th.appendChild(handle);
            document.addEventListener('mousemove',function(e){
                if(!dragging)return;
                var diff=e.clientX-startX;
                var newW=Math.max(30,startW+diff);
                th.style.width=newW+'px';
                // Spaltenbreite der Tabelle anpassen
                table.style.tableLayout='fixed';
            });
            document.addEventListener('mouseup',function(){
                if(dragging){
                    dragging=false;
                    handle.classList.remove('dragging');
                    document.body.style.cursor='';document.body.style.userSelect='';
                    saveWidths(cols,storageKey);
                }
            });
        });
    }
    function saveWidths(cols,key){
        var widths={};
        cols.forEach(function(th,i){
            if(th.style.width)widths[i]=th.style.width;
        });
        localStorage.setItem(key,JSON.stringify(widths));
    }
    makeResizable('lv-table','lv_col_widths');
    makeResizable('pos-table','pos_col_widths');
})();

/* === Header-Kontextmenü: Ansicht speichern === */
(function(){
    var hdrMenu=document.createElement('div');
    hdrMenu.id='hdr-context-menu';
    hdrMenu.style.cssText='position:fixed;display:none;z-index:10001;background:#fff;border:1px solid #ccc;border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);padding:4px 0;min-width:180px;font-size:0.8rem';
    hdrMenu.innerHTML='<div style="padding:7px 14px;cursor:pointer;display:flex;align-items:center;gap:8px" onmouseenter="this.style.background=\'#f0f2f5\'" onmouseleave="this.style.background=\'\'" onclick="saveColumnWidths();hideHeaderMenu()"><span style="font-size:1rem">💾</span><span>Ansicht speichern</span></div>';
    document.body.appendChild(hdrMenu);
    
    window.hideHeaderMenu=function(){hdrMenu.style.display='none';};
    document.addEventListener('click',function(e){
        if(hdrMenu.style.display!=='none'&&!hdrMenu.contains(e.target))hideHeaderMenu();
    });
    document.addEventListener('keydown',function(e){if(e.key==='Escape')hideHeaderMenu();});
    
    // Rechtsklick auf header
    document.querySelectorAll('#pos-table thead th, #lv-table thead th').forEach(function(th){
        th.addEventListener('contextmenu',function(e){
            e.preventDefault();
            hdrMenu.style.left=e.clientX+'px';
            hdrMenu.style.top=e.clientY+'px';
            hdrMenu.style.display='block';
        });
    });
})();
function saveColumnWidths(){
    ['lv_col_widths','pos_col_widths'].forEach(function(key){
        var saved=localStorage.getItem(key);
        if(saved)localStorage.setItem('saved_'+key,saved);
    });
    // Flash-ähnliche Rückmeldung
    var btn=document.getElementById('btn-reset-filter');
    if(btn){var old=btn.textContent;btn.textContent='✓ Gespeichert';btn.style.background='#d4edda';setTimeout(function(){btn.textContent=old;btn.style.background='';},1500);}
}

/* === Live Search (clientseitig, filtert die LV-Tabelle) === */
(function(){
    const inp=document.getElementById('lv-search');
    if(!inp)return;let t;
    inp.addEventListener('input',function(){
        clearTimeout(t);t=setTimeout(function(){
            const q=inp.value.trim().toLowerCase();
            const items=document.querySelectorAll('#lv-list .lv-item');
            var found=0;
            items.forEach(function(el){
                var txt=(el.dataset.posnr+' '+el.dataset.text+' '+el.dataset.rsa+' '+el.dataset.abschnitt).toLowerCase();
                if(!q||txt.indexOf(q)!==-1){el.style.display='';found++;}
                else{el.style.display='none';}
            });
            var emptyMsg=document.getElementById('lv-empty-row');
            if(!found&&q){
                if(!emptyMsg){
                    var tbody=document.getElementById('lv-list');
                    var tr=document.createElement('tr');tr.id='lv-empty-row';
                    tr.innerHTML='<td colspan="4" style="padding:12px;text-align:center;color:#999;font-size:0.75rem">Keine Positionen gefunden.</td>';
                    tbody.appendChild(tr);
                }
            }else if(emptyMsg){emptyMsg.remove();}
        },200);
    });
})();

/* === LV Select: Windows-Stil (Ctrl/Klick, Shift/Bereich) + Formular befüllen === */
var selectedSet=new Set();
var lastClickedIdx=-1;
var currentLVRow=null;

// Event-Delegation für Klick auf der LV-Liste
document.addEventListener('DOMContentLoaded',function(){
    var lvBody=document.getElementById('lv-list');
    if(!lvBody)return;
    // Klick
    lvBody.addEventListener('click',function(e){
        var row=e.target.closest('.lv-item');
        if(!row)return;
        var idx=Array.from(lvBody.children).indexOf(row);
        if(e.ctrlKey||e.metaKey){
            // Toggle in multi-selection
            if(selectedSet.has(row.dataset.id)){
                selectedSet.delete(row.dataset.id);
                row.classList.remove('is-selected');
            }else{
                selectedSet.add(row.dataset.id);
                row.classList.add('is-selected');
            }
            lastClickedIdx=idx;
            updateLVCount();
            return;
        }
        if(e.shiftKey&&lastClickedIdx>=0){
            // Range select
            var min=Math.min(lastClickedIdx,idx);
            var max=Math.max(lastClickedIdx,idx);
            for(var i=min;i<=max;i++){
                var r=lvBody.children[i];
                if(r&&r.classList){
                    selectedSet.add(r.dataset.id);
                    r.classList.add('is-selected');
                }
            }
            updateLVCount();return;
        }
        // Einfach-Klick: Selektion zurücksetzen, nur diese Zeile auswählen + Formular füllen
        clearMultiSelect();
        selectedSet.add(row.dataset.id);
        row.classList.add('is-selected');
        updateLVCount();
        fillForm(row);
        lastClickedIdx=idx;
    });
});

function fillForm(row){
    currentLVRow=row;
    currentLVId=parseInt(row.dataset.id);
    var d=row.dataset;
    currentEinheit=d.eh||'';
    document.getElementById('form-lv-id').value=d.id;
    document.getElementById('form-posnr').value=d.posnr;
    document.getElementById('form-kurztext').value=d.text||'';
    document.getElementById('form-abschnitt').value=d.abschnitt||'';
    document.getElementById('form-bemerkung').value='';
    document.getElementById('form-langtext').textContent=d.lang||'';
    updateLangPanel();
    setFormelTyp('standard');
    var faktorEl=document.getElementById('form-faktor');
    ['field-laenge','field-breite','field-tiefe'].forEach(function(id){
        document.getElementById(id).querySelector('label').style.fontWeight='normal';
        document.getElementById(id).querySelector('input').style.background='';
        document.getElementById(id).querySelector('input').value='';
    });
    var eh=d.eh;
    faktorEl.value='1,0';
    faktorEl.style.background='';
    faktorEl.style.fontWeight='';
    faktorEl.placeholder='';
    if(eh==='M'){
        document.getElementById('field-laenge').querySelector('input').style.background='#fff3cd';
    }else if(eh==='M2'){
        document.getElementById('field-laenge').querySelector('input').style.background='#fff3cd';
        document.getElementById('field-breite').querySelector('input').style.background='#fff3cd';
    }else if(eh==='M3'){
        document.getElementById('field-laenge').querySelector('input').style.background='#fff3cd';
        document.getElementById('field-breite').querySelector('input').style.background='#fff3cd';
        document.getElementById('field-tiefe').querySelector('input').style.background='#fff3cd';
    }
    var mengeEl=document.getElementById('form-menge');
    berechneStd();
    if(eh==='ST'||eh==='LE'||eh==='STD'||eh==='h'||eh==='Psch'){
        mengeEl.style.background='#fff3cd';
        mengeEl.style.fontWeight='bold';
        mengeEl.value='';
    }else{
        mengeEl.style.background='';
        mengeEl.style.fontWeight='';
    }
}

function clearMultiSelect(){
    selectedSet.forEach(function(id){
        var row=document.querySelector('#lv-list .lv-item[data-id="'+id+'"]');
        if(row)row.classList.remove('is-selected');
    });
    selectedSet.clear();
}

/* === Multi-Select für rechte Tabelle === */
var rightSelectedSet=new Set();
var rightLastClickedIdx=-1;

function clearRightMultiSelect(){
    rightSelectedSet.forEach(function(id){
        var row=document.querySelector('#pos-sortable tr[data-id="'+id+'"]');
        if(row)row.classList.remove('is-selected-right');
    });
    rightSelectedSet.clear();
    var el=document.getElementById('right-count');
    if(el)el.textContent='';
}

function updateRightCount(){
    var n=rightSelectedSet.size;
    var el=document.getElementById('right-count');
    if(!el)return;
    if(n===0){el.textContent='';return;}
    var sum=0;
    rightSelectedSet.forEach(function(id){
        var row=document.querySelector('#pos-sortable tr[data-id="'+id+'"]');
        if(row)sum+=parseFloat(row.dataset.gp)||0;
    });
    el.textContent=n+' ausgewählt, Summe='+formatGerman(sum)+' €';
}

/* === Formelart umschalten === */
function setFormelTyp(t){
    formelTyp=t;
    var b1=document.getElementById('btn-std'),b2=document.getElementById('btn-frei');
    b1.className='btn-formel'+(t==='standard'?' is-active':'');
    b2.className='btn-formel'+(t==='frei'?' is-active':'');
    document.getElementById('form-std').style.display=t==='standard'?'':'none';
    document.getElementById('form-frei').style.display=t==='frei'?'':'none';
}

/* === Berechnungen === */
function parseNum(v){return parseFloat(String(v).replace(/,/g,'.'))||0;}

function berechneStd(){
    const f=parseNum(document.getElementById('form-faktor').value);
    const l=parseNum(document.getElementById('form-laenge').value);
    const b=parseNum(document.getElementById('form-breite').value);
    const t=parseNum(document.getElementById('form-tiefe').value);
    const eh=currentLVRow&&currentLVRow.dataset.eh?currentLVRow.dataset.eh:currentEinheit||'ST';
    var menge=0;
    if(eh==='ST'||eh==='LE'||eh==='STD'||eh==='h'||eh==='Psch')menge=f*1;
    else if(eh==='M')menge=l;
    else if(eh==='M2')menge=l*b;
    else if(eh==='M3')menge=l*b*t;
    else menge=l;
    document.getElementById('form-menge').value=germanNum(menge,3)||'0,000';
}
function berechneFrei(){
    var menge=0;
    try{
        const expr=document.getElementById('form-formel').value.trim();
        if(expr)menge=berechneAusdruck(expr);
    }catch(e){menge=0;}
    document.getElementById('form-menge-frei').value=germanNum(menge,3)||'0,000';
}

/* === Einfacher Math-Parser (Punkt vor Strich) === */
function berechneAusdruck(s){
    s=s.replace(/,/g,'.'); // Deutsche Dezimaltrenner
    var tokens=[],i=0,num='';
    function flush(){if(num!==''){tokens.push(parseFloat(num));num='';}}
    while(i<s.length){
        var c=s[i];
        if(c>='0'&&c<='9'||c==='.'){num+=c;i++;continue;}
        flush();
        if(c==='+'||c==='-'||c==='*'||c==='/'||c==='('||c===')'){tokens.push(c);i++;continue;}
        if(c===' '||c==='\t'){i++;continue;}
        throw new Error('Ungültiges Zeichen: '+c);
    }
    flush();
    var pos=0;
    function prim(){
        if(pos>=tokens.length)throw new Error('Erwarte Zahl');
        if(typeof tokens[pos]==='number')return tokens[pos++];
        if(tokens[pos]==='('){pos++;var v=expr();if(tokens[pos]!==')')throw new Error(') erwartet');pos++;return v;}
        throw new Error('Syntaxfehler');
    }
    function mul(){
        var v=prim();
        while(pos<tokens.length&&(tokens[pos]==='*'||tokens[pos]==='/')){
            var op=tokens[pos++];var r=prim();
            v=op==='*'?v*r:v/r;
        }
        return v;
    }
    function expr(){
        var v=mul();
        while(pos<tokens.length&&(tokens[pos]==='+'||tokens[pos]==='-')){
            var op=tokens[pos++];var r=mul();
            v=op==='+'?v+r:v-r;
        }
        return v;
    }
    return expr();
}

/* === Hinzufügen mit Formular-Daten (overrides + insert_idx) === */
function addMitFormular(){
    if(selectedSet.size===0){alert('Keine LV-Positionen ausgewählt.');return;}
    var ids=[];selectedSet.forEach(function(id){ids.push(parseInt(id));});
    var overrides={};
    overrides.formel_typ=formelTyp;
    if(formelTyp==='frei'){
        overrides.formel=document.getElementById('form-formel').value;
    }else{
        overrides.faktor=parseNum(document.getElementById('form-faktor').value);
        overrides.laenge=parseNum(document.getElementById('form-laenge').value);
        overrides.breite=parseNum(document.getElementById('form-breite').value);
        overrides.tiefe=parseNum(document.getElementById('form-tiefe').value);
    }
    // Text-Overrides nur bei Einzelauswahl (sonst überschreiben sie alle LV-spezifischen Werte)
    if(ids.length===1){
        var pn=document.getElementById('form-posnr').value;if(pn)overrides.pos_nr=pn;
        ['kurztext','abschnitt','bemerkung'].forEach(function(id){
            var v=document.getElementById('form-'+id).value;
            if(v)overrides[id]=v;
        });
    }
    var body={ids:ids,overrides:overrides};
    var insertIdx=getInsertIdxFromCheckbox();
    if(insertIdx>=0)body.insert_idx=insertIdx;
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/batch-add',{
        method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify(body)
    }).then(function(r){return r.json()}).then(function(resp){
        var marked=resp.added?resp.added.map(function(p){return p.id;}):ids;
        // Push to undo stack for new positions with overrides - ONE ENTRY PER POSITION
        if(resp.added && resp.added.length > 0) {
            resp.added.forEach(function(pos) {
                var overridesDesc = Object.keys(overrides || {}).map(function(k){ return k + '=' + overrides[k]; }).join(', ') || 'keine';
                var desc = 'Pos ' + (pos.posnr || pos.id) + ' aus LV hinzugefügt (' + overridesDesc + ')';
                pushUndoState(desc, [{
                    position_id: pos.id, 
                    pos_nr: pos.posnr || '', 
                    old: {id: pos.id}, 
                    new: {_action: 'add_form', id: pos.id, overrides: overrides}
                }]);
            });
        } else if(marked && marked.length > 0) {
            pushUndoState(marked.length + ' Position(en) mit Formular hinzugefügt', marked.map(function(id) {
                return {position_id: id, pos_nr: '', old: {id: id}, new: {_action: 'add_form', id: id, overrides: overrides}};
            }));
        }
        var doEmpty=document.getElementById('chk-empty-end')?.checked;
        if(doEmpty){
            fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/position/leer',{
                method:'POST',headers:{'Content-Type':'application/json'}
            }).then(function(r){return r.json()}).then(function(resp2){
                var m2=marked.slice();
                if(resp2.id)m2.push(resp2.id);
                reloadWithMarked(m2);
            }).catch(function(){reloadWithMarked(marked)});
        }else{reloadWithMarked(marked);}
    });
}

/* === Leere Zeile + insert_idx === */
function addLeereZeile(){
    var body={};
    var insertIdx=getInsertIdxFromCheckbox();
    if(insertIdx>=0)body.insert_idx=insertIdx;
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/position/leer',{
        method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify(body)
    }).then(function(r){return r.json()}).then(function(resp){
        if(resp.id) {
            pushUndoState('+ Leere Zeile hinzugefügt', [{
                position_id: resp.id, 
                pos_nr: resp.pos_nr || '', 
                old: {id: null}, 
                new: {_action: 'add_empty', id: resp.id}
            }]);
        }
        reloadWithMarked(resp.id?[resp.id]:[]);});
}

/* === Multi-Select: Alle/Keine + Auswahl hinzufügen === */
function toggleAllLV(){
    var anySelected=selectedSet.size>0;
    document.querySelectorAll('#lv-list .lv-item').forEach(function(row){
        if(anySelected){selectedSet.delete(row.dataset.id);row.classList.remove('is-selected');}
        else{selectedSet.add(row.dataset.id);row.classList.add('is-selected');}
    });
    updateLVCount();
}
function updateLVCount(){
    var all=document.querySelectorAll('#lv-list .lv-item').length;
    var n=selectedSet.size;
    var sum=0;
    selectedSet.forEach(function(id){
        var row=document.querySelector('#lv-list .lv-item[data-id="'+id+'"]');
        if(row)sum+=parseFloat(row.dataset.ep)||0;
    });
    document.getElementById('lv-count').textContent=n+'/'+all+' ausgewählt'+(sum>0?' | ∑ EP='+formatGerman(sum)+' €':'');
}

/* === Prüft ob "Am Ende anfügen" aktiv ist === */
function shouldAppendEnd(){
    var chk=document.getElementById('chk-anfuegen');
    return chk&&chk.checked;
}

/* === insert_idx aus Checkbox + rightLastClickedIdx ermitteln === */
function getInsertIdxFromCheckbox(){
    if(shouldAppendEnd())return -1;
    if(rightLastClickedIdx>=0)return rightLastClickedIdx+1;
    return -1;
}
function getInsertIdxFromDrop(){
    if(shouldAppendEnd())return -1;
    if(_dragInsertIdx>=0)return _dragInsertIdx;
    return -1;
}

function addSelectedLV(){
    var ids=[];
    selectedSet.forEach(function(id){ids.push(parseInt(id));});
    if(!ids.length){alert('Keine LV-Positionen ausgewählt.');return;}
    var body={ids:ids};
    var insertIdx=getInsertIdxFromCheckbox();
    if(insertIdx>=0)body.insert_idx=insertIdx;
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/batch-add',{
        method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify(body)
    }).then(function(r){return r.json()}).then(function(resp){
        var ids2=resp.added?resp.added.map(function(p){return p.id;}):ids;
        if(resp.added && resp.added.length > 0) {
            resp.added.forEach(function(pos) {
                var kt = pos.kurztext ? ' - ' + pos.kurztext.substring(0,20) : '';
                var desc = '+ Pos ' + (pos.pos_nr || pos.id) + kt;
                pushUndoState(desc, [{
                    position_id: pos.id, 
                    pos_nr: pos.pos_nr || '', 
                    old: {id: null}, 
                    new: {_action: 'add', id: pos.id}
                }]);
            });
        } else if(ids2 && ids2.length > 0) {
            pushUndoState(ids2.length + ' Position(en) aus LV hinzugefügt', ids2.map(function(id) {
                return {position_id: id, pos_nr: '', old: {id: null}, new: {_action: 'add', id: id}};
            }));
        }
        reloadWithMarked(ids2);});
}

/* === Markierte Einträge löschen, kopieren + Tabelle leeren === */
function deleteSelectedPos(){
    if(rightSelectedSet.size===0){alert('Keine Positionen markiert.');return;}
    var ids=[];rightSelectedSet.forEach(function(id){ids.push(parseInt(id));});
    // Store positions data for undo before deleting
    var positionsData = [];
    ids.forEach(function(id) {
        var row = document.querySelector('#pos-sortable tr[data-id="'+id+'"]');
        if(row) {
            positionsData.push({
                id: id,
                posnr: row.dataset.posnr || '',
                faktor: row.dataset.faktor || '1',
                laenge: row.dataset.laenge || '0',
                breite: row.dataset.breite || '0',
                tiefe: row.dataset.tiefe || '0',
                menge: row.dataset.menge || '0',
                menge_hinten: row.dataset.mengeHinten || '0',
                einheit: row.dataset.einheit || '',
                formel_typ: row.dataset.formelTyp || 'standard',
                formel: row.dataset.formel || '',
                abschnitt: row.dataset.abschnitt || '',
                bemerkung: row.dataset.bemerkung || '',
                sort_order: row.rowIndex
            });
        }
    });
    // Push to undo stack BEFORE deleting
    if(positionsData.length > 0) {
        pushUndoState(positionsData.length + ' Position(en) gelöscht', positionsData.map(function(p) {
            return {position_id: p.id, pos_nr: p.posnr, old: p, new: {_action: 'delete', id: p.id}};
        }));
    }
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/batch-loeschen',{
        method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({ids:ids})
    }).then(function(r){return r.json()}).then(function(){location.reload();});
}
function copySelectedPos(){
    if(rightSelectedSet.size===0){alert('Keine Positionen markiert.');return;}
    var ids=[];rightSelectedSet.forEach(function(id){ids.push(parseInt(id));});
    var body={ids:ids.map(Number)};
    if(!shouldAppendEnd()){
        if(rightLastClickedIdx>=0)body.insert_idx=rightLastClickedIdx+1;
    }
    // Store for undo (need to capture original IDs before copy)
    var originalIds = ids.slice();
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/kopieren',{
        method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify(body)
    }).then(function(r){return r.json()}).then(function(resp){
        // Push to undo stack with new position IDs
        if(resp.added && resp.added.length > 0) {
            var copyInfo = [];
            for(var i=0; i<resp.added.length; i++) {
                copyInfo.push({
                    old_id: originalIds[i],
                    new_id: resp.added[i]
                });
            }
            pushUndoState(originalIds.length + ' Position(en) kopiert', copyInfo.map(function(info) {
                return {position_id: info.new_id, pos_nr: '', old: {id: info.old_id}, new: {_action: 'copy', old_id: info.old_id, new_id: info.new_id}};
            }));
        }
        if(resp.added)reloadWithMarked(resp.added);
        else location.reload();
    });
}
function deleteAllPos(){
    if(!confirm('Wirklich ALLE Positionen dieser Tabelle löschen?'))return;
    // Store ALL positions data for undo
    var allRows = document.querySelectorAll('#pos-sortable tr');
    var allPositionsData = [];
    allRows.forEach(function(row) {
        var id = parseInt(row.dataset.id);
        if(id) {
            allPositionsData.push({
                id: id,
                posnr: row.dataset.posnr || '',
                faktor: row.dataset.faktor || '1',
                laenge: row.dataset.laenge || '0',
                breite: row.dataset.breite || '0',
                tiefe: row.dataset.tiefe || '0',
                menge: row.dataset.menge || '0',
                menge_hinten: row.dataset.mengeHinten || '0',
                einheit: row.dataset.einheit || '',
                formel_typ: row.dataset.formelTyp || 'standard',
                formel: row.dataset.formel || '',
                abschnitt: row.dataset.abschnitt || '',
                bemerkung: row.dataset.bemerkung || ''
            });
        }
    });
    // Push to undo stack BEFORE deleting all
    if(allPositionsData.length > 0) {
        pushUndoState('Alle ' + allPositionsData.length + ' Position(en) gelöscht', allPositionsData.map(function(p) {
            return {position_id: p.id, pos_nr: p.posnr, old: p, new: {_action: 'delete_all', id: p.id}};
        }));
    }
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/leeren',{
        method:'POST',headers:{'Content-Type':'application/json'}
    }).then(function(r){return r.json()}).then(function(){location.reload();});
}

/* === Drop-Indicator: farbige Linie === */
var dropIndicator=(function(){
    var el=document.createElement('div');el.id='drop-indicator';
    document.body.appendChild(el);return el;
})();
var _dragInsertIdx=-1;

function getInsertIndex(y){
    var rows=document.querySelectorAll('#pos-sortable tr');
    for(var i=0;i<rows.length;i++){
        var r=rows[i].getBoundingClientRect();
        if(y<r.top+r.height/2)return i;
    }
    return rows.length;
}

function showDropIndicator(e){
    var table=document.getElementById('pos-table');
    if(!table)return;
    dropIndicator.style.display='block';
    var rows=document.querySelectorAll('#pos-sortable tr');
    var tableRect=table.getBoundingClientRect();
    if(!rows.length){
        dropIndicator.style.top=(tableRect.top+2)+'px';
        dropIndicator.style.left=(tableRect.left+2)+'px';
        dropIndicator.style.width=(tableRect.width-4)+'px';
        _dragInsertIdx=0;
        return;
    }
    var idx=getInsertIndex(e.clientY);
    _dragInsertIdx=idx;
    if(idx<rows.length){
        var rowRect=rows[idx].getBoundingClientRect();
        dropIndicator.style.top=(rowRect.top-2)+'px';
    }else{
        var lastRect=rows[rows.length-1].getBoundingClientRect();
        dropIndicator.style.top=(lastRect.bottom-2)+'px';
    }
    dropIndicator.style.left=(tableRect.left+2)+'px';
    dropIndicator.style.width=(tableRect.width-4)+'px';
}

function hideDropIndicator(){
    dropIndicator.style.display='none';
    _dragInsertIdx=-1;
}

/* === Drag & Drop (LV→Tabelle) + Multi-Select rechts + Reordering === */
document.addEventListener('DOMContentLoaded',function(){
    var lvBody=document.getElementById('lv-list');
    var posBody=document.getElementById('pos-sortable');
    
    // LV-Dragstart
    if(lvBody){
        lvBody.addEventListener('dragstart',function(e){
            window._dragFromLV=true;
            if(selectedSet.size>0){
                var ids=[];selectedSet.forEach(function(id){ids.push(parseInt(id));});
                e.dataTransfer.setData('text/plain',JSON.stringify(ids));
            }else{
                var row=e.target.closest('.lv-item');
                if(row)e.dataTransfer.setData('text/plain',JSON.stringify([parseInt(row.dataset.id)]));
            }
            e.dataTransfer.effectAllowed='copy';
        });
    }
    
    // === Multi-Select rechte Tabelle ===
    if(posBody){
        posBody.addEventListener('click',function(e){
            var row=e.target.closest('tr');
            if(!row)return;
            if(e.target.tagName==='A'||e.target.tagName==='BUTTON')return;
            var idx=Array.from(posBody.children).indexOf(row);
            if(e.ctrlKey||e.metaKey){
                if(rightSelectedSet.has(row.dataset.id)){
                    rightSelectedSet.delete(row.dataset.id);
                    row.classList.remove('is-selected-right');
                }else{
                    rightSelectedSet.add(row.dataset.id);
                    row.classList.add('is-selected-right');
                }
                rightLastClickedIdx=idx;updateRightCount();return;
            }
            if(e.shiftKey&&rightLastClickedIdx>=0){
                var min=Math.min(rightLastClickedIdx,idx);
                var max=Math.max(rightLastClickedIdx,idx);
                clearRightMultiSelect();
                for(var i=min;i<=max;i++){
                    var r=posBody.children[i];
                    if(r){rightSelectedSet.add(r.dataset.id);r.classList.add('is-selected-right');}
                }
                rightLastClickedIdx=idx;updateRightCount();return;
            }
            clearRightMultiSelect();
            rightSelectedSet.add(row.dataset.id);
            row.classList.add('is-selected-right');
            rightLastClickedIdx=idx;
            updateRightCount();
        });
        // Klick auf Position → Eingabe füllen
        posBody.addEventListener('click',function(e){
            var row=e.target.closest('tr');
            if(!row||row.classList.contains('is-trenner'))return;
            if(e.target.tagName==='A'||e.target.tagName==='BUTTON'||e.target.tagName==='INPUT')return;
            var d=row.dataset;
            var eh=d.einheit||'';
            currentEinheit=eh;
            // LV-Auswahl nicht zurücksetzen (sonst geht "Am Ende anfügen" verloren)
            document.getElementById('form-lv-id').value='';
            document.getElementById('form-posnr').value=d.posnr||'';
            document.getElementById('form-kurztext').value=d.kurztext||'';
            document.getElementById('form-abschnitt').value=d.abschnitt||'';
            document.getElementById('form-langtext').textContent=d.langtext||'';
            updateLangPanel();
            document.getElementById('form-bemerkung').value=d.bemerkung||'';
            setFormelTyp(d.formelTyp||'standard');
            if((d.formelTyp||'standard')==='frei'){
                document.getElementById('form-formel').value=d.formel||'';
                berechneFrei();
            }else{
                currentEinheit=eh;
                // Hintergrund-Farben zurücksetzen
                ['field-laenge','field-breite','field-tiefe'].forEach(function(id){
                    document.getElementById(id).querySelector('label').style.fontWeight='normal';
                    document.getElementById(id).querySelector('input').style.background='';
                    document.getElementById(id).querySelector('input').value='';
                });
                // Faktor: default 1,0 wenn 0, sonst Wert
                var fakt=parseNum(d.faktor);
                var faktorEl=document.getElementById('form-faktor');
                faktorEl.value=fakt>0?germanNum(d.faktor,2):'1,0';
                faktorEl.style.background='';
                faktorEl.style.fontWeight='';
                faktorEl.placeholder='';
                if(eh==='ST'||eh==='LE'||eh==='STD'||eh==='h'||eh==='Psch'){
                    ['field-laenge','field-breite','field-tiefe'].forEach(function(id){
                        document.getElementById(id).querySelector('input').value='';
                    });
                }
                if(eh==='M'){
                    document.getElementById('field-laenge').querySelector('input').style.background='#fff3cd';
                    document.getElementById('form-laenge').value=d.laenge&&parseNum(d.laenge)?germanNum(d.laenge,2):'';
                }else if(eh==='M2'){
                    document.getElementById('field-laenge').querySelector('input').style.background='#fff3cd';
                    document.getElementById('field-breite').querySelector('input').style.background='#fff3cd';
                    document.getElementById('form-laenge').value=d.laenge&&parseNum(d.laenge)?germanNum(d.laenge,2):'';
                    document.getElementById('form-breite').value=d.breite&&parseNum(d.breite)?germanNum(d.breite,2):'';
                }else if(eh==='M3'){
                    document.getElementById('field-laenge').querySelector('input').style.background='#fff3cd';
                    document.getElementById('field-breite').querySelector('input').style.background='#fff3cd';
                    document.getElementById('field-tiefe').querySelector('input').style.background='#fff3cd';
                    document.getElementById('form-laenge').value=d.laenge&&parseNum(d.laenge)?germanNum(d.laenge,2):'';
                    document.getElementById('form-breite').value=d.breite&&parseNum(d.breite)?germanNum(d.breite,2):'';
                    document.getElementById('form-tiefe').value=d.tiefe&&parseNum(d.tiefe)?germanNum(d.tiefe,2):'';
                }
                // Menge-Highlight für ST/LE/STD
                berechneStd();
                if(eh==='ST'||eh==='LE'||eh==='STD'||eh==='h'||eh==='Psch'){
                    document.getElementById('form-menge').style.background='#fff3cd';
                    document.getElementById('form-menge').style.fontWeight='bold';
                    document.getElementById('form-menge').value='';
                }else{
                    document.getElementById('form-menge').style.background='';
                    document.getElementById('form-menge').style.fontWeight='';
                }
            }
        });
        // Doppelklick → Inline-Edit
        posBody.addEventListener('dblclick',function(e){
            var td=e.target.closest('td');
            if(!td)return;
            var row=td.closest('tr');
            if(!row||row.classList.contains('is-trenner'))return;
            var field=td.dataset.field;
            if(!field)return; // nur Zellen mit data-field sind editierbar
            if(td.querySelector('input')||td.querySelector('select'))return;
            var fTyp=row.dataset.formelTyp||'standard';
            var txt=td.textContent.trim();
            if(txt==='–')txt='';
            var textFields={'formel_typ':1,'einheit':1,'abschnitt':1,'kurztext':1,'bemerkung':1,'formel':1};
            // Z-Art: Select
            if(field==='formel_typ'){
                var sel=document.createElement('select');
                sel.style.width='100%';sel.style.boxSizing='border-box';sel.style.fontSize='inherit';
                sel.className='input is-small';
                ['Standard','Freie Formel Z91'].forEach(function(t){
                    var o=document.createElement('option');
                    o.value=t;o.textContent=t;
                    if((fTyp==='frei'&&t==='Freie Formel Z91')||(fTyp!=='frei'&&t==='Standard'))o.selected=true;
                    sel.appendChild(o);
                });
                td.textContent='';td.appendChild(sel);sel.focus();
                function saveZArt(){
                    var newTyp=sel.value==='Freie Formel Z91'?'frei':'standard';
                    var oldTyp=row.dataset.formelTyp||'standard';
                    td.innerHTML='<span class="tag is-light is-small" style="font-size:0.65rem">'+(newTyp==='frei'?'Z91':'Std')+'</span>';
                    row.dataset.formelTyp=newTyp;
                    // Push to undo stack
                    if (oldTyp !== newTyp) {
                        pushUndoState('Z-Art geändert', [{position_id: parseInt(row.dataset.id), pos_nr: '', old: {formel_typ: oldTyp}, new: {formel_typ: newTyp}}]);
                    }
                    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/position/'+row.dataset.id+'/update-cell',{
                        method:'POST',headers:{'Content-Type':'application/json'},
                        body:JSON.stringify({field:'formel_typ',value:newTyp})
                    }).then(function(r){return r.json()}).then(function(resp){
                        if(resp.formel_typ_changed)location.reload();
                    });
                }
                sel.addEventListener('blur',saveZArt);
                sel.addEventListener('change',function(ev){ev.preventDefault();saveZArt();});
                return;
            }
            var inp=document.createElement('input');
            inp.value=(field==='formel')?(row.dataset.formel||''):txt;
            inp.style.width='100%';inp.style.boxSizing='border-box';inp.style.fontSize='inherit';
            inp.className='input is-small';
            td.textContent='';td.appendChild(inp);inp.focus();inp.select();
            var _saving=false;
            function save(){
                if(_saving)return;
                _saving=true;
                var val=inp.value.trim();
                var isText=textFields[field];
                var oldVal = txt;
                if(isText){
                    if(field==='formel'&&td.colSpan){
                        td.colSpan=3;td.style.textAlign='center';td.style.fontFamily='monospace';td.style.fontSize='0.75rem';td.style.background='#fafafa';
                    }
                    var inner=td.querySelector('[data-cell]');
                    if(inner){inner.textContent=val||'–';}else{td.textContent=val||'–';}
                }else{
                    var numVal=parseFloat(val.replace(',','.'))||0;
                    var fmt=formatGerman(numVal);
                    var inner=td.querySelector('[data-cell]');
                    if(inner){inner.textContent=fmt;}else{td.textContent=fmt;}
                    val=numVal;
                }
                td.style.borderColor='';
                // Push to undo stack
                if (oldVal !== val) {
                    var rowId = parseInt(row.dataset.id);
                    var undoChange = {position_id: rowId, pos_nr: '', old: {}, new: {}};
                    undoChange.old[field] = isText ? oldVal : (parseFloat(oldVal.replace(',','.'))||0);
                    undoChange.new[field] = val;
                    pushUndoState('Feld "'+field+'" geändert', [undoChange]);
                }
                fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/position/'+row.dataset.id+'/update-cell',{
                    method:'POST',headers:{'Content-Type':'application/json'},
                    body:JSON.stringify({field:field,value:val})
                }).then(function(r){return r.json()}).then(function(resp){
                    var row2=document.querySelector('#pos-sortable tr[data-id="'+row.dataset.id+'"]');
                    if(resp.menge!==undefined){row2.dataset.menge=resp.menge;
                        var mengeTd=row2.querySelector('[data-cell="menge"]');if(mengeTd)mengeTd.innerHTML=formatGerman3(resp.menge);}
                    if(resp.gesamtpreis!==undefined){row2.dataset.gp=resp.gesamtpreis;
                        var gpTd=row2.querySelector('[data-cell="gp"]');if(gpTd)gpTd.innerHTML=formatGerman(resp.gesamtpreis);}
                    if(resp.menge_hinten!==undefined){row2.dataset.mengeHinten=resp.menge_hinten;
                        var hintenTd=row2.querySelector('[data-field="menge_hinten"]');if(hintenTd)hintenTd.textContent=formatGerman(resp.menge_hinten);}
                });
            }
            inp.addEventListener('blur',save);
            inp.addEventListener('keydown',function(ev){
                if(ev.key==='Enter'){ev.preventDefault();save();}
                if(ev.key==='Escape'){td.textContent=txt;inp.blur();}
            });
        });
    }
    
    // === Drag innerhalb der rechten Tabelle (verschieben markierter Zeilen) ===
    if(posBody){
        // Dragstart: alle markierten IDs speichern
        posBody.addEventListener('dragstart',function(e){
            var row=e.target.closest('tr');
            if(!row)return;
            if(e.target.tagName==='A'||e.target.tagName==='BUTTON'){e.preventDefault();return;}
            var ids=[];
            if(rightSelectedSet.has(row.dataset.id)){
                rightSelectedSet.forEach(function(id){ids.push(id);});
            }else{
                ids.push(row.dataset.id);
            }
            e.dataTransfer.setData('text/plain',JSON.stringify(ids));
            e.dataTransfer.effectAllowed='move';
            window._dragFromTable=true;
        });
        posBody.addEventListener('dragend',function(){window._dragFromTable=false;hideDropIndicator();});
        // Shared drop/dragover handler für Container + posBody (für leere Tabelle)
        var posContainer=document.querySelector('#am-right > div:not([style*="flex-shrink"])');
        function handlePosDrop(e){
            e.preventDefault();
            e.stopPropagation();
            var raw=e.dataTransfer.getData('text/plain');
            var dropIdx=_dragInsertIdx;
            hideDropIndicator();
            if(raw==='__LEERE_ZEILE__'){
                var body={};
                if(!shouldAppendEnd()&&dropIdx>=0){
                    body.insert_idx=dropIdx;
                }
                fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/position/leer',{
                    method:'POST',headers:{'Content-Type':'application/json'},
                    body:JSON.stringify(body)
                }).then(function(r){return r.json()}).then(function(resp){
                    if(resp.id) {
                        // Push to undo stack for new empty position
                        pushUndoState('Leere Zeile hinzugefügt', [{
                            position_id: resp.id, 
                            pos_nr: '', 
                            old: {id: resp.id}, 
                            new: {_action: 'add_empty', id: resp.id}
                        }]);
                        reloadWithMarked([resp.id]);
                    } else {
                        reloadWithMarked([]);
                    }
                });
                return;
            }
            var saveIdx=dropIdx;
            var useEnd=shouldAppendEnd();
            var ids;
            try{ids=JSON.parse(raw);}catch(ex){ids=null;}
            if(!ids||!Array.isArray(ids)||ids.length===0)return;
            if(window._dragFromLV){
                var body={ids:ids.map(Number)};
                if(!useEnd&&saveIdx>=0)body.insert_idx=saveIdx;
                // Push to undo stack BEFORE fetch for LV additions
                var insertLine = saveIdx >= 0 ? saveIdx : (document.querySelectorAll('#pos-sortable tr').length || 1);
                fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/batch-add',{
                    method:'POST',headers:{'Content-Type':'application/json'},
                    body:JSON.stringify(body)
                }).then(function(r){return r.json()}).then(function(resp){
                    var ids2=resp.added?resp.added.map(function(p){return p.id;}):ids;
                    // Push to undo stack AFTER fetch for new positions - ONE ENTRY PER POSITION
                    if(resp.added && resp.added.length > 0) {
                        resp.added.forEach(function(pos, i) {
                            var desc = '+ Pos ' + (pos.pos_nr || pos.id) + ' via Drag&Drop aus LV (Zeile ' + (insertLine + i) + ')';
                            if(pos.kurztext) desc += ' - ' + pos.kurztext.substring(0,25);
                            pushUndoState(desc, [{
                                position_id: pos.id, 
                                pos_nr: pos.pos_nr || '', 
                                old: {id: null},
                                new: {_action: 'add', id: pos.id}
                            }]);
                        });
                    } else if(ids2 && ids2.length > 0) {
                        pushUndoState(ids2.length + ' Position(en) aus LV hinzugefügt', ids2.map(function(id) {
                            return {position_id: id, pos_nr: '', old: {id: null}, new: {_action: 'add', id: id}};
                        }));
                    }
                    // Mark ALL added positions after LV drag & drop
                    if(ids2 && ids2.length > 0) {
                        sessionStorage.setItem('marked_pos_' + AUFMASS_ID, JSON.stringify(ids2));
                    }
                    reloadWithMarked(ids2);});
            }else if(window._dragFromTable){
                var body={ids:ids.map(function(x){return parseInt(x,10);})};
                if(!useEnd && typeof saveIdx === 'number' && saveIdx >= 0){
                    body.insert_idx = saveIdx;
                }
                fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/batch-reorder',{
                    method:'POST',
                    headers:{'Content-Type':'application/json'},
                    body:JSON.stringify(body)
                }).then(function(r){return r.json()}).then(function(resp){
                    if(resp && resp.status === 'ok') {
                        reloadWithMarked(ids);
                    }
                }).catch(function(e){
                    console.error('batch-reorder error:', e);
                });
            }
            window._dragFromLV=false;window._dragFromTable=false;
        }
        function handlePosDragOver(e){
            e.preventDefault();
            if(window._dragFromLV||window._dragFromTable){
                e.dataTransfer.dropEffect=window._dragFromLV?'copy':'move';
            }else{
                e.dataTransfer.dropEffect='copy';
            }
        }
        if(posContainer){
            posContainer.addEventListener('dragover',handlePosDragOver);
            posContainer.addEventListener('drop',handlePosDrop);
        }
        posBody.addEventListener('dragover',handlePosDragOver);
        posBody.addEventListener('drop',handlePosDrop);
        // === Context-Menu per Rechtsklick ===
        posBody.addEventListener('contextmenu',function(e){
            var row=e.target.closest('tr');
            if(!row)return;
            if(e.target.tagName==='A'||e.target.tagName==='BUTTON')return;
            var idx=Array.from(posBody.children).indexOf(row);
            if(!row.classList.contains('is-trenner')&&!e.ctrlKey&&!e.shiftKey){
                if(!rightSelectedSet.has(row.dataset.id)){
                    clearRightMultiSelect();
                    rightSelectedSet.add(row.dataset.id);
                    row.classList.add('is-selected-right');
                    updateRightCount();
                }
            }
            rightLastClickedIdx=idx;
            showContextMenu(e);
        });
    }
    
    // === Document-weite Handler nur für den visuellen Indicator ===
    document.addEventListener('dragover',function(e){
        var table=document.getElementById('pos-table');
        if(!table)return;
        var r=table.getBoundingClientRect();
        if(e.clientX>=r.left&&e.clientX<=r.right&&e.clientY>=r.top&&e.clientY<=r.bottom){
            showDropIndicator(e);
        }else if(dropIndicator.style.display!=='none'){
            hideDropIndicator();
        }
    });
    document.addEventListener('dragleave',function(e){
        if(!e.relatedTarget||e.relatedTarget===document.documentElement)hideDropIndicator();
    });
    document.addEventListener('dragend',function(){window._dragFromLV=false;window._dragFromTable=false;hideDropIndicator();});
    
    // === Farben laden ===
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/farben').then(function(r){return r.json()}).then(function(farben){
        Object.keys(farben).forEach(function(pid){
            var row=document.querySelector('#pos-sortable tr[data-id="'+pid+'"]');
            if(!row)return;
            var cols=farben[pid];
            if(cols.all){row.style.background=cols.all;return;}
            if(cols.pos_nr){
                var td=row.querySelector('td:nth-child(4)');
                if(td)td.style.background=cols.pos_nr;td.style.color='#fff';
            }
            if(cols.menge){
                var td=row.querySelector('td:nth-child(9)');
                if(td)td.style.background=cols.menge;
            }
            if(cols.laenge){
                var td=row.querySelector('td:nth-child(6)');
                if(td)td.style.background=cols.laenge;
            }
            if(cols.breite){
                var td=row.querySelector('td:nth-child(7)');
                if(td)td.style.background=cols.breite;
            }
            if(cols.tiefe){
                var td=row.querySelector('td:nth-child(8)');
                if(td)td.style.background=cols.tiefe;
            }
        });
    });
});

/* === Spalten-Filter (Sortieren + Filtern) === */
var filters={}; // { field: { search:'', sort:null, values:Set|null } }
var activeFilterField=null;

function showFilterMenu(field,th){
    hideFilterMenu();
    var menu=document.getElementById('filter-menu');
    activeFilterField=field;
    if(!filters[field])filters[field]={search:'',sort:null,values:null};
    var rect=th.getBoundingClientRect();
    menu.style.left=rect.left+'px';
    menu.style.top=(rect.bottom+2)+'px';
    menu.style.display='block';
    document.getElementById('filter-values').style.display='';
    // Search vorbelegen
    var inp=document.getElementById('filter-search');
    inp.value=filters[field].search||'';
    // Checkboxen bauen
    buildFilterCheckboxes(field);
}
function hideFilterMenu(){
    document.getElementById('filter-menu').style.display='none';
    activeFilterField=null;
}
function buildFilterCheckboxes(field){
    var div=document.getElementById('filter-checkboxes');
    div.innerHTML='';
    var rows=document.querySelectorAll('#pos-sortable tr[data-id]');
    var values=new Set();
    rows.forEach(function(r){
        var v=String(r.getAttribute('data-'+field)||'');
        if(v)values.add(v);
    });
    var arr=Array.from(values).sort(function(a,b){
        var an=parseFloat(a.replace(',','.'));var bn=parseFloat(b.replace(',','.'));
        if(!isNaN(an)&&!isNaN(bn))return an-bn;
        return a.localeCompare(b);
    });
    arr.unshift('');
    values=arr;
    var search=(filters[field].search||'').toLowerCase();
    var currentVal=filters[field].values;
    values.forEach(function(v){
        if(search&&!v.toLowerCase().includes(search))return;
        var lb=document.createElement('label');
        var cb=document.createElement('input');
        cb.type='checkbox';
        cb.checked=currentVal===null||currentVal.has(v);
        cb.onchange=function(){filterToggleValue(field,v);};
        lb.appendChild(cb);
        lb.appendChild(document.createTextNode(' '+(v||'(leer)')));
        div.appendChild(lb);
    });
}
function filterToggleValue(field,value){
    if(!filters[field])filters[field]={search:'',sort:null,values:null};
    if(!filters[field].values)filters[field].values=new Set();
    if(filters[field].values.has(value)){
        filters[field].values.delete(value);
        if(!filters[field].values.size)filters[field].values=null;
    }else{
        filters[field].values.add(value);
        // Prüfen ob alle Werte ausgewählt → null (kein Filter)
        var all=document.querySelectorAll('#filter-checkboxes input[type=checkbox]');
        var allChecked=true;
        all.forEach(function(cb){if(!cb.checked)allChecked=false;});
        if(allChecked)filters[field].values=null;
    }
    applyFilters();
}
function filterSearch(val){
    if(!activeFilterField)return;
    if(!filters[activeFilterField])filters[activeFilterField]={search:'',sort:null,values:null};
    filters[activeFilterField].search=val;
    buildFilterCheckboxes(activeFilterField);
    applyFilters();
}
function filterSelectAll(){
    if(!activeFilterField)return;
    if(!filters[activeFilterField])filters[activeFilterField]={search:'',sort:null,values:null};
    filters[activeFilterField].values=null; // null = kein Filter
    applyFilters();
    buildFilterCheckboxes(activeFilterField);
}
function filterSelectNone(){
    if(!activeFilterField)return;
    if(!filters[activeFilterField])filters[activeFilterField]={search:'',sort:null,values:null};
    filters[activeFilterField].values=new Set(); // leeres Set = nichts zeigen
    applyFilters();
    buildFilterCheckboxes(activeFilterField);
}
function filterSort(dir){
    if(!activeFilterField)return;
    if(!filters[activeFilterField])filters[activeFilterField]={search:'',sort:null,values:null};
    filters[activeFilterField].sort=dir;
    applyFilters();
    hideFilterMenu();
}
function applyFilters(){
    // Sort- und Filter-Status visualisieren (fbtn active)
    var all=document.querySelectorAll('#pos-table thead th[data-field]');
    all.forEach(function(th){
        var f=th.getAttribute('data-field');
        var btn=th.querySelector('.fbtn');
        if(btn){
            var st=filters[f];
            var active=st&&((st.sort)||(st.search)||(st.values));
            btn.classList.toggle('active',!!active);
        }
    });
    var rows=Array.from(document.querySelectorAll('#pos-sortable tr'));
    // Filter anwenden
    rows.forEach(function(r){
        if(!r.dataset.id){r.style.display='';return;}
        var show=true;
        Object.keys(filters).forEach(function(f){
            var st=filters[f];
            if(!st)return;
            // Text-Suche
            if(st.search){
                var val=String(r.getAttribute('data-'+f)||'');
                if(val.toLowerCase().indexOf(st.search.toLowerCase())===-1){show=false;return;}
            }
            // Wert-Liste
            if(st.values!==null){
                var val2=String(r.getAttribute('data-'+f)||'');
                if(!st.values.has(val2)){show=false;return;}
            }
        });
        r.style.display=show?'':'none';
    });
    // Sortieren
    var sortField=null,sortDir=null;
    Object.keys(filters).forEach(function(f){
        if(filters[f].sort){sortField=f;sortDir=filters[f].sort;}
    });
    if(sortField&&sortDir){
        var parent=document.getElementById('pos-sortable');
        var allSorted=rows.filter(function(r){return r.dataset.id;}).sort(function(a,b){
            var va=String(a.getAttribute('data-'+sortField)||'');
            var vb=String(b.getAttribute('data-'+sortField)||'');
            var na=parseFloat(va.replace(',','.'));var nb=parseFloat(vb.replace(',','.'));
            if(!isNaN(na)&&!isNaN(nb)){
                return sortDir==='asc'?na-nb:nb-na;
            }
            return sortDir==='asc'?va.localeCompare(vb):vb.localeCompare(va);
        });
        // Trenner an ihren Positionen lassen, nur Datenzeilen umsortieren
        var trenner=rows.filter(function(r){return r.classList.contains('is-trenner');});
        // Alle Zeilen neu anordnen: Trenner bleiben, Datenzeilen dazwischen einsortiert
        var newOrder=[];
        var dataIdx=0;
        rows.forEach(function(r){
            if(r.classList.contains('is-trenner')){
                newOrder.push(r);
            }else{
                if(dataIdx<allSorted.length)newOrder.push(allSorted[dataIdx++]);
            }
        });
        newOrder.forEach(function(r){parent.appendChild(r);});
    }
    // Reset-Button-Status
    var hasActive=Object.keys(filters).some(function(f){
        var st=filters[f];return st&&(st.sort||st.search||st.values!==null);
    });
    document.getElementById('btn-reset-filter').style.display=hasActive?'':'none';
}
function filterReset(){
    filters={};
    activeFilterField=null;
    document.getElementById('filter-search').value='';
    hideFilterMenu();
    // Alle Zeilen zeigen + Sortierung zurücksetzen via Reload (einfachster Weg)
    location.reload();
}
// Filter-Menü schließen bei Klick außerhalb
document.addEventListener('click',function(e){
    var menu=document.getElementById('filter-menu');
    if(menu&&menu.style.display!=='none'&&!menu.contains(e.target)&&!e.target.closest('th[data-field]')){
        hideFilterMenu();
    }
});
/* === LV-Tabellen Filter === */
var lvFilters={};
var activeLVFilterField=null;
function showLVFilterMenu(field,th){
    hideLVFilterMenu();
    var menu=document.getElementById('filter-menu');
    activeLVFilterField=field;
    if(!lvFilters[field])lvFilters[field]={search:'',sort:null};
    var rect=th.getBoundingClientRect();
    menu.style.left=rect.left+'px';
    menu.style.top=(rect.bottom+2)+'px';
    menu.style.display='block';
    var inp=document.getElementById('filter-search');
    inp.value=lvFilters[field].search||'';
    // Nur Sortierung + Suche, keine Checkboxen (LV hat zu viele unique values)
    document.getElementById('filter-values').style.display='none';
}
function hideLVFilterMenu(){activeLVFilterField=null;}
function applyLVFilters(){
    var all=document.querySelectorAll('#lv-table thead th[data-field]');
    all.forEach(function(th){
        var f=th.getAttribute('data-field');
        var btn=th.querySelector('.fbtn');
        if(btn){
            var st=lvFilters[f];
            btn.classList.toggle('active',!!(st&&(st.sort||st.search)));
        }
    });
    var rows=Array.from(document.querySelectorAll('#lv-list .lv-item'));
    rows.forEach(function(r){
        var show=true;
        Object.keys(lvFilters).forEach(function(f){
            var st=lvFilters[f];
            if(!st)return;
            if(st.search){
                var val=String(r.getAttribute('data-'+f)||'');
                if(val.toLowerCase().indexOf(st.search.toLowerCase())===-1){show=false;return;}
            }
        });
        r.style.display=show?'':'none';
        r.classList.toggle('filter-hidden',!show);
    });
    // Sortieren
    var sortField=null,sortDir=null;
    Object.keys(lvFilters).forEach(function(f){
        if(lvFilters[f].sort){sortField=f;sortDir=lvFilters[f].sort;}
    });
    if(sortField&&sortDir){
        var parent=document.getElementById('lv-list');
        var sorted=rows.filter(function(r){return r.style.display!=='none';});
        sorted.sort(function(a,b){
            var va=String(a.getAttribute('data-'+sortField)||'');
            var vb=String(b.getAttribute('data-'+sortField)||'');
            var na=parseFloat(va.replace(',','.'));var nb=parseFloat(vb.replace(',','.'));
            if(!isNaN(na)&&!isNaN(nb))return sortDir==='asc'?na-nb:nb-na;
            return sortDir==='asc'?va.localeCompare(vb):vb.localeCompare(va);
        });
        sorted.forEach(function(r){parent.appendChild(r);});
    }
    updateLVCount();
}
// LV filter nutzt die gleichen Buttons (Sort) aus dem filter-menu
// Überschreibe filterSort für LV
var _origFilterSort=filterSort;
filterSort=function(dir){
    if(activeLVFilterField!==null&&activeLVFilterField!==undefined){
        if(!lvFilters[activeLVFilterField])lvFilters[activeLVFilterField]={search:'',sort:null};
        lvFilters[activeLVFilterField].sort=dir;
        applyLVFilters();
        hideFilterMenu();
        hideLVFilterMenu();
    }else{
        _origFilterSort(dir);
    }
};
var _origFilterSearch=filterSearch;
filterSearch=function(val){
    var menu=document.getElementById('filter-menu');
    if(activeLVFilterField!==null&&activeLVFilterField!==undefined){
        if(!lvFilters[activeLVFilterField])lvFilters[activeLVFilterField]={search:'',sort:null};
        lvFilters[activeLVFilterField].search=val;
        applyLVFilters();
    }else{
        _origFilterSearch(val);
    }
};
// LV filter zurücksetzen + Checkboxen wieder zeigen wenn POS-Filter
var _origHideFilterMenu=hideFilterMenu;
hideFilterMenu=function(){
    _origHideFilterMenu();
    document.getElementById('filter-values').style.display='';
    activeLVFilterField=null;
};
/* === Context-Menu === */
function showContextMenu(e){
    e.preventDefault();
    var menu=document.getElementById('pos-context-menu');
    if(!menu)return;
    menu.style.left=e.clientX+'px';
    menu.style.top=e.clientY+'px';
    menu.style.display='block';
}
function hideContextMenu(){
    var menu=document.getElementById('pos-context-menu');
    if(menu)menu.style.display='none';
}
document.addEventListener('click',function(e){
    var menu=document.getElementById('pos-context-menu');
    if(menu&&menu.style.display!=='none'&&!menu.contains(e.target))hideContextMenu();
});
document.addEventListener('keydown',function(e){
    if(e.key==='Escape')hideContextMenu();
});
/* === Edit Modal === */
function openEdit(posId){
    const row=document.querySelector('#pos-sortable tr[data-id="'+posId+'"]');
    if(!row)return;
    const d=row.dataset;
    var ft=d.formelTyp||'standard';
    document.getElementById('pos-edit-content').innerHTML=
        '<form method="POST" action="/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/position/'+posId+'/aktualisieren">'+
        '<div class="field"><label class="label">Pos-Nr</label><input class="input" name="pos_nr" value="'+esc(d.posnr)+'"></div>'+
        '<div class="field"><label class="label">Kurztext</label><input class="input" name="kurztext" value="'+esc(d.kurztext)+'"></div>'+
        '<div class="field"><label class="label">Einheit</label><input class="input" name="einheit" value="'+esc(d.einheit)+'"></div>'+
        '<div class="field"><label class="label">Einzelpreis (€)</label><input class="input" name="einzelpreis" type="number" step="0.01" value="'+d.ep+'"></div>'+
        '<div class="field"><label class="label">RSA-Abschnitt</label><input class="input" name="abschnitt" value="'+esc(d.abschnitt||'')+'"></div>'+
        '<div class="field"><label class="label">Formelart</label><div>'+
        '<label class="radio"><input type="radio" name="formel_typ" value="standard" '+(ft==='standard'?'checked':'')+' onchange="editFormelTyp(this.value)"> Standard</label>'+
        '<label class="radio"><input type="radio" name="formel_typ" value="frei" '+(ft==='frei'?'checked':'')+' onchange="editFormelTyp(this.value)"> Freie Formel (Z91)</label></div></div>'+
        '<div id="edit-std" style="display:'+(ft==='standard'?'':'none')+'">'+
        '<div class="columns"><div class="column"><label class="label">Faktor</label><input class="input" name="faktor" type="number" step="0.01" value="'+d.faktor+'"></div>'+
        '<div class="column"><label class="label">Länge (m)</label><input class="input" name="laenge" type="number" step="0.01" value="'+d.laenge+'"></div>'+
        '<div class="column"><label class="label">Breite (m)</label><input class="input" name="breite" type="number" step="0.01" value="'+d.breite+'"></div>'+
        '<div class="column"><label class="label">Tiefe (m)</label><input class="input" name="tiefe" type="number" step="0.01" value="'+d.tiefe+'"></div></div></div>'+
        '<div id="edit-frei" style="display:'+(ft==='frei'?'':'none')+'">'+
        '<div class="field"><label class="label">Formel (Z91)</label><input class="input" name="formel" value="'+esc(d.formel||'')+'" placeholder="2*1+1"></div></div>'+
        '<div class="field"><label class="label">Bemerkung</label><textarea class="textarea" name="bemerkung" rows="2">'+esc(d.bemerkung)+'</textarea></div>'+
        '<button class="button is-primary" type="submit">Speichern & Neu berechnen</button>'+
        '<button class="button is-light" type="button" onclick="closeEdit()">Abbrechen</button></form>';
    document.getElementById('pos-edit-modal').classList.add('is-active');
}
window.editFormelTyp=function(v){
    document.getElementById('edit-std').style.display=v==='standard'?'':'none';
    document.getElementById('edit-frei').style.display=v==='frei'?'':'none';
};
function closeEdit(){document.getElementById('pos-edit-modal').classList.remove('is-active');}
function esc(s){if(!s)return '';return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}

/* === Multi-Edit === */
function openMultiEdit(){
    if (rightSelectedSet.size === 0) return;
    // Sortiere IDs nach ihrer Position in der Tabelle (data-sort Attribut)
    var ids = Array.from(rightSelectedSet).sort(function(a, b){
        var rowA = document.querySelector('#pos-sortable tr[data-id="'+a+'"]');
        var rowB = document.querySelector('#pos-sortable tr[data-id="'+b+'"]');
        var sortA = rowA ? parseInt(rowA.dataset.sort) || 0 : 0;
        var sortB = rowB ? parseInt(rowB.dataset.sort) || 0 : 0;
        return sortA - sortB;
    });
    var rows = [];
    ids.forEach(function(id){
        var r = document.querySelector('#pos-sortable tr[data-id="'+id+'"]');
        if (!r) return;
        var d = r.dataset;
        var ft = d.formelTyp || 'standard';
        rows.push({
            id: id,
            posnr: d.posnr||'',
            kurztext: d.kurztext||'',
            einheit: d.einheit||'',
            ep: d.ep||'0',
            gp: d.gp||'0',
            faktor: d.faktor||'1',
            laenge: d.laenge||'0',
            breite: d.breite||'0',
            tiefe: d.tiefe||'0',
            formel: d.formel||'',
            menge: d.menge||'0',
            menge_hinten: d.mengeHinten||'0',
            abschnitt: d.abschnitt||'',
            bemerkung: d.bemerkung||'',
            formelTyp: ft
        });
    });
    if (rows.length === 0) return;
    function _gf(v,p){var s=germanNum(v,p);return s!==''?s:'0';}
    window._meOrigData = {};
    rows.forEach(function(r){ 
        window._meOrigData[r.id] = {faktor: r.faktor, laenge: r.laenge, breite: r.breite, tiefe: r.tiefe, menge: r.menge, menge_hinten: r.menge_hinten, formel: r.formel, posnr: r.posnr}; 
    });
    function _rel(eh,f){
        if(f==='faktor')return true;
        if(f==='laenge')return !/^(ST|LE|STD|h|Psch)$/.test(eh);
        if(f==='breite')return eh==='M2'||eh==='M3';
        if(f==='tiefe')return eh==='M3';
        return true;
    }
    var html = '<div style="overflow-x:auto"><table class="table is-fullwidth is-hoverable" id="me-table" style="font-size:0.85rem;table-layout:fixed;white-space:nowrap"><thead><tr>' +
        '<th style="width:30px">#</th><th style="width:45px">Z-Art</th><th style="width:55px">Abschn</th>' +
        '<th style="width:50px">Faktor</th><th style="width:50px">Länge</th><th style="width:50px">Breite</th><th style="width:50px">Tiefe</th>' +
        '<th style="width:60px">Menge</th><th style="width:55px">EH</th><th style="width:100px">Kurztext</th><th style="width:75px">Bemerkung</th>' +
        '<th style="width:60px">M-hinten</th><th style="width:60px">EP</th><th style="width:65px">GP</th><th style="width:30px">✕</th>' +
        '</tr></thead><tbody>';
    rows.forEach(function(r, idx){
        var ft = r.formelTyp;
        var isFrei = ft === 'frei';
        var stUnit = /^(ST|LE|STD|h|Psch)$/.test(r.einheit);
        function _inp(field,val,width,css){return'<input class="input is-small me-field me-recalc" data-id="'+r.id+'" data-field="'+field+'" value="'+val+'" style="width:'+width+';font-size:0.8rem;padding:1px 4px;height:26px'+(css?css:'')+'" inputmode="decimal" oninput="meRecalc(\''+r.id+'\')">';}
        var la = _rel(r.einheit,'laenge') ? _inp('laenge',_gf(r.laenge,3),'55px') : '<span class="me-na" style="color:#888">–</span>';
        var br = _rel(r.einheit,'breite') ? _inp('breite',_gf(r.breite,3),'55px') : '<span class="me-na" style="color:#888">–</span>';
        var ti = _rel(r.einheit,'tiefe') ? _inp('tiefe',_gf(r.tiefe,3),'55px') : '<span class="me-na" style="color:#888">–</span>';
        html += '<tr id="me-row-'+r.id+'">' +
            '<td style="text-align:center;color:#888;font-size:0.7rem">'+esc(r.posnr)+'</td>' +
            '<td><select class="is-small me-field me-recalc" data-id="'+r.id+'" data-field="formel_typ" style="width:45px;font-size:0.8rem;padding:1px" onchange="meRecalc(\''+r.id+'\')">' +
                '<option value="standard"'+(ft==='standard'?' selected':'')+'>Std</option>'+
                '<option value="frei"'+(ft==='frei'?' selected':'')+'>Z91</option></select></td>' +
            '<td><input class="input is-small me-field" data-id="'+r.id+'" data-field="abschnitt" value="'+esc(r.abschnitt)+'" style="width:60px;font-size:0.8rem;padding:1px 4px;height:26px"></td>' +
            '<td>'+(isFrei?'<span class="me-na" style="color:#888">–</span>':_inp('faktor',_gf(r.faktor,3),'55px'))+'</td>' +
            '<td>'+(isFrei?
                '<input class="input is-small me-field me-recalc" data-id="'+r.id+'" data-field="formel" value="'+esc(r.formel||'')+'" style="width:100%;font-size:0.8rem;padding:1px 4px;height:26px;font-family:monospace;background:#e8f4fd" oninput="meRecalc(\''+r.id+'\')">' :
                la)+'</td>' +
            '<td>'+(isFrei?'<span class="me-na" style="color:#888">–</span>':br)+'</td>' +
            '<td>'+(isFrei?'<span class="me-na" style="color:#888">–</span>':ti)+'</td>' +
            // Menge: editierbar bei ST/LE/STD/h/Psch, sonst berechnet
            '<td>'+(stUnit?'<input class="input is-small me-field me-recalc" data-id="'+r.id+'" data-field="menge" value="'+_gf(r.menge,3)+'" style="width:60px;font-size:0.8rem;padding:1px 4px;height:26px;background:#fff3cd" inputmode="decimal" oninput="meRecalc(\''+r.id+'\')">':'<span class="me-menge" style="font-size:0.85rem;font-weight:600">'+_gf(r.menge,3)+'</span>')+'</td>' +
            '<td><input class="input is-small me-field me-recalc" data-id="'+r.id+'" data-field="einheit" value="'+esc(r.einheit)+'" style="width:70px;font-size:0.8rem;padding:1px 4px;height:26px" onchange="meRecalc(\''+r.id+'\')"></td>' +
            '<td style="overflow:hidden;font-size:0.8rem;padding:0 4px;white-space:nowrap">'+esc(r.kurztext)+'</td>' +
            '<td style="overflow:hidden"><input class="input is-small me-field" data-id="'+r.id+'" data-field="bemerkung" value="'+esc(r.bemerkung)+'" style="width:100%;font-size:0.8rem;padding:1px 4px;height:26px"></td>' +
            '<td><input class="input is-small me-field me-recalc" data-id="'+r.id+'" data-field="menge_hinten" value="'+_gf(r.menge_hinten,3)+'" style="width:55px;font-size:0.8rem;padding:1px 4px;height:26px" inputmode="decimal" oninput="meRecalc(\''+r.id+'\')"></td>' +
            '<td class="me-ep" style="font-size:0.8rem">'+_gf(r.ep,2)+'</td>' +
            '<td class="me-gp" style="font-size:0.85rem;font-weight:600">'+_gf(r.gp,2)+'</td>' +
            '<td></td></tr>';
    });
    html += '</tbody></table></div>';
    document.getElementById('multi-edit-content').innerHTML = html;
    // Unit-basierte Amber-Markierung (welche Felder sind relevant)
    rows.forEach(function(r){
        var row = document.getElementById('me-row-'+r.id);
        if (!row || r.formelTyp === 'frei') return;
        var eh = r.einheit;
        if (eh === 'M') {
            var el = row.querySelector('.me-recalc[data-field="laenge"]');
            if (el) el.style.background = '#fff3cd';
        } else if (eh === 'M2') {
            var els = ['laenge','breite'];
            els.forEach(function(f){ var el=row.querySelector('.me-recalc[data-field="'+f+'"]'); if(el)el.style.background='#fff3cd'; });
        } else if (eh === 'M3') {
            ['laenge','breite','tiefe'].forEach(function(f){ var el=row.querySelector('.me-recalc[data-field="'+f+'"]'); if(el)el.style.background='#fff3cd'; });
        }
    });
    document.getElementById('multi-edit-modal').classList.add('is-active');
}
function closeMultiEdit(){
    document.getElementById('multi-edit-modal').classList.remove('is-active');
}
function meRecalc(id){
    var row = document.getElementById('me-row-'+id);
    if (!row) return;
    var inputs = row.querySelectorAll('.me-recalc');
    var vals = {};
    inputs.forEach(function(inp){ vals[inp.dataset.field] = inp.value; });
    var ft = vals.formel_typ || 'standard';
    var eh = vals.einheit || '';
    var stUnit = /^(ST|LE|STD|h|Psch)$/.test(eh);
    var faktor = parseFloat((vals.faktor||'1').replace(',','.')) || 1;
    var laenge = parseFloat((vals.laenge||'0').replace(',','.')) || 0;
    var breite = parseFloat((vals.breite||'0').replace(',','.')) || 0;
    var tiefe = parseFloat((vals.tiefe||'0').replace(',','.')) || 0;
    var menge_val = parseFloat((vals.menge||'0').replace(',','.')) || 0;
    var menge_hinten = parseFloat((vals.menge_hinten||'0').replace(',','.')) || 0;
    var ep = parseFloat((row.querySelector('.me-ep')?.textContent||'0').replace(',','.').replace('–','')) || 0;
    var menge;
    if (ft === 'frei') {
        // Clientseitige Formel-Auswertung für Live-Vorschau
        var formelStr = vals.formel || '';
        menge = 0;
        if (formelStr.trim()) {
            var san = formelStr.replace(/,/g,'.').replace(/\s/g,'');
            if (/^[\d+\-*/().]+$/.test(san)) {
                try { var c = Function('"use strict"; return ('+san+')')(); if(typeof c==='number'&&isFinite(c)) menge=c; } catch(e) {}
            }
        }
    } else if (stUnit) {
        menge = menge_val;
    } else if (eh === 'M') {
        menge = laenge;
    } else if (eh === 'M2') {
        menge = laenge * breite;
    } else if (eh === 'M3') {
        menge = laenge * breite * tiefe;
    } else {
        menge = laenge;
    }
    // Server berechnet menge_hinten = faktor * menge
    var newMh = ft === 'frei' ? menge : faktor * menge;
    var gp = newMh * ep;
    // menge_hinten-Eingabe aktualisieren
    var mhInput = row.querySelector('[data-field="menge_hinten"]');
    if (mhInput) mhInput.value = newMh.toFixed(3).replace('.',',');
    var mengeEl = row.querySelector('.me-menge');
    if (mengeEl) {
        mengeEl.textContent = menge.toFixed(3).replace('.',',');
    } else {
        var mengeInput = row.querySelector('.me-recalc[data-field="menge"]');
        if (mengeInput) mengeInput.value = menge.toFixed(3).replace('.',',');
    }
    var gpEl = row.querySelector('.me-gp');
    if (gpEl) gpEl.textContent = gp.toFixed(2).replace('.',',');
    // Bei Einheit- oder Z-Art-Wechsel: Inputs umbauen
    var needsRebuild = false;
    if (eh !== row.dataset.prevUnit) { row.dataset.prevUnit = eh; needsRebuild = true; }
    if (ft !== row.dataset.prevFt) { row.dataset.prevFt = ft; needsRebuild = true; }
    if (needsRebuild) {
        rebuildRow(id, ft, stUnit);
        // Unit-basierte Amber-Markierung nach Neuaufbau
        if (ft !== 'frei') {
            if (eh === 'M') {
                var el = row.querySelector('.me-recalc[data-field="laenge"]');
                if (el) el.style.background = '#fff3cd';
            } else if (eh === 'M2') {
                ['laenge','breite'].forEach(function(f){
                    var el=row.querySelector('.me-recalc[data-field="'+f+'"]');
                    if(el)el.style.background='#fff3cd';
                });
            } else if (eh === 'M3') {
                ['laenge','breite','tiefe'].forEach(function(f){
                    var el=row.querySelector('.me-recalc[data-field="'+f+'"]');
                    if(el)el.style.background='#fff3cd';
                });
            }
        }
    }
}
function rebuildRow(id, ft, stUnit){
    var row = document.getElementById('me-row-'+id);
    if (!row) return;
    var isFrei = ft === 'frei';
    var hideFLT = isFrei;
    function _gn(v){ var s = germanNum(v,3); return s !== '' ? s : '0'; }
    var ehInput = row.querySelector('.me-recalc[data-field="einheit"]');
    var ehVal = ehInput ? ehInput.value : '';
    function _rel(f){
        if(f==='faktor')return true;
        if(f==='laenge')return !/^(ST|LE|STD|h|Psch)$/.test(ehVal);
        if(f==='breite')return ehVal==='M2'||ehVal==='M3';
        if(f==='tiefe')return ehVal==='M3';
        return true;
    }
    ['faktor','laenge','breite','tiefe'].forEach(function(f){
        var colIdx = {faktor:4, laenge:5, breite:6, tiefe:7}[f];
        var td = row.querySelector('td:nth-child('+colIdx+')');
        if (!td) return;
        var inp = td.querySelector('.me-recalc[data-field="'+f+'"]');
        var isRel = !hideFLT && _rel(f);
        if (inp) {
            if (!isRel) { td.innerHTML = '<span class="me-na" style="color:#888">–</span>'; }
        } else {
            var span = td.querySelector('.me-na');
            if (span && isRel) {
                var orig = window._meOrigData ? window._meOrigData[id] : null;
                var val = (orig && orig[f]) ? _gn(orig[f]) : '0';
                td.innerHTML = '<input class="input is-small me-field me-recalc" data-id="'+id+'" data-field="'+f+'" value="'+val+'" style="width:55px;font-size:0.8rem;padding:1px 4px;height:26px" inputmode="decimal" oninput="meRecalc(\''+id+'\')">';
            }
        }
    });
    // Formel-Umschaltung: Länge ↔ Formel
    if (isFrei) {
        if (!row.querySelector('[data-field="formel"]')) {
            var laengeTd = row.querySelector('td:nth-child(5)');
            if (laengeTd) {
                var orig2 = window._meOrigData ? window._meOrigData[id] : null;
                var fv = (orig2 && orig2.formel !== undefined) ? esc(orig2.formel) : '';
                laengeTd.innerHTML = '<input class="input is-small me-field me-recalc" data-id="'+id+'" data-field="formel" value="'+fv+'" style="width:100%;font-size:0.8rem;padding:1px 4px;height:26px;font-family:monospace;background:#e8f4fd" oninput="meRecalc(\''+id+'\')">';
            }
        }
    } else {
        var formelInp = row.querySelector('[data-field="formel"]');
        if (formelInp) {
            var td = formelInp.parentElement;
            var orig2 = window._meOrigData ? window._meOrigData[id] : null;
            var lv = (orig2 && orig2.laenge) ? _gn(orig2.laenge) : '0';
            td.innerHTML = '<input class="input is-small me-field me-recalc" data-id="'+id+'" data-field="laenge" value="'+lv+'" style="width:55px;font-size:0.8rem;padding:1px 4px;height:26px" inputmode="decimal" oninput="meRecalc(\''+id+'\')">';
        }
    }
    // Menge umbauen
    var mengeCell = row.querySelector('td:nth-child(8)');
    if (mengeCell) {
        var orig = window._meOrigData ? window._meOrigData[id] : null;
        if (stUnit) {
            if (!mengeCell.querySelector('input')) {
                var val = (orig && orig.menge) ? _gn(orig.menge) : '0';
                mengeCell.innerHTML = '<input class="input is-small me-field me-recalc" data-id="'+id+'" data-field="menge" value="'+val+'" style="width:60px;font-size:0.8rem;padding:1px 4px;height:26px;background:#fff3cd" inputmode="decimal" oninput="meRecalc(\''+id+'\')">';
            }
        } else {
            if (!mengeCell.querySelector('.me-menge')) {
                var val = (orig && orig.menge) ? _gn(orig.menge) : '0';
                mengeCell.innerHTML = '<span class="me-menge" style="font-size:0.85rem;font-weight:600">'+val+'</span>';
            }
        }
    }
    meRecalc(id);
}
function saveMultiEdit(){
    try {
        var fields = document.querySelectorAll('.me-field');
        var updates = {};
        fields.forEach(function(inp){
            var id = inp.dataset.id;
            var field = inp.dataset.field;
            var val = inp.value.trim();
            if (!updates[id]) updates[id] = {};
            updates[id][field] = val;
        });
        // Fehlende Felder aus Originaldaten ergänzen (für "–" Platzhalter)
        Object.keys(window._meOrigData || {}).forEach(function(id){
            if (!updates[id]) return;
            var orig = window._meOrigData[id];
            ['faktor','laenge','breite','tiefe','menge_hinten'].forEach(function(f){
                if (!(f in updates[id]) && (f in orig)) {
                    updates[id][f] = orig[f];
                }
            });
        });
        var numFields = {'faktor':1,'laenge':1,'breite':1,'tiefe':1,'menge_hinten':1,'menge':1};
        Object.keys(updates).forEach(function(id){
            Object.keys(updates[id]).forEach(function(f){
                if (numFields[f] && updates[id][f] !== '') {
                    updates[id][f] = parseFloat(String(updates[id][f]).replace(',','.')) || 0;
                }
            });
        });
// Push to undo stack - ONE ENTRY PER POSITION
    Object.keys(updates).forEach(function(id){
        if (window._meOrigData && window._meOrigData[id]) {
            var oldVals = {};
            var changesDesc = [];
            Object.keys(updates[id]).forEach(function(f){
                var oldVal = window._meOrigData[id][f] || null;
                var newVal = updates[id][f];
                oldVals[f] = oldVal;
                if (oldVal !== newVal) {
                    // Create a readable description for this field
                    var fieldLabels = {
                        'faktor': 'Faktor', 'laenge': 'Länge', 'breite': 'Breite', 'tiefe': 'Tiefe',
                        'menge': 'Menge', 'menge_hinten': 'Menge hinten', 'einheit': 'EH',
                        'abschnitt': 'Abschnitt', 'bemerkung': 'Bemerkung', 'formel': 'Formel',
                        'formel_typ': 'Z-Art'
                    };
                    changesDesc.push((fieldLabels[f] || f) + ': ' + (oldVal || 'leer') + ' → ' + (newVal || 'leer'));
                }
            });
            if (changesDesc.length > 0) {
                var posNr = window._meOrigData[id].posnr || ('Pos #' + id);
                var description = posNr + ': ' + changesDesc.join(', ');
                pushUndoState(description, [{position_id: parseInt(id), pos_nr: posNr, old: oldVals, new: updates[id]}]);
            }
        }
    });
        // Schließe Modal sofort
        var modal = document.getElementById('multi-edit-modal');
        if (modal) modal.classList.remove('is-active');
        var btn = document.getElementById('btn-multi-save');
        if (btn) btn.classList.add('is-loading');
        fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/positionen/batch-edit', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({positions: updates})
        }).then(function(r){
            if (!r.ok) {
                throw new Error('Server responded with status ' + r.status);
            }
            return r.text().then(function(text){
                if (!text || text.trim() === '') {
                    return {};
                }
                try {
                    return JSON.parse(text);
                } catch(e) {
                    console.error('Response text:', text);
                    throw new Error('Invalid JSON response: ' + text.substring(0, 200));
                }
            });
        }).then(function(data){
            location.href = location.pathname + '?t=' + Date.now();
        }).catch(function(e){
            alert('Fehler beim Speichern: '+e.message);
            if (btn) btn.classList.remove('is-loading');
        });
    } catch(e) {
        alert('Fehler: '+e.message);
    }
}

/* === Langtext Sidepanel === */
function updateLangPanel(){
    var lt=document.getElementById('form-langtext');
    var btn=document.getElementById('btn-lang-panel');
    if(!lt||!btn)return;
    var hasText=lt.textContent.trim().length>0;
    btn.style.display=hasText?'inline':'none';
    // Sidepanel content immer aktualisieren
    var pc=document.getElementById('lang-panel-content');
    if(pc)pc.textContent=lt.textContent;
}
function openLangPanel(){
    var panel=document.getElementById('am-lang-panel');
    var exp=document.getElementById('lang-expanded');
    if(!panel||!exp)return;
    // Wenn Panel bereits aufgeklappt ist → einklappen
    if(panel.style.display!=='none'&&exp.style.display!=='none'){
        toggleLangPanel(false);
        return;
    }
    var lt=document.getElementById('form-langtext');
    var pc=document.getElementById('lang-panel-content');
    var div=document.getElementById('lang-divider');
    var coll=document.getElementById('lang-collapsed');
    if(!lt||!pc||!coll)return;
    pc.textContent=lt.textContent;
    coll.style.display='none';
    exp.style.display='flex';
    panel.style.display='flex';
    div.style.display='';
    panel.style.width=localStorage.getItem('am_lang_width')||'380px';
    panel.style.minWidth='200px';
}
function toggleLangPanel(forceOpen){
    var panel=document.getElementById('am-lang-panel');
    var div=document.getElementById('lang-divider');
    var coll=document.getElementById('lang-collapsed');
    var exp=document.getElementById('lang-expanded');
    if(!panel||!coll||!exp)return;
    var isOpen=panel.style.display!=='none'&&exp.style.display!=='none';
    if(forceOpen===true&&isOpen)return;
    if(forceOpen===false&&!isOpen)return;
    if(isOpen||forceOpen===false){
        coll.style.display='flex';
        exp.style.display='none';
        panel.style.width='28px';
        panel.style.minWidth='28px';
    }else{
        coll.style.display='none';
        exp.style.display='flex';
        panel.style.display='flex';
        div.style.display='';
        panel.style.width=localStorage.getItem('am_lang_width')||'380px';
        panel.style.minWidth='200px';
    }
}
function closeLangPanel(){
    var panel=document.getElementById('am-lang-panel');
    var div=document.getElementById('lang-divider');
    if(panel)panel.style.display='none';
    if(div)div.style.display='none';
}
/* Sidepanel Resizer */
(function(){
    var div=document.getElementById('lang-divider');
    var panel=document.getElementById('am-lang-panel');
    if(!div||!panel)return;
    var saved=localStorage.getItem('am_lang_width');
    if(saved&&parseInt(saved)>100){panel.style.width=saved+'px';panel.style.minWidth=saved+'px';}
    var drag=false,sx=0;
    div.addEventListener('mousedown',function(e){
        if(panel.style.display==='none')return;
        var exp=document.getElementById('lang-expanded');
        if(!exp||exp.style.display==='none')return;
        drag=true;sx=e.clientX;
        document.body.style.cursor='col-resize';document.body.style.userSelect='none';
    });
    document.addEventListener('mousemove',function(e){
        if(!drag)return;
        var nw=panel.offsetWidth-(e.clientX-sx);
        if(nw>100)panel.style.width=nw+'px';
        sx=e.clientX;
    });
    document.addEventListener('mouseup',function(){
        if(drag){localStorage.setItem('am_lang_width',panel.offsetWidth);drag=false;
        document.body.style.cursor='';document.body.style.userSelect='';}
    });
    // Init: collapsed by default
    var coll=document.getElementById('lang-collapsed');
    var exp=document.getElementById('lang-expanded');
    var panel=document.getElementById('am-lang-panel');
    if(coll&&exp&&panel){exp.style.display='none';coll.style.display='flex';panel.style.display='none';panel.style.width='28px';panel.style.minWidth='28px';}
})();
</script>

{% if positionen %}
<div class="box mt-3">
    <h3 class="title is-5">Mengen- und Positions-Zusammenfassung</h3>
    <div style="overflow-x:auto">
        <table class="table is-fullwidth is-hoverable" style="font-size:0.8rem">
            <thead>
                <tr>
                    <th>Pos-Nr</th>
                    <th>Kurztext</th>
                    <th class="has-text-right">Menge</th>
                    <th class="has-text-right">EP (€)</th>
                    <th class="has-text-right">GP (€)</th>
                </tr>
            </thead>
            <tbody>
                {% set seen = namespace(posns=[]) %}
                {% set totals = namespace(menge=0, gp=0) %}
                {% for pos in positionen if pos.pos_nr %}
                {% if pos.pos_nr not in seen.posns %}
                {% set _ = seen.posns.append(pos.pos_nr) %}
                {% set menge_sum = namespace(val=0) %}
                {% for p in positionen if p.pos_nr == pos.pos_nr %}
                {% set menge_sum.val = menge_sum.val + (p.menge_hinten or p.menge or 0) %}
                {% endfor %}
                {% set totals.menge = totals.menge + menge_sum.val %}
                {% set totals.gp = totals.gp + (positionen|selectattr('pos_nr','equalto',pos.pos_nr)|sum(attribute='gesamtpreis') or 0) %}
                <tr>
                    <td><code>{{ pos.pos_nr }}</code></td>
                    <td>{{ pos.kurztext or '' }}</td>
                    <td class="has-text-right">{{ menge_sum.val|german_number(3) }}</td>
                    <td class="has-text-right">{{ pos.einzelpreis|german_number }}</td>
                    <td class="has-text-right">{{ positionen|selectattr('pos_nr','equalto',pos.pos_nr)|sum(attribute='gesamtpreis')|german_number }}</td>
                </tr>
                {% endif %}
                {% endfor %}
            </tbody>
            <tfoot>
                <tr class="has-text-weight-bold">
                    <td colspan="2">Summe</td>
                    <td class="has-text-right">{{ totals.menge|german_number(3) }}</td>
                    <td></td>
                    <td class="has-text-right">{{ totals.gp|german_number }} €</td>
                </tr>
            </tfoot>
        </table>
    </div>
</div>
{% endif %}
<!-- Modal für Zusatzmodule (volle Bildschirmbreite) -->
<div id="modul-modal" class="modal">
  <div class="modal-background" onclick="var m=document.getElementById('modul-modal');if(m){saveModulFormState();m.classList.remove('is-active')}"></div>
  <div class="modal-card" style="width:auto;max-width:95vw;max-height:90vh">
    <header class="modal-card-head" style="padding:0.5rem">
      <p class="modal-card-title" style="font-size:1rem" id="modul-modal-title">Zusatzmodul</p>
      <button class="delete" onclick="var m=document.getElementById('modul-modal');if(m){saveModulFormState();m.classList.remove('is-active')}"></button>
    </header>
    <section class="modal-card-body" id="modul-modal-body"></section>
  </div>
</div>
<script>
function closeModulModal(){var m=document.getElementById('modul-modal');if(m){saveModulFormState();m.classList.remove('is-active')}}

/* Formularwerte des Modul-Popups speichern/wiederherstellen */
function saveModulFormState(){
  var body=document.getElementById('modul-modal-body');
  if(!body)return;
  var data={};
  body.querySelectorAll('input,textarea,select').forEach(function(el){
    var name=el.getAttribute('name');
    if(!name)return;
    if(el.type==='checkbox'||el.type==='radio'){data[name]=el.checked?'an':''}
    else{data[name]=el.value}
  });
  try{localStorage.setItem('modul_form_state',JSON.stringify(data))}catch(e){}
}
function restoreModulFormState(){
  var raw;
  try{raw=localStorage.getItem('modul_form_state')}catch(e){}
  if(!raw)return;
  var data;
  try{data=JSON.parse(raw)}catch(e){}
  if(!data)return;
  var body=document.getElementById('modul-modal-body');
  if(!body)return;
  body.querySelectorAll('input,textarea,select').forEach(function(el){
    var name=el.getAttribute('name');
    if(!name||data[name]===undefined)return;
    if(el.type==='checkbox'||el.type==='radio'){el.checked=data[name]==='an'}
    else{el.value=data[name]}
  });
}

// ===== Zusatzmodule + Ausgeblendete Details State speichern/wiederherstellen =====
function saveToggleState(){
    var hd = document.getElementById('hidden-details');
    if (hd) try { localStorage.setItem('hidden_details_open', hd.hasAttribute('open') ? '1' : '0'); } catch(e) {}
}
(function(){
    var d = document.getElementById('zusatz-details');
    if (!d) return;
    var s = localStorage.getItem('zusatz_open');
    if (s === '1') d.setAttribute('open', '');
    d.addEventListener('toggle', function(){
        localStorage.setItem('zusatz_open', d.hasAttribute('open') ? '1' : '0');
    });
})();
(function(){
    var d = document.getElementById('hidden-details');
    if (!d) return;
    var s = localStorage.getItem('hidden_details_open');
    if (s === '1') d.setAttribute('open', '');
    d.addEventListener('toggle', function(){
        localStorage.setItem('hidden_details_open', d.hasAttribute('open') ? '1' : '0');
    });
})();

// ===== Multi-Select: Klick toggelt Auswahl (capture phase → vor HTMX) =====
document.addEventListener('click', function(ev){
    var btn = ev.target.closest('.js-mod-btn');
    if (!btn) return;
    var hasHtmx = btn.hasAttribute('hx-get');
    // Bei normalem Klick auf sichtbares Modul: Selektion löschen, HTMX öffnet Formular
    if (!ev.ctrlKey && hasHtmx) {
        document.querySelectorAll('.js-mod-btn.selected').forEach(function(b){ b.classList.remove('selected'); });
        updateBatchUI();
        return;
    }
    // Ctrl+Klick oder Klick auf ausgeblendetes Modul: Selektion toggeln
    ev.preventDefault();
    ev.stopPropagation();
    btn.classList.toggle('selected');
    updateBatchUI();
}, {capture: true});
function updateBatchUI(){
    var sel = document.querySelectorAll('.js-mod-btn.selected');
    var info = document.getElementById('batch-info');
    var count = document.getElementById('sel-count');
    if (!info || !count) return;
    if (sel.length === 0) { info.style.display = 'none'; return; }
    info.style.display = 'inline';
    count.textContent = sel.length;
    var vis = document.getElementById('modul-sortable');
    var hid = document.getElementById('modul-sortable-hidden');
    var hasVisible = vis && Array.from(vis.querySelectorAll('.js-mod-btn.selected')).length > 0;
    var hasHidden = hid && Array.from(hid.querySelectorAll('.js-mod-btn.selected')).length > 0;
    var btnHide = document.getElementById('btn-hide-selected');
    var btnShow = document.getElementById('btn-show-selected');
    if (btnHide) btnHide.style.display = hasVisible ? '' : 'none';
    if (btnShow) btnShow.style.display = hasHidden ? '' : 'none';
}
document.getElementById('btn-hide-selected')?.addEventListener('click', function(){ batchToggle('hide'); });
document.getElementById('btn-show-selected')?.addEventListener('click', function(){ batchToggle('show'); });
document.getElementById('btn-clear-selection')?.addEventListener('click', function(){
    document.querySelectorAll('.js-mod-btn.selected').forEach(function(b){ b.classList.remove('selected'); });
    updateBatchUI();
});

// ===== Batch Hide/Show (auch für Multi-Drag) =====
function batchToggle(action, entries){
    if (!entries) {
        var sel = document.querySelectorAll('.js-mod-btn.selected');
        if (sel.length === 0) return;
        entries = [];
        sel.forEach(function(btn){ entries.push({type: btn.dataset.type, id: btn.dataset.id}); });
    }
    if (entries.length === 0) return;
    saveToggleState();
    fetch('/projekt/module-toggle-hidden-batch', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({action: action, entries: entries})
    }).then(function(r){ return r.json(); }).then(function(){
        location.reload();
    }).catch(function(e){ console.error('Batch toggle failed', e); });
}

// ===== SortableJS mit Cross-Container Drag & Drop =====
function initSortable(){
    if (typeof Sortable === 'undefined') return;
    function makeSortable(el){
        if (!el) return;
        new Sortable(el, {
            group: 'modules',
            animation: 150,
            handle: '.js-mod-btn',
            onStart: function(ev){
                // Hidden-Details automatisch öffnen beim Ziehen
                var details = document.getElementById('hidden-details');
                if (details && !details.hasAttribute('open')) details.setAttribute('open', '');
                // Markiere alle selektierten im Quell-Container
                var src = ev.from;
                if (!src) return;
                var selected = src.querySelectorAll('.js-mod-btn.selected');
                if (selected.length > 1) {
                    window._dragMulti = [];
                    selected.forEach(function(b){ window._dragMulti.push({type: b.dataset.type, id: b.dataset.id}); });
                } else {
                    window._dragMulti = null;
                }
            },
            onEnd: function(ev){
                if (ev.from !== ev.to) {
                    // Cross-container move
                    var entries = window._dragMulti || [{type: ev.item.dataset.type, id: ev.item.dataset.id}];
                    var action = ev.to.id === 'modul-sortable-hidden' ? 'hide' : 'show';
                    batchToggle(action, entries);
                } else {
                    saveToggleState();
                    var order = [];
                    ev.from.querySelectorAll('.js-mod-btn').forEach(function(btn){
                        order.push({type: btn.dataset.type, id: btn.dataset.id});
                    });
                    fetch('/projekt/module-reorder', {
                        method: 'POST',
                        headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify({order: order})
                    }).catch(function(e){ console.error('Reorder failed', e); });
                }
                window._dragMulti = null;
            }
        });
    }
    makeSortable(document.getElementById('modul-sortable'));
    makeSortable(document.getElementById('modul-sortable-hidden'));
}
document.addEventListener('DOMContentLoaded', initSortable);

function toggleModHidden(el) {
    var type = el.dataset.type;
    var id = el.dataset.id;
    saveToggleState();
    fetch('/projekt/module-toggle-hidden', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({type: type, id: id})
    }).then(function(r){ return r.json(); }).then(function(){
        location.reload();
    }).catch(function(e){ console.error('Toggle failed', e); });
}

/* ===== Lock-Management ===== */
{% if locked_by_name %}
(function(){
    // Read-only mode: disable all interactive elements
    function disableInputs(root){
        root.querySelectorAll('button, input, textarea, select, a.button').forEach(function(el){el.disabled=true;el.style.pointerEvents='none';el.style.opacity='0.6'});
        document.querySelectorAll('#pos-sortable tr').forEach(function(row){row.style.cursor='not-allowed'});
        document.querySelectorAll('.js-mod-btn').forEach(function(el){el.style.pointerEvents='none';el.style.opacity='0.5'});
        var dropAreas = document.querySelectorAll('#pos-sortable');
        dropAreas.forEach(function(da){da.style.pointerEvents='none'});
    }
    disableInputs(document);
})();
{% else %}
(function(){
    var PROJECT_ID = {{ project.id }};
    var AUFMASS_ID = {{ aufmass.id }};
    var HEARTBEAT_INTERVAL = 30000; // 30s
    var hbTimer = null;

    function lockHeartbeat(){
        fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/heartbeat',{method:'POST',headers:{'Content-Type':'application/json'}})
            .catch(function(e){console.error('Heartbeat failed',e)});
    }

    function releaseLock(){
        if(hbTimer){clearInterval(hbTimer);hbTimer=null}
        navigator.sendBeacon('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/unlock','{}');
    }

    // Start heartbeat after acquiring lock
    fetch('/projekt/'+PROJECT_ID+'/'+AUFMASS_ID+'/lock',{method:'POST',headers:{'Content-Type':'application/json'}})
        .then(function(r){
            if(r.status===423)return r.json().then(function(d){console.warn('Lock held by:',d.holder)});
            hbTimer=setInterval(lockHeartbeat,HEARTBEAT_INTERVAL);
        }).catch(function(e){console.error('Lock acquire failed',e)});

    window.addEventListener('beforeunload',releaseLock);
})();
{% endif %}
{% endblock %}
