from pathlib import Path
import importlib.util
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, KeepTogether
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.units import mm

OUT = Path('/home/agent/outputs/moenchaltorf_jourfix_260623')
BASE_SCRIPT = OUT / 'create_deliverables.py'
TEMPLATE = Path('/home/agent/.hermes/cache/documents/doc_2b98587a9631_Marketing_Vorlage Kopf_Fusszeile_Protokoll.docx')
DOCX_OUT = OUT / '260623_Moenchaltorf_Protokoll_1_Fachplaner_Jourfixe_ERNE_CD.docx'
PDF_OUT = OUT / '260623_Moenchaltorf_Protokoll_1_Fachplaner_Jourfixe_ERNE_CD.pdf'

spec = importlib.util.spec_from_file_location('base', BASE_SCRIPT)
base = importlib.util.module_from_spec(spec)
spec.loader.exec_module(base)

ERNE_BLUE = '365294'
ERNE_BEIGE = 'E2D3C4'
BEIGE_LIGHT = 'F5F0EA'
BLUE_LIGHT = 'E8EDF7'
DARK = '111111'
MID_GREY = '6D6E71'

# ---------- DOCX helpers ----------
def clear_body(doc):
    body = doc._body._element
    for child in list(body):
        if child.tag.endswith('}sectPr'):
            continue
        body.remove(child)

def set_font(run, size=9, bold=False, color=DARK, name='Arial'):
    run.font.name = name
    run._element.rPr.rFonts.set(qn('w:eastAsia'), name)
    run.font.size = Pt(size)
    run.font.bold = bold
    run.font.color.rgb = RGBColor.from_string(color)

def shade_cell(cell, fill):
    tcPr = cell._tc.get_or_add_tcPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:fill'), fill)
    tcPr.append(shd)

def cell_text(cell, text, bold=False, size=8, color=DARK, align=None):
    cell.text = ''
    p = cell.paragraphs[0]
    if align:
        p.alignment = align
    r = p.add_run(str(text))
    set_font(r, size=size, bold=bold, color=color)
    cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.TOP
    return p

def set_cell_border(cell, **kwargs):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    borders = tcPr.first_child_found_in('w:tcBorders')
    if borders is None:
        borders = OxmlElement('w:tcBorders')
        tcPr.append(borders)
    for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
        if edge in kwargs:
            tag = 'w:{}'.format(edge)
            element = borders.find(qn(tag))
            if element is None:
                element = OxmlElement(tag)
                borders.append(element)
            for key, value in kwargs[edge].items():
                element.set(qn('w:{}'.format(key)), str(value))

def add_page_field(paragraph, field):
    run = paragraph.add_run()
    fldChar1 = OxmlElement('w:fldChar'); fldChar1.set(qn('w:fldCharType'), 'begin')
    instrText = OxmlElement('w:instrText'); instrText.set(qn('xml:space'), 'preserve'); instrText.text = field
    fldChar2 = OxmlElement('w:fldChar'); fldChar2.set(qn('w:fldCharType'), 'end')
    run._r.append(fldChar1); run._r.append(instrText); run._r.append(fldChar2)
    return run

def set_footer(doc):
    for s in doc.sections:
        s.different_first_page_header_footer = False
        f = s.footer
        for p in f.paragraphs:
            p._element.getparent().remove(p._element)
        p = f.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        r = p.add_run('Seite '); set_font(r, 8, color=MID_GREY)
        add_page_field(p, 'PAGE')
        r = p.add_run(' von '); set_font(r, 8, color=MID_GREY)
        add_page_field(p, 'NUMPAGES')
        r = p.add_run('\twww.erne.net'); set_font(r, 8, color=ERNE_BLUE)

def add_heading(doc, text, level=1):
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(9 if level == 1 else 6)
    p.paragraph_format.space_after = Pt(4)
    r = p.add_run(text.upper() if level == 1 else text)
    set_font(r, size=13 if level == 1 else 10, bold=True, color=ERNE_BLUE)
    # small blue rule below level 1
    if level == 1:
        pPr = p._p.get_or_add_pPr()
        pBdr = OxmlElement('w:pBdr')
        bottom = OxmlElement('w:bottom')
        bottom.set(qn('w:val'), 'single'); bottom.set(qn('w:sz'), '8'); bottom.set(qn('w:space'), '4'); bottom.set(qn('w:color'), ERNE_BLUE)
        pBdr.append(bottom); pPr.append(pBdr)
    return p

