from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import PP_ALIGN, MSO_AUTO_SIZE
from pathlib import Path
import shutil, json

src=Path('/home/agent/260603_GaM_Gemeinsam_erfolgreich_dank_klar_definierten_Prozessen_Rev01.pptx')
out_dir=Path('/home/agent/erne_presentation_review')
out_dir.mkdir(exist_ok=True)
out=out_dir/'260603_GaM_Gemeinsam_erfolgreich_dank_klar_definierten_Prozessen_Rev02_MG_optimiert.pptx'
shutil.copy2(src,out)
prs=Presentation(str(out))
W,H=prs.slide_width, prs.slide_height
BLUE=RGBColor(0,80,145)
DARK=RGBColor(31,45,61)
LIGHT=RGBColor(245,248,251)
GREY=RGBColor(102,112,128)
ACCENT=RGBColor(235,135,45)
GREEN=RGBColor(45,140,95)
WHITE=RGBColor(255,255,255)

def set_textbox(shape, text, size=18, bold=False, color=DARK, align=None):
    shape.text = text
    tf=shape.text_frame
    tf.word_wrap=True
    try: tf.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
    except Exception: pass
    for p in tf.paragraphs:
        if align is not None: p.alignment=align
        for r in p.runs:
            r.font.size=Pt(size); r.font.bold=bold; r.font.color.rgb=color; r.font.name='Aptos'

def add_text(slide, x,y,w,h,text,size=16,bold=False,color=DARK,fill=None,line=None,align=None,margin=0.08):
    shape=slide.shapes.add_textbox(x,y,w,h)
    tf=shape.text_frame
    tf.clear(); tf.word_wrap=True
    tf.margin_left=Inches(margin); tf.margin_right=Inches(margin); tf.margin_top=Inches(margin/2); tf.margin_bottom=Inches(margin/2)
    for i,line_text in enumerate(text.split('\n')):
        p=tf.paragraphs[0] if i==0 else tf.add_paragraph()
        p.text=line_text
        if align: p.alignment=align
        for r in p.runs:
            r.font.name='Aptos'; r.font.size=Pt(size); r.font.bold=bold; r.font.color.rgb=color
    if fill:
        shape.fill.solid(); shape.fill.fore_color.rgb=fill
    if line:
        shape.line.color.rgb=line
    else:
        shape.line.fill.background()
    return shape

def add_rect(slide,x,y,w,h,fill=LIGHT,line=None,transparency=0, radius=True):
    shp=slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE if radius else MSO_SHAPE.RECTANGLE,x,y,w,h)
    shp.fill.solid(); shp.fill.fore_color.rgb=fill; shp.fill.transparency=transparency
    if line: shp.line.color.rgb=line
    else: shp.line.fill.background()
    return shp

def add_badge(slide, label, step=None):
    x=W-Inches(3.0); y=Inches(0.20); w=Inches(2.72); h=Inches(0.48)
    add_rect(slide,x,y,w,h,fill=BLUE,transparency=0,radius=True)
    txt=(f'{step}  ' if step else '')+label
    add_text(slide,x+Inches(0.12),y+Inches(0.055),w-Inches(0.24),h-Inches(0.08),txt,size=10,bold=True,color=WHITE,align=PP_ALIGN.CENTER,margin=0)

def add_footer_msg(slide, msg):
    add_text(slide, Inches(0.45), H-Inches(0.42), W-Inches(0.9), Inches(0.18), msg, size=7, color=GREY, align=PP_ALIGN.RIGHT, margin=0)

def replace_all_text(slide, replacements):
    for shape in slide.shapes:
        if not hasattr(shape,'text_frame'): continue
        for p in shape.text_frame.paragraphs:
            for r in p.runs:
                txt=r.text
                for old,new in replacements.items(): txt=txt.replace(old,new)
                r.text=txt

repls={
 'Beschaffun':'Beschaffung','durch das Ganze Projekte':'durch das ganze Projekt','ev Technische':'evtl. technische','LV versand':'LV-Versand','zum beachten':'zu beachten','Fehlkalkulation Budget !':'Budget-Fehlkalkulation!'
}
for i in range(19): replace_all_text(prs.slides[i], repls)