def add_bullet(doc, text):
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(1.5)
    p.paragraph_format.left_indent = Cm(0.35)
    p.paragraph_format.first_line_indent = Cm(-0.25)
    r = p.add_run('• ')
    set_font(r, size=8.7, color=ERNE_BLUE)
    r = p.add_run(text)
    set_font(r, size=8.7)
    return p

def add_table(doc, headers, rows, widths=None, font_size=7.5):
    tbl = doc.add_table(rows=1, cols=len(headers))
    try:
        tbl.style = 'Table Grid'
    except KeyError:
        pass
    tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
    for i,h in enumerate(headers):
        cell_text(tbl.rows[0].cells[i], h, bold=True, size=font_size, color='FFFFFF', align=WD_ALIGN_PARAGRAPH.CENTER)
        shade_cell(tbl.rows[0].cells[i], ERNE_BLUE)
    for ri,row in enumerate(rows):
        cells = tbl.add_row().cells
        for i,val in enumerate(row):
            cell_text(cells[i], val, size=font_size)
            shade_cell(cells[i], BEIGE_LIGHT if ri % 2 else 'FFFFFF')
    if widths:
        for row in tbl.rows:
            for i,w in enumerate(widths):
                row.cells[i].width = Cm(w)
    for row in tbl.rows:
        for c in row.cells:
            set_cell_border(c, top={'val':'single','sz':'2','color':'D9D9D9'}, bottom={'val':'single','sz':'2','color':'D9D9D9'}, left={'val':'single','sz':'2','color':'D9D9D9'}, right={'val':'single','sz':'2','color':'D9D9D9'})
    doc.add_paragraph('')
    return tbl

def add_info_band(doc, label, value):
    tbl = doc.add_table(rows=1, cols=2)
    tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
    try:
        tbl.style = 'Table Grid'
    except KeyError:
        pass
    cell_text(tbl.rows[0].cells[0], label.upper(), bold=True, size=8, color='FFFFFF')
    shade_cell(tbl.rows[0].cells[0], ERNE_BLUE)
    cell_text(tbl.rows[0].cells[1], value, size=8)
    shade_cell(tbl.rows[0].cells[1], BEIGE_LIGHT)
    tbl.rows[0].cells[0].width = Cm(4.0); tbl.rows[0].cells[1].width = Cm(13.2)
    for c in tbl.rows[0].cells:
        set_cell_border(c, top={'val':'single','sz':'2','color':'FFFFFF'}, bottom={'val':'single','sz':'2','color':'FFFFFF'}, left={'val':'single','sz':'2','color':'FFFFFF'}, right={'val':'single','sz':'2','color':'FFFFFF'})
    return tbl