s=prs.slides[0]
add_rect(s, Inches(0.55), Inches(6.05), Inches(11.8), Inches(0.55), fill=BLUE, radius=True)
add_text(s, Inches(0.78), Inches(6.16), Inches(11.25), Inches(0.26), 'Klar definierte Prozesse sind kein Administrationsaufwand – sie sichern Preis, Termin und Qualität.', size=16, bold=True, color=WHITE, align=PP_ALIGN.CENTER, margin=0)
add_badge(s,'Marcel Gasser',step='Teil 1/2')

s=prs.slides[1]
for sh in s.shapes:
    if hasattr(sh,'text') and 'Gemeinsames Verständnis' in sh.text:
        set_textbox(sh, '• Gemeinsames Verständnis für den aktuellen Beschaffungsprozess\n\n• Aufzeigen, wann Beschaffung den Projekterfolg beeinflusst\n\n• Grundsätze guter Vergaben: Preis, Termin und Qualität\n\n• Schnittstellen PL / Backoffice / Beschaffung kennen\n\n• Präsentation als späteres Nachschlagewerk nutzbar machen', size=17, color=DARK)
add_rect(s, Inches(0.55), Inches(5.55), Inches(11.8), Inches(0.68), fill=LIGHT, line=BLUE, radius=True)
add_text(s, Inches(0.85), Inches(5.70), Inches(11.2), Inches(0.32), 'Kernbotschaft: Je früher der Prozess sauber startet, desto grösser ist der Hebel in Verhandlung, Risiko und Ergebnis.', size=14, bold=True, color=BLUE, align=PP_ALIGN.CENTER, margin=0)
add_badge(s,'Zielbild',step='01')

s=prs.slides[2]
add_badge(s,'Grundsätze',step='02')
for idx,(word,col) in enumerate([('Preis',BLUE),('Termin',ACCENT),('Qualität',GREEN)]):
    x=Inches(0.65+idx*2.15); y=Inches(5.58)
    add_rect(s,x,y,Inches(1.75),Inches(0.48),fill=col,radius=True)
    add_text(s,x,y+Inches(0.11),Inches(1.75),Inches(0.18),word,size=13,bold=True,color=WHITE,align=PP_ALIGN.CENTER,margin=0)
add_text(s, Inches(0.65), Inches(6.10), Inches(6.6), Inches(0.26), 'Diese drei Kriterien bleiben über den ganzen Vergabeprozess sichtbar – auch wenn Details später nachgeschlagen werden.', size=9, color=GREY, margin=0)

for n,(badge,callout) in enumerate([('Leitfaden','Vom Basic zur Vergabeentscheidung: Vorbereitung, Vergleichbarkeit, Varianten und Verhandlung.'),('Praxishebel','Nicht alles verhandeln – die relevanten Preishebel finden und fokussiert bearbeiten.')], start=3):
    s=prs.slides[n]
    add_badge(s,badge,step=f'0{n}')
    add_rect(s, Inches(0.55), Inches(5.65), Inches(11.75), Inches(0.45), fill=LIGHT, line=BLUE, radius=True)
    add_text(s, Inches(0.78), Inches(5.76), Inches(11.25), Inches(0.16), callout, size=11, bold=True, color=BLUE, align=PP_ALIGN.CENTER, margin=0)

s=prs.slides[5]
add_badge(s,'Ausschreibung und Vergabe',step='05')
for idx,(head,body,col) in enumerate([('Ziel','vergleichbare, belastbare Angebote',BLUE),('Risiko','Billigst ist nicht beste Vergabe',ACCENT),('Chance','Unternehmer macht den letzten Preis',GREEN)]):
    x=Inches(0.65+idx*3.9); y=Inches(5.70)
    add_rect(s,x,y,Inches(3.45),Inches(0.55),fill=col,radius=True)
    add_text(s,x+Inches(0.08),y+Inches(0.06),Inches(3.3),Inches(0.15),head,size=9,bold=True,color=WHITE,align=PP_ALIGN.CENTER,margin=0)
    add_text(s,x+Inches(0.08),y+Inches(0.26),Inches(3.3),Inches(0.13),body,size=8,color=WHITE,align=PP_ALIGN.CENTER,margin=0)