# ---------- Build DOCX ----------
def build_docx():
    doc = Document(str(TEMPLATE))
    clear_body(doc)
    sec = doc.sections[0]
    sec.top_margin = Cm(1.35); sec.bottom_margin = Cm(1.25); sec.left_margin = Cm(1.55); sec.right_margin = Cm(1.35)
    sec.header_distance = Cm(0.5); sec.footer_distance = Cm(0.5)
    for st in ['Normal', 'Body Text']:
        if st in doc.styles:
            doc.styles[st].font.name = 'Arial'; doc.styles[st].font.size = Pt(9)
    set_footer(doc)

    # Title block in ERNE corporate style
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(0)
    r = p.add_run('PROTOKOLL')
    set_font(r, size=9, bold=True, color=ERNE_BLUE)
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(6)
    r = p.add_run('1. FAC HPLANER-JOUR-FIXE'.replace('FAC H','FACH'))
    set_font(r, size=24, bold=False, color=ERNE_BLUE)
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(10)
    r = p.add_run('410.3170.10 – Mönchaltorf – Gemeindehaus mit Wohnbau')
    set_font(r, size=11, bold=True, color=DARK)

    add_info_band(doc, 'Datum / Zeit', '23.06.2026, 13:15–ca. 15:09 Uhr')
    add_info_band(doc, 'Ort / Medium', 'Microsoft Teams')
    add_info_band(doc, 'Sitzungsleitung', 'Cristian Donatiello, ERNE AG Holzbau')
    add_info_band(doc, 'Protokollbasis', 'Teams-Transkript, geführter Fachplaner-Jour-fixe und handgeführtes Vorlageprotokoll')
    add_info_band(doc, 'Legende', 'B = Beschluss | P = Pendenz | I = Information')

    add_heading(doc, 'Teilnehmende', 1)
    add_table(doc, ['Kürzel','Name','Firma','Rolle / Fachbereich'], base.participants, widths=[1.3,4.2,5.1,6.0], font_size=7.3)

    add_heading(doc, 'Management Summary', 1)
    summary = [
        'Hauptthemen waren Höhenkoordination über der Tiefgarage, Werkleitungs-/Meteorwasserplanung, Dämmperimeter/Bauphysik, Wohnbau-Treppenhaus, Elektro-/HLKS-Steigzonen und Tiefgaragenlüftung.',
        'Die Höhen über der Tiefgaragendecke sind kritisch: Gefälle, Belag, Abdichtung, Dämmung und Entwässerung müssen anhand von Querschnitten und Koten konsolidiert werden.',
        'Das Wohnbau-Treppenhaus inkl. Liftkern wird als Massivbau weiterverfolgt; Auflager, Dämmperimeter und Schnittstellen bleiben zu koordinieren.',
        'Die natürliche bzw. teilnatürliche Tiefgaragenlüftung ist unsicher und benötigt eine Simulation; mechanische / teilmechanische Lösungen beeinflussen Raumhöhen, Kanäle und Schächte.',
        'Werkleitungen, Retention und Meteorwasser sind wegen nicht möglicher konzentrierter Versickerung früh mit Gemeinde/Bünzli AG abzustimmen.',
        'Elektro und HLKS benötigen weitere Koordination zu Technikflächen, Putzräumen, Steigzonen, Bodenkanälen sowie Brandschutz-/Schallschutzdetails.',
        'Der Terminplan wird digitalisiert und beim nächsten Fachplaner-Jour-fixe vom 07.07.2026 geprüft.',
    ]
    for s in summary: add_bullet(doc, s)

    add_heading(doc, 'Ampelstatus', 1)
    add_table(doc, ['Bereich','Status','Begründung / Handlungsbedarf'], [
        ['Gesamtstatus','Gelb','Viele Schnittstellen sind erkannt, mehrere koordinationskritische Punkte sind noch offen.'],
        ['Termine','Gelb','Baueingabe-relevante Themen Werkleitungen, Höhen, Technik und Lüftung müssen kurzfristig geklärt werden.'],
        ['Kosten','Gelb','Simulation Tiefgaragenlüftung, mögliche Kanal-/Schachtlösungen und Höhen-/Belagsvarianten können Kosten auslösen.'],
        ['Qualität / PQM','Gelb','Dämmperimeter, Kondensat, Schallschutz und Raumhöhen benötigen technische Absicherung.'],
        ['Koordination','Gelb','3D-/2D-Schnittstellen, Einfügepunkte, Fachmodelle und Steigzonen sind zu konsolidieren.'],
        ['Entscheide / Eskalation','Gelb','Massivbau Treppenhaus entschieden; Lüftung, Meteorwasser und Technikflächen offen.'],
    ], widths=[3.8,1.8,11.2], font_size=7.5)

    add_heading(doc, 'Traktanden / Protokoll', 1)
    for title, bullets in base.sections:
        add_heading(doc, title, 2)
        for b in bullets: add_bullet(doc, b)

    add_heading(doc, 'Entscheide', 1)
    add_table(doc, ['Entscheid','Kontext','Folge / Verantwortlich'], base.decisions, widths=[3.8,7.7,5.2], font_size=7.1)
    add_heading(doc, 'Offene Fragen', 1)
    add_table(doc, ['Frage','Kontext','Klärung durch','Termin'], base.open_questions, widths=[3.7,6.2,3.5,3.3], font_size=6.8)
    add_heading(doc, 'Risiken und Chancen', 1)
    add_table(doc, ['Thema','Art','Bewertung','Auswirkung','Nächster Schritt'], base.risks, widths=[3.2,1.4,4.4,3.7,4.0], font_size=6.5)
    add_heading(doc, 'Pendenzenliste', 1)
    add_table(doc, ['Nr.','Pendenz','Verantwortlich','Termin','Prio','Relevanz','Status'], base.pendenzen, widths=[0.9,6.4,2.4,2.3,1.1,2.3,1.0], font_size=6.1)

    add_heading(doc, 'Punkte für nächste Sitzung', 1)
    for item in [
        'Digitalisierter Terminplan und Sommerferien-/Koordinationstermine.',
        'Konsolidierter Architekturstand mit Fachplanerinputs und Höhen-/Kotenprüfung.',
        'Werkleitungs-/Meteorwasserabstimmung mit Gemeinde/Bünzli AG.',
        'Entscheidungsgrundlage Tiefgaragenlüftung inkl. Offerte Simulation.',
        'Steigzonen, Technikflächen, Putzräume und Elektro-/HLKS-Koordination.',
        'Holzbau-/Massivbau-Abgrenzung, Deckenauflager und Dämmperimeter Wohnbau.',
    ]: add_bullet(doc, item)

    add_heading(doc, 'Kurzversion zum Weiterleiten', 1)
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(3); p.paragraph_format.space_after = Pt(6)
    r = p.add_run('Der 1. Fachplaner-Jour-fixe zeigte mehrere koordinationskritische Punkte: Höhen/Aufbau über Tiefgarage, Werkleitungen/Meteorwasser, Dämmperimeter, Steigzonen/Technikflächen und Tiefgaragenlüftung. Das Treppenhaus/Liftkern Wohnbau wird als Massivbau weiterverfolgt. Für die Umgebung werden Querschnitte/Koten bis Ende KW 27 erstellt; Architektur prüft das Absenken des Gebäudes um ca. 5 cm. Die natürliche/teilnatürliche Tiefgaragenlüftung benötigt eine Simulation; EBERLE stellt ERNE die Offerte zu. Nächster Fachplaner-Jour-fixe: 07.07.2026, Microsoft Teams.')
    set_font(r, size=8.7)
    doc.save(DOCX_OUT)