process_labels=[('Grundlagen klären','Was intern/extern? Welche Grundlagen, Termine, Leistungsbeschriebe?'),('Kontrollfragen','Infos finden, Grundsätze prüfen, Flussdiagramm lesen.'),('Anfragen','Mindestanzahl Unternehmer bewusst wählen – Marktbreite schaffen.'),('Varianten und Verhandlung','Varianten erkennen, technisch bereinigen, nachverhandeln.'),('Auswertung und Vergabe','Bonität, Vergabeprotokoll, Zusage – sauber dokumentieren.'),('Absage und Vertrag','Absagen fair, Werkvertrag sauber und vollständig abschliessen.')]
for k,(lab,cap) in enumerate(process_labels, start=7):
    s=prs.slides[k-1]
    add_badge(s,lab,step=f'P{k-6}')
    add_rect(s, Inches(0.45), Inches(6.18), Inches(12.05), Inches(0.30), fill=LIGHT, line=BLUE, radius=True)
    add_text(s, Inches(0.65), Inches(6.25), Inches(11.65), Inches(0.09), cap, size=8.5, bold=True, color=BLUE, align=PP_ALIGN.CENTER, margin=0)

s=prs.slides[12]
add_badge(s,'Weitere Prozesse',step='06')
add_rect(s, Inches(0.65), Inches(4.90), Inches(11.55), Inches(0.75), fill=LIGHT, line=BLUE, radius=True)
add_text(s, Inches(0.92), Inches(5.05), Inches(11.0), Inches(0.33), 'Nächster Block: Wie Backoffice/Beschaffung die Projektleitung entlastet und Informationen zentralisiert.', size=15, bold=True, color=BLUE, align=PP_ALIGN.CENTER, margin=0)

s=prs.slides[13]
add_badge(s,'Backoffice-Schnittstelle',step='07')
add_rect(s, Inches(0.60), Inches(5.70), Inches(11.65), Inches(0.52), fill=LIGHT, line=BLUE, radius=True)
add_text(s, Inches(0.82), Inches(5.82), Inches(11.2), Inches(0.18), 'Entscheidend ist nicht „wer hilft irgendwann“, sondern: wer liefert welche Information bis wann in welcher Qualität.', size=11, bold=True, color=BLUE, align=PP_ALIGN.CENTER, margin=0)

for k,(lab,cap) in enumerate([('Rollen klären','Abgrenzung: wer macht was, bis wann, in welcher Qualität?'),('Bearbeitung','LV erarbeiten, PL-Kontrolle, Offerten vergleichen, Versand, Abgebote, Werkvertrag.'),('Prozessdetail','Detailfolie als Nachschlagewerk – im Vortrag nur die Logik und Pflichtpunkte hervorheben.'),('Signatur / Abschluss','Digitale Freigabe und Abschluss konsequent im Prozess mitdenken.')], start=15):
    s=prs.slides[k-1]
    add_badge(s,lab,step=f'B{k-14}')
    add_rect(s, Inches(0.45), Inches(6.18), Inches(12.05), Inches(0.30), fill=LIGHT, line=BLUE, radius=True)
    add_text(s, Inches(0.65), Inches(6.25), Inches(11.65), Inches(0.09), cap, size=8.5, bold=True, color=BLUE, align=PP_ALIGN.CENTER, margin=0)

s=prs.slides[18]
add_badge(s,'Werkvertrag',step='08')
add_rect(s, Inches(7.55), Inches(4.95), Inches(4.35), Inches(1.00), fill=LIGHT, line=BLUE, radius=True)
add_text(s, Inches(7.78), Inches(5.12), Inches(3.90), Inches(0.45), 'Merksatz: Sicherheiten früh prüfen – nicht erst, wenn der Werkvertrag unterschriftsreif ist.', size=12, bold=True, color=BLUE, align=PP_ALIGN.CENTER, margin=0)