# ---------- PDF helpers ----------
def register_font():
    for fp in ['/usr/share/fonts/truetype/msttcorefonts/Arial.ttf','/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf','/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf']:
        if Path(fp).exists():
            pdfmetrics.registerFont(TTFont('ERNEBody', fp)); return 'ERNEBody'
    return 'Helvetica'

def esc(text):
    return str(text).replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('\n','<br/>')

def build_pdf():
    font = register_font()
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='ERNE_Title', fontName=font, fontSize=23, leading=27, textColor=colors.HexColor('#'+ERNE_BLUE), spaceAfter=7))
    styles.add(ParagraphStyle(name='ERNE_Over', fontName=font, fontSize=8, leading=12, textColor=colors.HexColor('#'+ERNE_BLUE), spaceAfter=1))
    styles.add(ParagraphStyle(name='ERNE_H1', fontName=font, fontSize=12, leading=15, textColor=colors.HexColor('#'+ERNE_BLUE), spaceBefore=8, spaceAfter=4))
    styles.add(ParagraphStyle(name='ERNE_H2', fontName=font, fontSize=9.2, leading=11, textColor=colors.HexColor('#'+ERNE_BLUE), spaceBefore=5, spaceAfter=2))
    styles.add(ParagraphStyle(name='ERNE_Body', fontName=font, fontSize=6.9, leading=8.4, textColor=colors.black, spaceAfter=1.5))
    styles.add(ParagraphStyle(name='ERNE_Small', fontName=font, fontSize=5.7, leading=6.8, textColor=colors.black, spaceAfter=1))
    styles.add(ParagraphStyle(name='ERNE_Center', fontName=font, fontSize=6.5, leading=7.5, alignment=TA_CENTER))

    def footer(canvas, doc):
        canvas.saveState()
        canvas.setStrokeColor(colors.HexColor('#'+ERNE_BEIGE)); canvas.setLineWidth(0.6)
        canvas.line(12*mm, 10*mm, 198*mm, 10*mm)
        canvas.setFont(font, 7); canvas.setFillColor(colors.HexColor('#'+MID_GREY))
        canvas.drawString(12*mm, 6.5*mm, f'Seite {doc.page}')
        canvas.setFillColor(colors.HexColor('#'+ERNE_BLUE))
        canvas.drawRightString(198*mm, 6.5*mm, 'www.erne.net')
        canvas.restoreState()

    doc = SimpleDocTemplate(str(PDF_OUT), pagesize=A4, rightMargin=12*mm, leftMargin=12*mm, topMargin=12*mm, bottomMargin=15*mm)
    story = []
    P = lambda text, style='ERNE_Body': Paragraph(esc(text), styles[style])
    story.append(P('PROTOKOLL', 'ERNE_Over'))
    story.append(P('1. FACHPLANER-JOUR-FIXE', 'ERNE_Title'))
    story.append(P('410.3170.10 – Mönchaltorf – Gemeindehaus mit Wohnbau', 'ERNE_H2'))

    def table(headers, rows, widths, small=False):
        style = styles['ERNE_Small' if small else 'ERNE_Body']
        data = [[Paragraph(esc(h), styles['ERNE_Center']) for h in headers]] + [[Paragraph(esc(c), style) for c in r] for r in rows]
        t = Table(data, colWidths=widths, repeatRows=1)
        t.setStyle(TableStyle([
            ('BACKGROUND',(0,0),(-1,0),colors.HexColor('#'+ERNE_BLUE)), ('TEXTCOLOR',(0,0),(-1,0),colors.white),
            ('GRID',(0,0),(-1,-1),0.25,colors.HexColor('#D9D9D9')), ('VALIGN',(0,0),(-1,-1),'TOP'),
            ('LEFTPADDING',(0,0),(-1,-1),3), ('RIGHTPADDING',(0,0),(-1,-1),3), ('TOPPADDING',(0,0),(-1,-1),2), ('BOTTOMPADDING',(0,0),(-1,-1),2),
            ('ROWBACKGROUNDS',(0,1),(-1,-1),[colors.white, colors.HexColor('#'+BEIGE_LIGHT)]),
        ]))
        story.append(t); story.append(Spacer(1,3))

    table(['Feld','Inhalt'], [['Datum / Zeit','23.06.2026, 13:15–ca. 15:09 Uhr'],['Ort / Medium','Microsoft Teams'],['Sitzungsleitung','Cristian Donatiello, ERNE AG Holzbau'],['Protokollbasis','Teams-Transkript, geführter Fachplaner-Jour-fixe und handgeführtes Vorlageprotokoll'],['Legende','B = Beschluss | P = Pendenz | I = Information']], [35*mm, 150*mm])
    story.append(P('TEILNEHMENDE','ERNE_H1')); table(['Kürzel','Name','Firma','Rolle'], base.participants, [13*mm,36*mm,52*mm,84*mm], small=True)
    story.append(P('MANAGEMENT SUMMARY','ERNE_H1'))
    for s in ['Hauptthemen waren Höhenkoordination über der Tiefgarage, Werkleitungs-/Meteorwasserplanung, Dämmperimeter/Bauphysik, Wohnbau-Treppenhaus, Elektro-/HLKS-Steigzonen und Tiefgaragenlüftung.', 'Die Höhen über der Tiefgaragendecke sind kritisch und anhand Querschnitten/Koten zu konsolidieren.', 'Das Wohnbau-Treppenhaus inkl. Liftkern wird als Massivbau weiterverfolgt.', 'Die natürliche/teilnatürliche Tiefgaragenlüftung benötigt eine Simulation; Kanal-/Schachtlösungen beeinflussen Raumhöhen, Türen und Fluchtwege.', 'Werkleitungen, Retention und Meteorwasser sind früh mit Gemeinde/Bünzli AG abzustimmen.', 'Nächster Fachplaner-Jour-fixe: 07.07.2026, Microsoft Teams.']:
        story.append(P('• '+s))
    story.append(P('AMPELSTATUS','ERNE_H1'))
    table(['Bereich','Status','Begründung / Handlungsbedarf'], [['Gesamtstatus','Gelb','Viele Schnittstellen erkannt; mehrere koordinationskritische Punkte offen.'],['Termine','Gelb','Baueingabe-relevante Themen kurzfristig klären.'],['Kosten','Gelb','Simulation, Kanäle/Schächte und Höhen-/Belagsvarianten können Kosten auslösen.'],['Qualität','Gelb','Dämmperimeter, Kondensat, Schallschutz und Raumhöhen absichern.'],['Koordination','Gelb','3D-/2D-Schnittstellen, Einfügepunkte und Steigzonen konsolidieren.']], [35*mm,18*mm,132*mm])
    story.append(P('TRAKTANDEN / PROTOKOLL','ERNE_H1'))
    for title, bullets in base.sections:
        story.append(P(title, 'ERNE_H2'))
        for b in bullets: story.append(P('• '+b))
    story.append(PageBreak())
    story.append(P('ENTSCHEIDE','ERNE_H1')); table(['Entscheid','Kontext','Folge / Verantwortlich'], base.decisions, [36*mm,92*mm,57*mm], small=True)
    story.append(P('OFFENE FRAGEN','ERNE_H1')); table(['Frage','Kontext','Klärung durch','Termin'], base.open_questions, [35*mm,75*mm,37*mm,38*mm], small=True)
    story.append(P('RISIKEN UND CHANCEN','ERNE_H1')); table(['Thema','Art','Bewertung','Auswirkung','Nächster Schritt'], base.risks, [34*mm,14*mm,55*mm,39*mm,43*mm], small=True)
    story.append(P('PENDENZENLISTE','ERNE_H1')); table(['Nr.','Pendenz','Verantwortlich','Termin','Prio','Relevanz','Status'], base.pendenzen, [9*mm,76*mm,26*mm,25*mm,12*mm,25*mm,12*mm], small=True)
    story.append(P('PUNKTE FÜR NÄCHSTE SITZUNG','ERNE_H1'))
    for item in ['Digitalisierter Terminplan und Sommerferien-/Koordinationstermine.','Konsolidierter Architekturstand mit Fachplanerinputs und Höhen-/Kotenprüfung.','Werkleitungs-/Meteorwasserabstimmung mit Gemeinde/Bünzli AG.','Entscheidungsgrundlage Tiefgaragenlüftung inkl. Offerte Simulation.','Steigzonen, Technikflächen, Putzräume und Elektro-/HLKS-Koordination.','Holzbau-/Massivbau-Abgrenzung, Deckenauflager und Dämmperimeter Wohnbau.']:
        story.append(P('• '+item))
    story.append(P('KURZVERSION ZUM WEITERLEITEN','ERNE_H1'))
    story.append(P('Der 1. Fachplaner-Jour-fixe zeigte mehrere koordinationskritische Punkte: Höhen/Aufbau über Tiefgarage, Werkleitungen/Meteorwasser, Dämmperimeter, Steigzonen/Technikflächen und Tiefgaragenlüftung. Das Treppenhaus/Liftkern Wohnbau wird als Massivbau weiterverfolgt. Für die Umgebung werden Querschnitte/Koten bis Ende KW 27 erstellt; Architektur prüft das Absenken des Gebäudes um ca. 5 cm. Die natürliche/teilnatürliche Tiefgaragenlüftung benötigt eine Simulation; EBERLE stellt ERNE die Offerte zu. Nächster Fachplaner-Jour-fixe: 07.07.2026, Microsoft Teams.'))
    doc.build(story, onFirstPage=footer, onLaterPages=footer)

if __name__ == '__main__':
    build_docx()
    build_pdf()
    print(DOCX_OUT)
    print(PDF_OUT)