for i in range(19):
    add_footer_msg(prs.slides[i], f'Marcel Gasser – Prozessübersicht Beschaffung/Backoffice | optimierte Fassung Rev02 | Folie {i+1}/19')

prs.save(str(out))
print(out)

html_path=out_dir/'ERNE_Beschaffung_HTML_Prototyp_MG.html'
slides = [
 ('Beschaffung und Backoffice im Baumanagement','Klar definierte Prozesse sichern Preis, Termin und Qualität.','Aktuelle Prozesse und Zusammenarbeit PL / Backoffice / Beschaffung seit 01.02.2026'),
 ('Zielbild','Die Präsentation soll später als Nachschlagewerk funktionieren.','Wir zeigen nicht jedes Detail live – aber die Prozesslogik muss hängen bleiben.'),
 ('Die drei Grundpfeiler','Preis · Termin · Qualität','Jede Vergabeentscheidung wird an diesen drei Kriterien gespiegelt.'),
 ('Basic@ERNE','Vom Leitfaden zur Entscheidung','Vorbereitung, Risikoanalyse, Eigenpreiseinschätzung, Ausschreibung, Vergabeprotokoll.'),
 ('Praxishebel','20 Prozent der Positionen können 80 Prozent des Preises prägen.','Fokus auf relevante Einheitspreise, Regiestunden, Etappierungen und Preishebel.'),
 ('Prozess Ausschreibung und Vergabe','Ziel: vergleichbare und belastbare Angebote.','Äpfel mit Äpfeln vergleichen – bevor verhandelt wird.'),
 ('P1 Grundlagen klären','Intern / extern? Grundlagen? Termine? Leistungsbeschrieb?','Der Prozess startet nicht beim Versand, sondern bei der Klarheit der Ausgangslage.'),
 ('P2 Kontrollfragen','Welche Grundsätze gelten? Wo finde ich Infos?','Die Kontrollfragen sind die Leitplanken für Projektleiter.'),
 ('P3 Unternehmer anfragen','Mindestanzahl Anfragen bewusst wählen.','Marktbreite schafft Vergleichbarkeit und Verhandlungsposition.'),
 ('P4 Varianten und Verhandlung','Varianten erkennen, nachverhandeln, Preishebel fokussieren.','Nicht der lauteste Anbieter gewinnt – sondern das beste, bereinigte Gesamtpaket.'),
 ('P5 Auswertung und Zusage','Bonität, Vergabeprotokoll, Vergabegespräch, Zusage.','Dokumentation ist Teil der Qualitätssicherung.'),
 ('P6 Absage und Werkvertrag','Fair absagen, sauber abschliessen.','Der Abschluss muss genauso strukturiert sein wie die Anfrage.'),
 ('Weitere Prozesse','Beschaffung ist kein Einzelschritt.','Weitere Basics und Schnittstellen greifen in denselben roten Faden.'),
 ('Backoffice-Unterstützung','Zentrale Abwicklung, Entlastung der Projektleitung.','Informationen an zentraler Stelle – Synergien projektübergreifend nutzen.'),
 ('Rollen und Qualität','Wer macht was, bis wann, in welcher Qualität?','Diese Frage entscheidet, ob Zusammenarbeit funktioniert oder später repariert werden muss.'),
 ('Bearbeitungskette','LV erarbeiten → PL-Kontrolle → Offertenvergleich → Versand → Abgebote → Werkvertrag','Schritt für Schritt sichtbar, ohne jedes Detail live erklären zu müssen.'),
 ('Prozessdetail','Detailfolie als Nachschlagewerk.','Im Vortrag: Logik und Pflichtpunkte hervorheben; später: Präsentation als Referenz.'),
 ('Abschluss / Signatur','Freigabe und digitale Signatur konsequent im Prozess mitdenken.','Der Prozess endet erst, wenn die Entscheidung sauber dokumentiert und abgeschlossen ist.'),
 ('Werkverträge und Sicherheiten','Garantien früh prüfen.','Sicherheiten sind Risikomanagement – nicht Papierkram am Schluss.'),
]
slide_json=json.dumps(slides, ensure_ascii=False)
html_content=f'''<!doctype html>
<html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>ERNE Beschaffung – HTML Prototyp MG</title>
<style>
:root{{--erne:#005091;--ink:#1f2d3d;--muted:#657080;--line:#d7e2ec;--soft:#f4f8fb;--accent:#eb872d;--green:#2d8c5f;}}
*{{box-sizing:border-box}} body{{margin:0;background:#eef3f7;font-family:Aptos,Segoe UI,Arial,sans-serif;color:var(--ink);overflow:hidden}}
#stage{{position:fixed;inset:0;display:grid;place-items:center}} canvas#network{{position:fixed;inset:0;width:100vw;height:100vh;opacity:.38}}
.deck{{width:min(96vw,177.78vh);aspect-ratio:16/9;background:white;border-radius:18px;box-shadow:0 30px 80px rgba(8,32,64,.24);position:relative;overflow:hidden}}
.slide{{position:absolute;inset:0;padding:72px 92px;display:grid;grid-template-rows:auto 1fr auto;opacity:0;transform:translateX(35px) scale(.985);transition:opacity .48s ease,transform .55s cubic-bezier(.2,.8,.2,1)}}
.slide.active{{opacity:1;transform:none;z-index:2}} .top{{display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--line);padding-bottom:18px}}
.logo{{font-weight:900;letter-spacing:.06em;color:var(--erne);font-size:34px}} .part{{background:var(--erne);color:white;border-radius:999px;padding:9px 18px;font-weight:800;font-size:15px}}
h1{{font-size:62px;line-height:1.03;margin:0;letter-spacing:-.035em;max-width:1050px;text-wrap:balance}} .subtitle{{font-size:31px;line-height:1.25;color:var(--erne);font-weight:800;margin-top:22px;max-width:1180px}}
.detail{{font-size:24px;line-height:1.42;color:var(--muted);margin-top:24px;max-width:1080px}} .body{{display:grid;align-content:center;position:relative}}
.triad{{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;margin-top:44px;max-width:1120px}} .pill{{border-radius:24px;padding:30px;background:var(--soft);border:2px solid var(--line);min-height:145px;position:relative;overflow:hidden}}
.pill b{{display:block;color:var(--erne);font-size:31px;margin-bottom:10px}} .pill:before{{content:"";position:absolute;inset:auto -40px -70px auto;width:170px;height:170px;background:var(--erne);opacity:.08;border-radius:50%}}
.road{{position:absolute;left:92px;right:92px;bottom:92px;height:78px;background:#f6f9fc;border:1px solid var(--line);border-radius:999px;display:flex;align-items:center;padding:0 24px;gap:10px}}
.dot{{height:42px;min-width:42px;border-radius:50%;display:grid;place-items:center;background:white;border:2px solid var(--line);font-weight:900;color:var(--muted);transition:.45s}} .dot.on{{background:var(--erne);color:white;border-color:var(--erne);transform:scale(1.14)}}
.line{{height:3px;flex:1;background:linear-gradient(90deg,var(--erne),#bfd5e8)}} .footer{{display:flex;align-items:end;justify-content:space-between;color:var(--muted);font-size:15px}}
.progress{{height:6px;background:#d9e6f0;border-radius:99px;width:320px;overflow:hidden}} .bar{{height:100%;background:var(--erne);width:0;transition:.4s}}
.controls{{position:fixed;right:18px;bottom:16px;color:#385168;background:rgba(255,255,255,.82);border:1px solid #d8e2eb;border-radius:12px;padding:9px 12px;font-size:13px;backdrop-filter:blur(8px)}}
.kicker{{font-size:17px;text-transform:uppercase;letter-spacing:.13em;color:var(--accent);font-weight:900;margin-bottom:16px}} .bigquote{{font-size:42px;line-height:1.17;font-weight:900;color:var(--erne);max-width:1150px}}
@media (prefers-reduced-motion: reduce){{.slide,.dot,.bar{{transition:none}} canvas#network{{display:none}}}}
</style></head><body>
<canvas id="network"></canvas><div id="stage"><main class="deck" id="deck"></main></div><div class="controls">← / → navigieren · F Vollbild</div>
<script>
const slides={slide_json};
const deck=document.getElementById('deck'); let current=Number(localStorage.getItem('erne-mg-slide')||0);
function template(s,i){{ const [t,sub,detail]=s; const isProcess=i>=6&&i<=11; const isBack=i>=14&&i<=17; const dots=Array.from({{length:6}},(_,k)=>`<div class="dot ${{isProcess&&k===i-6?'on':''}}">${{k+1}}</div>${{k<5?'<div class="line"></div>':''}}`).join('');
return `<section class="slide" data-i="${{i}}"><div class="top"><div class="logo">ERNE</div><div class="part">Marcel Gasser · Teil 1/2</div></div><div class="body"><div class="kicker">${{i<6?'Ausgangslage und Grundsätze':isProcess?'Prozess Ausschreibung / Vergabe':isBack?'Backoffice-Prozess':'Schnittstellen und Sicherheiten'}}</div><h1>${{t}}</h1><div class="subtitle">${{sub}}</div><div class="detail">${{detail}}</div>${{i===2?'<div class="triad"><div class="pill"><b>Preis</b>Bestmöglicher wirtschaftlicher Entscheid.</div><div class="pill"><b>Termin</b>Zuverlässige Partner und rechtzeitige Vergabe.</div><div class="pill"><b>Qualität</b>Leistung, Risiko und Ausführung sauber beurteilen.</div></div>':''}}${{isProcess?'<div class="road">'+dots+'</div>':''}}</div><div class="footer"><span>Prototyp HTML-Präsentation · Prozessübersicht Beschaffung/Backoffice</span><div class="progress"><div class="bar" style="width:${{((i+1)/slides.length)*100}}%"></div></div><span>${{i+1}} / ${{slides.length}}</span></div></section>`}}
function render(){{deck.innerHTML=slides.map(template).join(''); deck.children[current].classList.add('active')}}
function go(n){{deck.children[current]?.classList.remove('active'); current=(n+slides.length)%slides.length; deck.children[current].classList.add('active'); localStorage.setItem('erne-mg-slide',current)}} render();
addEventListener('keydown',e=>{{if(e.key==='ArrowRight'||e.key===' ')go(current+1); if(e.key==='ArrowLeft')go(current-1); if(e.key.toLowerCase()==='f')document.documentElement.requestFullscreen?.();}});
const c=document.getElementById('network'),ctx=c.getContext('2d'); let pts=[]; function resize(){{c.width=innerWidth*devicePixelRatio;c.height=innerHeight*devicePixelRatio;pts=Array.from({{length:70}},()=>({{x:Math.random()*c.width,y:Math.random()*c.height,vx:(Math.random()-.5)*.45*devicePixelRatio,vy:(Math.random()-.5)*.45*devicePixelRatio}}));}} resize(); addEventListener('resize',resize);
function anim(){{ctx.clearRect(0,0,c.width,c.height); ctx.fillStyle='rgba(0,80,145,.9)'; ctx.strokeStyle='rgba(0,80,145,.16)'; for(const p of pts){{p.x+=p.vx;p.y+=p.vy;if(p.x<0||p.x>c.width)p.vx*=-1;if(p.y<0||p.y>c.height)p.vy*=-1;ctx.beginPath();ctx.arc(p.x,p.y,2.1*devicePixelRatio,0,Math.PI*2);ctx.fill();}} for(let i=0;i<pts.length;i++)for(let j=i+1;j<pts.length;j++){{const a=pts[i],b=pts[j],d=Math.hypot(a.x-b.x,a.y-b.y); if(d<190*devicePixelRatio){{ctx.globalAlpha=1-d/(190*devicePixelRatio);ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.stroke();ctx.globalAlpha=1;}}}} requestAnimationFrame(anim)}} anim();
</script></body></html>'''
html_path.write_text(html_content, encoding='utf-8')
print(html_path)
