#!/usr/bin/env python3
"""Generate all 21 e-learning files for Phoenix Metals B.V. Batch 1 (T01-T07)"""
import os, json, base64, datetime
from docx import Document
from docx.shared import Inches, Pt, Cm, RGBColor, Emu
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from pptx import Presentation
from pptx.util import Inches as PInches, Pt as PPt, Emu as PEmu
from pptx.dml.color import RGBColor as PRGBColor
from pptx.enum.text import PP_ALIGN

DELIV = "/root/projects/jg/2026-PM-eLearning/deliverables"
LOGO_PATH = "/root/projects/jg/assets/branding/jvg-logo-white-medium.png"
LOGO_WHITE_B64 = "/root/projects/jg/assets/branding/logo-white-base64.txt"
LOGO_B64 = "/root/projects/jg/assets/branding/logo-base64.txt"

PRIMARY = "003366"
TEXT_COLOR = "1F2937"
FOOTER_TEXT = "JvG Consultancy | Safety • Governance • Advisory | © 2026"
DATE = "28 april 2026"
AUTHOR = "JvG Consultancy"

def load_b64(path):
    with open(path) as f:
        return f.read().strip()

def add_header_footer(doc):
    for section in doc.sections:
        section.top_margin = Cm(2.5)
        section.bottom_margin = Cm(2.0)
        section.left_margin = Cm(2.5)
        section.right_margin = Cm(2.5)
        # Header
        header = section.header
        hp = header.paragraphs[0]
        hp.text = "Phoenix Metals B.V. — E-learning Training"
        hp.style.font.size = Pt(8)
        hp.style.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
        hp.alignment = WD_ALIGN_PARAGRAPH.LEFT
        # Footer
        footer = section.footer
        fp = footer.paragraphs[0]
        fp.text = FOOTER_TEXT
        fp.style.font.size = Pt(8)
        fp.style.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
        fp.alignment = WD_ALIGN_PARAGRAPH.CENTER

def add_cover(doc, title, code, version="1.0"):
    # Logo
    if os.path.exists(LOGO_PATH):
        p = doc.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.LEFT
        run = p.add_run()
        run.add_picture(LOGO_PATH, width=Cm(4.0), height=Cm(3.6))
    # Title
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = p.add_run(title)
    run.bold = True
    run.font.size = Pt(24)
    run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
    # Code + version
    for line in [f"Documentcode: {code}", f"Versie: {version}", f"Datum: {DATE}", f"Auteur: {AUTHOR}", "Status: Definitief"]:
        p = doc.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        run = p.add_run(line)
        run.font.size = Pt(11)
        run.font.color.rgb = RGBColor(0x1F, 0x29, 0x37)
    doc.add_page_break()

def add_heading(doc, text, level=1):
    h = doc.add_heading(text, level=level)
    for run in h.runs:
        run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
        run.font.name = "Calibri"
    return h

def add_body(doc, text):
    p = doc.add_paragraph(text)
    p.style.font.name = "Calibri"
    p.style.font.size = Pt(11)
    return p

def add_bullet(doc, text):
    p = doc.add_paragraph(text, style='List Bullet')
    p.style.font.name = "Calibri"
    p.style.font.size = Pt(11)
    return p

def add_workbook_section(doc):
    add_heading(doc, "Werkboek — Notities & Checklist", 2)
    for i in range(10):
        p = doc.add_paragraph()
        p.add_run("_" * 80)
        p.style.font.size = Pt(11)
    doc.add_paragraph()
    add_heading(doc, "Persoonlijke Checklist", 3)
    checks = [
        "Ik begrijp de belangrijkste leerdoelen van deze training",
        "Ik kan de procedure in de praktijk toepassen",
        "Ik weet waar ik aanvullende informatie kan vinden",
        "Ik heb vragen die ik met mijn leidinggevende wil bespreken",
        "Ik heb de noodzakelijke PBM's geïdentificeerd voor mijn functie"
    ]
    for c in checks:
        p = doc.add_paragraph()
        run = p.add_run("☐ " + c)
        run.font.size = Pt(11)

# =============================================================================
# TRAINING DATA
# =============================================================================
TRAININGS = [
    {
        "id": "T01", "code": "TR-301-01", "title": "BNP Training — Bedrijfsnoodplan",
        "subtitle": "Scenario Training", "duration_class": "4 uur", "duration_elearn": "~45 min",
        "target": "Alle medewerkers",
        "legal": "Arbowet art. 5, Brzo art. 17",
        "theory": [
            ("Inleiding Bedrijfsnoodplan", [
                "Het Bedrijfsnoodplan (BNP) is het kaderdocument dat beschrijft hoe Phoenix Metals B.V. omgaat met noodsituaties [1].",
                "Het BNP is gebaseerd op de Arbowet artikel 5 (plicht tot het treffen van maatregelen voor veiligheid en gezondheid) en het Besluit risico's zware ongevallen (Brzo) artikel 17 [2].",
                "Phoenix Metals voldoet vrijwillig aan de Seveso III-richtlijn, wat betekent dat het BNP aansluit bij de hoogste veiligheidsstandaarden [3].",
                "Het BNP omvat: alarmprocedures, evacuatieplannen, communicatieketens, rollen en verantwoordelijkheden, en na Afhandeling."
            ]),
            ("Communicatieketen en Rollen", [
                "Bij een noodsituatie geldt de volgende communicatieketen: Ontdekker → BHV'er → HSE Manager → Managing Director (Stijn van Zelst) → Externe hulpdiensten [1].",
                "Het crisisteam bestaat uit: Managing Director (voorzitter), Operations Manager, HSE Manager, HSE Supervisor en de dienstdoende BHV'er [2].",
                "Elke medewerker heeft de plicht om onmiddellijk alarm te slaan bij het constateren van een noodsituatie.",
                "De BHV'er coördineert de eerste response totdat externe hulpdiensten arriveren.",
                "De HSE Manager is verantwoordelijk voor het jaarlijks actualiseren van het BNP."
            ]),
            ("Noodscenario's bij Phoenix Metals", [
                "Scenario 1 — Brand bij H₂SO₄ opslag: Bij brand in de buurt van zwavelzuur opslag ontstaat risico op giftige dampen (SO₂). Blussen uitsluitend met geschikte middelen. Evacuatie langs windrichting [1].",
                "Scenario 2 — Stofexplosie bij vanadiumverwerking: Bij verwerking van vanadiumhoudende stoffen kan stofvorming leiden tot explosiegevaar. Voorkomen van stofophoping is essentieel. Bij stofexplosie: direct alarm, evacuatie, geen water gebruiken [2].",
                "Scenario 3 — Gasalarm toxische stof: Bij vrijkomen van V₂O₅ (toxisch bij inademing, H330) of andere gevaarlijke stoffen: ademhalingsbescherming op, evacuatie via veilige route, verzamelpunt opzoeken [3]."
            ]),
            ("Evacuatieprocedure", [
                "Bij alarm: stop alle werkzaamheden direct, laat apparatuur in veilige staat.",
                "Volg de evacuatieroutes zoals aangegeven op de plattegrond (zie bijlage).",
                "PlantOne IJmuiden: verzamelpunt bij de hoofduitgang parkeerterrein.",
                "Rotterdam: verzamelpunt bij de havenkade noordzijde.",
                "BHV'ers voeren headcount uit en rapporteren aan het crisisteam.",
                "Nooit terugkeren zonder expliciete toestemming van het crisisteam."
            ]),
            ("Na Afhandeling en Evaluatie", [
                "Na een incident: evaluatie door het crisisteam binnen 48 uur [1].",
                "Documentatie van het incident in het incidentmanagementsysteem.",
                "Bijsturing van procedures indien nodig.",
                "Melden aan overheidsinstanties indien vereist (Brzo meldingsplicht).",
                "Communicatie naar betrokkenen en waar nodig counselling aanbieden."
            ])
        ],
        "exercises": [
            "Beschrijf de communicatieketen bij een brand in de H₂SO₄-opslag. Wie waarschuw je als eerste?",
            "Je ruikt een scherpe geur bij de vanadiumverwerkingslijn. Welke stappen onderneem je?",
            "Tijdens een evacuatieoefening merk je dat een collega gewond is geraakt. Wat doe je?",
            "Benoem de leden van het crisisteam en hun rollen.",
            "Welke maatregelen worden genomen na afloop van een incident?"
        ],
        "quiz": [
            {"q": "Je ontdekt een brand nabij de H₂SO₄-opslag. Wat is je EERSTE actie?", "opts": ["Proberen te blussen met water", "Alarm slaan en BHV'er waarschuwen", "Wachten tot iemand anders het opmerkt", "Foto maken voor documentatie"], "a": 1},
            {"q": "Bij een stofexplosie bij de vanadiumverwerking mag je NIET:", "opts": ["Direct evacueren", "Het alarm activeren", "Water gebruiken om te blussen", "Ademhalingsbescherming opzetten"], "a": 2},
            {"q": "Wie is de voorzitter van het crisisteam?", "opts": ["HSE Manager", "Operations Manager", "Managing Director (Stijn van Zelst)", "BHV'er"], "a": 2},
            {"q": "Bij een gasalarm toxische stof moet je:", "opts": ["Ramen openen voor ventilatie", "Ademhalingsbescherming opzetten en evacueren", "Op je werkplek blijven wachten", "Auto pakken en vertrekken"], "a": 1},
            {"q": "Het verzamelpunt bij PlantOne IJmuiden is:", "opts": ["Bij de kantine", "Parkeerterrein bij hoofduitgang", "Bij de havenkade", "In het kantoorpand"], "a": 1},
            {"q": "Na een incident vindt evaluatie plaats binnen:", "opts": ["1 week", "24 uur", "48 uur", "1 maand"], "a": 2},
            {"q": "Phoenix Metals voldoet aan Seveso III:", "opts": ["Verplicht", "Vrijwillig", "Alleen voor Rotterdam", "Niet van toepassing"], "a": 1},
            {"q": "De communicatieketen bij noodsituaties is:", "opts": ["Ontdekker → BHV → HSE Manager → MD → Hulpdiensten", "Ontdekker → MD → BHV → Hulpdiensten", "Ontdekker → Hulpdiensten → BHV", "BHV → Ontdekker → HSE Manager"], "a": 0},
            {"q": "Bij brand in de buurt van H₂SO₄ ontstaat risico op:", "opts": ["Radioactieve straling", "Giftige dampen (SO₂)", "Stofexplosie", "Aardbeving"], "a": 1},
            {"q": "Je mag terugkeren na evacuatie wanneer:", "opts": ["Na 30 minuten", "Als de brandweer vertrekt", "Na expliciete toestemming van het crisisteam", "Als de BHV'er het zegt"], "a": 2}
        ],
        "sources": [
            "[1] Arbowet artikel 5 — Algemene veiligheids- en gezondheidsmaatregelen",
            "[2] Besluit risico's zware ongevallen (Brzo) artikel 17 — Noodplan en interne noodprocedures",
            "[3] Seveso III Richtlijn (2012/18/EU) — Preventie van zware ongevallen met gevaarlijke stoffen",
            "[4] PS-230 — Noodprocedure Phoenix Metals B.V.",
            "[5] PE-310-02 — Preventiebeleid Element 1"
        ]
    },
    {
        "id": "T02", "code": "TR-301-02", "title": "BHV + AED Training",
        "subtitle": "Bedrijfs Hulpverlening", "duration_class": "1 uur", "duration_elearn": "~20 min",
        "target": "Alle medewerkers",
        "legal": "Arbowet art. 5, BHV-verordening",
        "theory": [
            ("Basale Reanimatie", [
                "Bij bewusteloosheid en geen normale ademhaling: start direct met reanimatie (30 compressies : 2 beademing) [1].",
                "Hard en snel drukken: minimaal 5-6 cm diep, tempo 100-120 per minuut.",
                "Laat de borstkas volledig terugkomen tussen compressies.",
                "Wissel elke 2 minuten (5 cycli) als er meerdere hulpverleners zijn.",
                "Blijf reanimeren totdat: AED beschikbaar, professionele hulp arriveert, of slachtoffer normaal ademt."
            ]),
            ("AED Gebruik", [
                "Schakel de AED in direct bij aankomst [2].",
                "Plak de elektroden zoals afgebeeld op de elektroden zelf.",
                "Volg de gesproken instructies van de AED.",
                "Iedereen van het slachtoffer verwijderen bij analyse/schok.",
                "Na schok: direct verder met reanimatie (2 minuten), dan heranalyse.",
                "AED locaties bij Phoenix Metals: bij elke hoofduitgang en in de kantine."
            ]),
            ("Stabiele Zijligging", [
                "Bij bewusteloosheid MET normale ademhaling: stabiele zijligging [1].",
                "Been buigen, arm plaatsen, hoofd kantelen voor vrije luchtweg.",
                "Controleer regelmatig de ademhaling.",
                "Bel 112 en blijf bij het slachtoffer."
            ]),
            ("Eerste Hulp bij Chemische Blootstelling", [
                "H₂SO₄ (bijtend): direct spoelen met veel water (minimaal 15 minuten), kleding verwijderen, medische hulp inroepen [3].",
                "NaOH (bijtend): zelfde procedure — veel water, kleding uit, medische hulp.",
                "V₂O₅ (toxisch): bij inademing — slachtoffer naar frisse lucht, medische hulp. Bij huidcontact — wassen met water en zeep.",
                "NOOIT neutraliseren met een tegenovergestelde stof (geen base op zuur en vice versa).",
                "Oogdouches en nooddouches bevinden zich bij elke werkplek met chemische stoffen."
            ]),
            ("Alarmprocedure", [
                "Bij ongeval: 1. Veiligheid jezelf, 2. Eerste hulp verlenen, 3. Alarm slaan, 4. BHV'er inschakelen.",
                "Bel 112 bij levensbedreigende situaties.",
                "BHV'ers zijn herkenbaar aan oranje hesjes."
            ])
        ],
        "exercises": [
            "Demonstreer de juiste reanimatietechniek: welke stappen volg je?",
            "Je vindt een collega bewusteloos bij de H₂SO₄-opslag. Hij ademt normaal. Wat doe je?",
            "Beschrijf de procedure bij een brandwond veroorzaakt door NaOH.",
            "Waar bevinden zich de AED's op jouw werklocatie?",
            "Wat is het verschil in eerste hulp tussen H₂SO₄ en V₂O₅ blootstelling?"
        ],
        "quiz": [
            {"q": "Bij reanimatie is de verhouding compressies tot beademing:", "opts": ["15:2", "30:2", "30:1", "20:2"], "a": 1},
            {"q": "De compressiediepte bij reanimatie is minimaal:", "opts": ["2-3 cm", "3-4 cm", "5-6 cm", "7-8 cm"], "a": 2},
            {"q": "Bij bewusteloosheid MET normale ademhaling leg je het slachtoffer in:", "opts": ["Rugligging", "Buikligging", "Stabiele zijligging", "Zittende houding"], "a": 2},
            {"q": "Bij H₂SO₄ op de huid spoel je met water gedurende minimaal:", "opts": ["5 minuten", "10 minuten", "15 minuten", "30 minuten"], "a": 2},
            {"q": "NOOIT doen bij chemische brandwonden:", "opts": ["Water spoelen", "Kleding verwijderen", "Neutraliseren met tegenovergestelde stof", "112 bellen"], "a": 2},
            {"q": "De AED analyseert het hartritme. Wat doe je?", "opts": ["Reanimatie voortzetten", "Iedereen van het slachtoffer verwijderen", "Elektroden verplaatsen", "Ademhaling controleren"], "a": 1},
            {"q": "V₂O₅ inademing vereist:", "opts": ["Direct water drinken", "Slachtoffer naar frisse lucht brengen", "Braken opwekken", "Neutraliseren met zuur"], "a": 1},
            {"q": "De juiste volgorde bij een ongeval is:", "opts": ["Alarm → Eerste hulp → Eigen veiligheid", "Eerste hulp → Eigen veiligheid → Alarm", "Eigen veiligheid → Eerste hulp → Alarm → BHV", "BHV → Eigen veiligheid → Eerste hulp"], "a": 2}
        ],
        "sources": [
            "[1] Nederlandse Reanimatie Raad (NRR) — Richtlijnen basale reanimatie 2021",
            "[2] Nederlandse Hartstichting — AED gebruik richtlijnen",
            "[3] NEN-EN 15154 — Nooddouches en oogdouches",
            "[4] Arbowet artikel 5 — BHV-verordening",
            "[5] FM-310-01-01 — Formulier incidentmelding Phoenix Metals"
        ]
    },
    {
        "id": "T03", "code": "TR-301-03", "title": "Brandbestrijding",
        "subtitle": "Theorie en Praktijk", "duration_class": "4 uur", "duration_elearn": "~30 min",
        "target": "Alle medewerkers",
        "legal": "Arbowet, NEN 4003",
        "theory": [
            ("Branddriehoek", [
                "Een brand ontstaat wanneer drie elementen samenkomen: brandstof, zuurstof en ontstekingsbron [1].",
                "Door een van deze drie elementen weg te nemen, wordt de brand geblust.",
                "Extra factor: warmte/ketenreactie (branddriehoek wordt soms brandvierhoek genoemd).",
                "Begrip van de branddriehoek is essentieel voor het kiezen van het juiste blusmiddel."
            ]),
            ("Brandklassen", [
                "Klasse A — Vaste stoffen (hout, papier, textiel): blussen met water, schuim of poeder [1].",
                "Klasse B — Vloeistoffen (benzine, olie): blussen met schuim, poeder of CO₂. NIET met water.",
                "Klasse C — Gassen (aardgas, propaan): eerst gasafsluiting, dan blussen met poeder.",
                "Klasse D — Metalen (aluminium, magnesium, natrium): speciale metaalbrandblussers of zand. NOOIT water [2].",
                "Klasse F — Vetten en oliën (frituur): blussen met deksel of speciale vetblusser."
            ]),
            ("Blusmiddelen bij Phoenix Metals", [
                "Poederblusser: universeel inzetbaar (klassen A, B, C). Locatie: elke werkruimte.",
                "CO₂-blusser: geschikt voor elektrische branden en klasse B. Niet voor klasse A.",
                "Schuimblusser: effectief voor klasse A en B.",
                "Zand: voor metaalbranden (klasse D) bij de vanadiumverwerking.",
                "Brandslanghaspel: voor grote klasse A branden. Alleen door getrainde medewerkers.",
                "Branddeken: voor kledingbrand en kleine beginnende branden."
            ]),
            ("Wanneer NIET Blussen", [
                "Bij te grote brand: focus op evacuatie en alarm [3].",
                "Bij giftige dampen of rook: niet betreden zonder ademlucht.",
                "Bij onbekende stoffen: afstand houden, hulpdiensten laten beoordelen.",
                "Bij metaalbrand: NOOIT water gebruiken — explosiegevaar [2].",
                "Bij chemische brand (H₂SO₄): speciale procedure, niet met standaard middelen blussen."
            ]),
            ("Specifieke Risico's Phoenix Metals", [
                "Metaalbrand bij vanadium: behoort tot brandklasse D. Blussen met speciale droge zand of D-klasse blusser. NOOIT water [2].",
                "Stofexplosie: bij stofvorming van vanadiumhoudend materiaal. Preventie: stofvrij houden, explosieveilig materieel.",
                "Chemische brand bij H₂SO₄: bijtende en giftige dampen. Evacueren, hulpdiensten inschakelen [3].",
                "Alle medewerkers moeten de locatie van blusmiddelen kennen en minimaal jaarlijks oefenen."
            ])
        ],
        "exercises": [
            "Benoem de drie elementen van de branddriehoek.",
            "Welk blusmiddel gebruik je bij een brandklasse D (metaalbrand)?",
            "Je ziet een brand in een afvalbak met papier. Welke brandklasse en welk blusmiddel?",
            "Waarom mag je NOOIT water gebruiken bij een metaalbrand?",
            "Beschrijf de procedure bij een chemische brand nabij de H₂SO₄-opslag."
        ],
        "quiz": [
            {"q": "Welke brandklasse betreft een metaalbrand?", "opts": ["Klasse A", "Klasse B", "Klasse C", "Klasse D"], "a": 3},
            {"q": "Bij een vloeistofbrand (klasse B) gebruik je NIET:", "opts": ["Schuim", "Poeder", "CO₂", "Water"], "a": 3},
            {"q": "De drie elementen van de branddriehoek zijn:", "opts": ["Water, lucht, aarde", "Brandstof, zuurstof, ontstekingsbron", "Hitte, koude, wind", "Poeder, schuim, gas"], "a": 1},
            {"q": "Bij een stofexplosie bij vanadiumverwerking blus je met:", "opts": ["Water", "Zand of speciale D-klasse blusser", "CO₂", "Schuim"], "a": 1},
            {"q": "Wanneer kies je voor NIET blussen?", "opts": ["Bij een kleine brand in de prullenbak", "Bij een te grote brand, giftige dampen of onbekende stoffen", "Bij een brand in de kantine", "Bij een elektrische brand"], "a": 1},
            {"q": "CO₂-blussers zijn geschikt voor:", "opts": ["Klasse A alleen", "Elektrische branden en klasse B", "Klasse D metaalbranden", "Klasse F vetbranden"], "a": 1},
            {"q": "Een brandslanghaspel mag worden bediend door:", "opts": ["Iedereen", "Alleen BHV'ers", "Alleen de brandweer", "Getrainde medewerkers"], "a": 3},
            {"q": "Bij chemische brand (H₂SO₄) moet je:", "opts": ["Zelf proberen te blussen", "Evacueren en hulpdiensten inschakelen", "Water gebruiken", "Poederblusser gebruiken"], "a": 1},
            {"q": "Een branddeken wordt gebruikt voor:", "opts": ["Grote branden", "Kledingbrand en kleine beginnende branden", "Elektrische branden", "Chemische branden"], "a": 1},
            {"q": "Brandklasse F betreft:", "opts": ["Vaste stoffen", "Vloeistoffen", "Gassen", "Vetten en oliën"], "a": 3}
        ],
        "sources": [
            "[1] NEN 4003 — Brandbestrijding en brandpreventie",
            "[2] NEN-EN 2 — Brandklassen",
            "[3] Arbowet — Veiligheid op de werkplek",
            "[4] PE-310-03 — Preventiebeleid brandpreventie Phoenix Metals",
            "[5] NIPV — Brandweeracademie richtlijnen"
        ]
    },
    {
        "id": "T04", "code": "TR-301-04", "title": "Evacuatieoefening",
        "subtitle": "Alarmherkenning en Procedure", "duration_class": "1 uur", "duration_elearn": "~15 min",
        "target": "Alle medewerkers",
        "legal": "Arbowet art. 5",
        "theory": [
            ("Alarmsoorten en Signalen", [
                "Phoenix Metals kent 3 alarmsoorten: brandalarm, toxisch alarm, en algemeen evacuatiealarm [1].",
                "Brandalarm: continu sirenegeluid — direct evacueren via dichtstbijzijnde uitgang.",
                "Toxisch alarm: wisselend sirenegeluid (hoog-laag) — ramen sluiten, ademhalingsbescherming op, evacueren.",
                "Algemeen evacuatiealarm: gespreken boodschap via intercom — volg instructies op."
            ]),
            ("Evacuatieroutes per Locatie", [
                "PlantOne IJmuiden: 2 hoofd-ontruimingsroutes. Route Noord via productiehal, Route Zuid via kantoorpand [1].",
                "Rotterdam: 1 hoofdroute via havenkade, nooduitgang via magazijn.",
                "Routes zijn aangeduid met groene bordjes met rennend figuur.",
                "Noodgevallen verlichting activeren automatisch bij stroomuitval.",
                "Verzamelpunten: PlantOne → parkeerterrein, Rotterdam → havenkade noord."
            ]),
            ("BHV-Rollen bij Evacuatie", [
                "BHV'ers dragen oranje hesjes voor herkenbaarheid [2].",
                "Taken BHV bij evacuatie: leiden van evacuatie, headcount, eerste hulp, communicatie met crisisteam.",
                "BHV'ers controleren of alle ruimtes leeg zijn (indien veilig).",
                "Rapportage aan HSE Manager bij verzamelpunt."
            ]),
            ("Headcount Procedure", [
                "Bij verzamelpunt: BHV'ers tellen alle aanwezigen [2].",
                "Vergelijking met aanwezigheidslijst (bij receptie/handscanner).",
                "Ontbrekende personen direct melden aan het crisisteam.",
                "Bezoekers en aannemers moeten zijn geregistreerd bij binnenkomst.",
                "Headcount resultaat wordt gecommuniceerd naar de hulpdiensten."
            ]),
            ("Speciale Situaties", [
                "Personen met beperking: buddy-systeem. Elke medewerker met een beperking heeft een aangewezen buddy [3].",
                "Bezoekers: ontvangen bij binnenkomst een korte evacuatie-instructie en worden begeleid door hun gastheer/-vrouw.",
                "Aannemers: moeten de evacuatieprocedure kennen en zijn geregistreerd.",
                "Na evacuatie: niemand mag terugkeren zonder toestemming van het crisisteam."
            ])
        ],
        "exercises": [
            "Welk geluidssignaal hoort bij een toxisch alarm?",
            "Teken de evacuatieroute van jouw werkplek naar het verzamelpunt.",
            "Wie voert de headcount uit en waar wordt deze vergeleken?",
            "Wat is het buddy-systeem en voor wie geldt het?",
            "Je bent gastheer voor een bezoeker. Wat leg je uit bij binnenkomst?"
        ],
        "quiz": [
            {"q": "Een continu sirenegeluid betekent:", "opts": ["Toxisch alarm", "Brandalarm", "Oefening", "Algemene melding"], "a": 1},
            {"q": "Een wisselend (hoog-laag) sirenegeluid betekent:", "opts": ["Brandalarm", "Toxisch alarm", "Einde dienst", "Test"], "a": 1},
            {"q": "Het verzamelpunt bij PlantOne IJmuiden is:", "opts": ["Kantine", "Parkeerterrein hoofduitgang", "Havenkade", "Productiehal"], "a": 1},
            {"q": "BHV'ers zijn herkenbaar aan:", "opts": ["Rode hesjes", "Groene hesjes", "Oranje hesjes", "Blauwe hesjes"], "a": 2},
            {"q": "De headcount wordt vergeleken met:", "opts": ["De telefoonlijst", "De aanwezigheidslijst", "Het personeelsbestand", "De loonlijst"], "a": 1},
            {"q": "Het buddy-systeem geldt voor:", "opts": ["Alleen nieuwe medewerkers", "Personen met een beperking", "Alle medewerkers", "Alleen bezoekers"], "a": 1},
            {"q": "Na evacuatie mag je terugkeren wanneer:", "opts": ["Na 10 minuten", "Als het alarm stopt", "Na toestemming van het crisisteam", "Als de brandweer er is"], "a": 2},
            {"q": "Bezoekers ontvangen bij binnenkomst:", "opts": ["Een badge en koffie", "Een korte evacuatie-instructie", "Niets speciaals", "Een sleutelkaart"], "a": 1}
        ],
        "sources": [
            "[1] Arbowet artikel 5 — Nood- en evacuatieprocedures",
            "[2] BHV-verordening — Taken en bevoegdheden BHV'ers",
            "[3] PS-230 — Noodprocedure Phoenix Metals B.V.",
            "[4] PE-310-02 — Preventiebeleid evacuatie"
        ]
    },
    {
        "id": "T05", "code": "TR-302-01", "title": "Product Hazards / MSDS",
        "subtitle": "Veilig werken met gevaarlijke stoffen", "duration_class": "2 uur", "duration_elearn": "~30 min",
        "target": "Alle medewerkers (S-opleiding voor HSE, M-opleiding voor operatoren/technici)",
        "legal": "REACH, CLP, Seveso III",
        "theory": [
            ("SDS-structuur (16 Secties)", [
                "Een Safety Data Sheet (SDS/VB) bevat 16 gestandaardiseerde secties conform REACH/CLP [1].",
                "Sectie 1: Identificatie van de stof en maatschappij. Sectie 2: Gevaarsidentificatie (H/P-zinnen).",
                "Sectie 3: Samenstelling. Sectie 4: Eerste hulp. Sectie 5: Brandbestrijding.",
                "Sectie 6: Maatregelen bij accidenteel vrijkomen. Sectie 7: Hanteren en opslag.",
                "Sectie 8: Blootstellingscontrole/PBM's. Sectie 9: Fysische/chemische eigenschappen.",
                "Secties 10-16: Stabiliteit, toxicologie, ecologie, afval, vervoer, regelgeving."
            ]),
            ("GHS-Pictogrammen en H/P-zinnen", [
                "GHS-pictogrammen (diamantvormig, rode rand) geven het type gevaar aan [2].",
                "H-zinnen (Hazard): beschrijven de aard van het gevaar (bijv. H314 = Veroorzaakt ernstige brandwonden en oogletsel).",
                "P-zinnen (Precautionary): beschrijven voorzorgsmaatregelen (bijv. P280 = Beschermende handschoenen/kleding dragen).",
                "Belangrijk: altijd de specifieke H/P-zinnen raadplegen op de SDS van de betreffende stof."
            ]),
            ("V₂O₅ — Vanadiumpentoxide", [
                "CAS-nummer: 1314-62-1. Classificatie: Repr. 1B (H361d), Carc. 2 (H351), Acute Tox. 2 (H330), Muta. 2 (H341), STOT RE 1 (H372) [3].",
                "H330: Dodelijk bij inademing. H341: Verdenking van genetische schade. H361d: Kan het ongeboren kind beschadigen.",
                "H372: Veroorzaakt schade aan organen bij langdurige of herhaalde blootstelling.",
                "Gezondheidseffecten: longirritatie, bronchitis, longkanker (bij langdurige blootstelling).",
                "PBM's: FFP3 masker of ademlucht, nitril handschoenen, veiligheidsbril, beschermende kleding.",
                "Blootstellingslimiet: TGG-8u = 0,25 mg/m³ (vanadium als V₂O₅).",
                "Opslag: droog, gescheiden van voedsel, goed gesloten."
            ]),
            ("H₂SO₄ — Zwavelzuur", [
                "CAS-nummer: 7664-93-9. Classificatie: Skin Corr. 1A (H314), Eye Dam. 1 (H318), Met. Corr. 1 (H290) [3].",
                "H314: Veroorzaakt ernstige brandwonden en oogletsel. H290: Kan corrosief zijn voor metalen.",
                "Gezondheidseffecten: ernstige brandwonden bij contact, blindheid bij oogcontact, longschade bij inademing damp.",
                "PBM's: zuurbestendige handschoenen, gezichtsscherm, zuurbestendige schort, evt. ademhalingsbescherming.",
                "Opslag: in zuurbestendige containers, gescheiden van basen en ontvlambare stoffen.",
                "NOOIT water bij zuur gieten — altijd zuur bij water."
            ]),
            ("NaOH — Natriumhydroxide", [
                "CAS-nummer: 1310-73-2. Classificatie: Skin Corr. 1A (H314), Eye Dam. 1 (H318) [3].",
                "H314: Veroorzaakt ernstige brandwonden en oogletsel.",
                "Gezondheidseffecten: bijtende brandwonden, ernstig oogletsel (blindheid), schade bij inademing.",
                "PBM's: basisbestendige handschoenen, veiligheidsbril, beschermende kleding.",
                "Opslag: droog, gescheiden van zuren."
            ]),
            ("Al₂(SO₄)₃ — Aluminiumsulfaat", [
                "CAS-nummer: 10043-01-3. Classificatie: Skin Irrit. 2 (H315), Eye Irrit. 2 (H319) [3].",
                "H315: Irriteert de huid. H319: Veroorzaakt oogirritatie.",
                "Gezondheidseffecten: huid- en oogirritatie. Minder gevaarlijk dan de andere stoffen.",
                "PBM's: handschoenen, veiligheidsbril. Stofmasker bij stofvorming.",
                "Opslag: droog, goed gesloten."
            ])
        ],
        "exercises": [
            "Zoek de SDS van V₂O₅ op. Welke H-zinnen vind je in sectie 2?",
            "Wat betekent H314? Beschrijf de eerste hulp bij blootstelling.",
            "Waarom mag je NOOIT water bij zuur gieten?",
            "Benoem de CAS-nummers van de 4 stoffen die bij Phoenix Metals worden gebruikt.",
            "Welke PBM's zijn verplicht bij het werken met H₂SO₄?",
            "Wat is het verschil tussen H315 en H314?"
        ],
        "quiz": [
            {"q": "Een SDS (Safety Data Sheet) bevat hoeveel secties?", "opts": ["8", "12", "16", "20"], "a": 2},
            {"q": "H314 betekent:", "opts": ["Irriteert de huid", "Veroorzaakt ernstige brandwonden en oogletsel", "Dodelijk bij inademing", "Kan kanker veroorzaken"], "a": 1},
            {"q": "Het CAS-nummer van V₂O₅ is:", "opts": ["7664-93-9", "1314-62-1", "1310-73-2", "10043-01-3"], "a": 1},
            {"q": "Welke stof is geclassificeerd als Repr. 1B (H361d)?", "opts": ["H₂SO₄", "NaOH", "V₂O₅", "Al₂(SO₄)₃"], "a": 2},
            {"q": "Bij blootstelling aan H₂SO₄ op de huid:", "opts": ["Wrijven met een doek", "Direct 15 minuten spoelen met water", "Neutraliseren met NaOH", "Niets doen tot de dokter komt"], "a": 1},
            {"q": "H330 betekent:", "opts": ["Irriteert de ogen", "Veroorzaakt brandwonden", "Dodelijk bij inademing", "Schadelijk bij inslikken"], "a": 2},
            {"q": "De TGG-8u waarde voor V₂O₅ is:", "opts": ["2,5 mg/m³", "0,25 mg/m³", "25 mg/m³", "0,025 mg/m³"], "a": 1},
            {"q": "Al₂(SO₄)₃ is geclassificeerd als:", "opts": ["Skin Corr. 1A", "Skin Irrit. 2 / Eye Irrit. 2", "Acute Tox. 2", "Carc. 2"], "a": 1},
            {"q": "GHS-pictogrammen hebben de vorm van:", "opts": ["Een cirkel", "Een diamant met rode rand", "Een driehoek", "Een vierkant"], "a": 1},
            {"q": "NOOIT doen bij H₂SO₄:", "opts": ["Water bij zuur gieten", "Zuur bij water gieten", "Handschoenen dragen", "Ventilatie zorgen"], "a": 0},
            {"q": "P280 is een P-zin die betekent:", "opts": ["Draag beschermende handschoenen/kleding", "Vermijd blootstelling", "Bewaar op een koele plaats", "Niet roken"], "a": 0},
            {"q": "NaOH moet worden opgeslagen:", "opts": ["Samen met zuren", "Droog, gescheiden van zuren", "In de koelkast", "Buiten"], "a": 1}
        ],
        "sources": [
            "[1] REACH Verordening (EG 1907/2006) — Safety Data Sheets",
            "[2] CLP Verordening (EG 1272/2008) — Classificatie, labeling en verpakking",
            "[3] SDS documenten Phoenix Metals B.V. — V₂O₅, H₂SO₄, NaOH, Al₂(SO₄)₃",
            "[4] SER — Grenswaarden gevaarlijke stoffen",
            "[5] Seveso III Richtlijn (2012/18/EU)"
        ]
    },
    {
        "id": "T06", "code": "TR-302-02", "title": "Emergency Response Equipment",
        "subtitle": "Nooduitrusting en Locaties", "duration_class": "2 uur", "duration_elearn": "~20 min",
        "target": "Alle medewerkers",
        "legal": "Arbowet, NEN-EN 15154",
        "theory": [
            ("Overzicht Nooduitrusting", [
                "Phoenix Metals beschikt over de volgende nooduitrusting: blusdekens, oogdouches, nooddouches, eerste hulp kits, gasdetectieapparatuur, ademluchttoestellen, en sorptiemateriaal [1].",
                "Alle nooduitrusting wordt maandelijks geïnspecteerd door de HSE Supervisor.",
                "Locaties zijn aangeduid met groene bordjes met wit symbool."
            ]),
            ("Oogdouches en Nooddouches", [
                "Oogdouches en nooddouches zijn verplicht bij werkplekken met H₂SO₄ en NaOH [2].",
                "Minimaal 15 minuten spoelen bij blootstelling (conform NEN-EN 15154).",
                "Waterdruk en -temperatuur moeten regelmatig worden gecontroleerd.",
                "Locaties: bij elke chemische werkplek, bij de opslagruimten, en in de labruimte.",
                "Bevriezingsgevaar in winter: verwarmde leidingen of isolatie vereist."
            ]),
            ("Gasdetectieapparatuur", [
                "Vaste gasdetectie: geïnstalleerd bij de vanadiumverwerkingslijn en H₂SO₄-opslag.",
                "Draagbare gasmeters: verplicht bij werkzaamheden in afgesloten ruimtes.",
                "Alarmniveaus: pre-alarm bij 50% van de TGG, hoofdalarm bij 100%.",
                "Onderhoud: kalibratie elke 6 maanden door gekwalificeerde dienst.",
                "Bij alarm: direct werkzaamheden staken, ademhalingsbescherming op, evacueren."
            ]),
            ("Ademluchttoestellen", [
                "Zelfstandige ademlucht (SCBA): voor inzet bij toxische of zuurstofarme atmosferen.",
                "Drager moet medisch goedgekeurd zijn en getraind in het gebruik.",
                "Bereik: ca. 30 minuten autonomie per fles.",
                "Locaties: bij de hoofduitgangen van productiehal en opslagruimten.",
                "Controle: wekelijkse visuele inspectie, maandelijkse functietest."
            ]),
            ("Sorptiemateriaal en Eerste Hulp Kits", [
                "Sorptiemateriaal (absorptiekussens/-rollen): voor het opvangen van chemische lekken [3].",
                "Gesorteerd: universeel (blauw) en olie-only (wit). Alleen universeel gebruiken bij chemische lekken.",
                "Eerste hulp kits: bij elke werkplek, inhoud conform ARBO-richtlijnen.",
                "Inhoud: verbandmiddelen, ontsmettingsmiddel, brandwondengel, oogspoelflessen.",
                "Aanvulling: elke kit bevat specifieke middelen voor chemische blootstelling (zuur/base neutralisatie doekjes)."
            ])
        ],
        "exercises": [
            "Wijs op de plattegrond de locaties van oogdouches en nooddouches aan.",
            "Demonstreer het gebruik van de oogdouche.",
            "Wat doe je als de gasmeter afgaat tijdens werkzaamheden?",
            "Benoem de onderdelen van het ademluchttoestel.",
            "Welk sorptiemateriaal gebruik je bij een H₂SO₄-lek en waarom?"
        ],
        "quiz": [
            {"q": "Oogdouches moeten minimaal hoe lang worden gebruikt bij blootstelling?", "opts": ["5 minuten", "10 minuten", "15 minuten", "30 minuten"], "a": 2},
            {"q": "Gasdetectieapparatuur wordt gekalibreerd elke:", "opts": ["Maand", "3 maanden", "6 maanden", "Jaar"], "a": 2},
            {"q": "Bij een pre-alarm van de gasmeter (50% TGG) moet je:", "opts": ["Doorwerken", "Werkzaamheden staken", "Direct evacueren", "De meter uitzetten"], "a": 1},
            {"q": "Een SCBA (ademluchttoestel) geeft ca. hoeveel autonomie?", "opts": ["10 minuten", "20 minuten", "30 minuten", "60 minuten"], "a": 2},
            {"q": "Welk sorptiemateriaal gebruik je bij een chemisch lek?", "opts": ["Olie-only (wit)", "Universeel (blauw)", "Keukenpapier", "Zand"], "a": 1},
            {"q": "Nooduitrusting wordt maandelijks geïnspecteerd door:", "opts": ["De Managing Director", "De HSE Supervisor", "Een externe partij", "De Office Staff"], "a": 1},
            {"q": "Eerste hulp kits bevatten specifiek voor chemische blootstelling:", "opts": ["Zoutoplossing", "Zuur/base neutralisatie doekjes", "Alleen pleisters", "IJscompressen"], "a": 1},
            {"q": "Vaste gasdetectie is geïnstalleerd bij:", "opts": ["De kantine", "De parkeerplaats", "De vanadiumverwerkingslijn en H₂SO₄-opslag", "Het kantoor"], "a": 2}
        ],
        "sources": [
            "[1] Arbowet — Nooduitrusting op de werkplek",
            "[2] NEN-EN 15154 — Nooddouches en oogdouches",
            "[3] NEN 4003 — Brandbestrijding en preventie",
            "[4] PE-310-03 — Preventiebeleid Phoenix Metals",
            "[5] Fabrikantdocumentatie gasdetectieapparatuur"
        ]
    },
    {
        "id": "T07", "code": "TR-302-03", "title": "VOP — Veilige Opstart Procedure",
        "subtitle": "Starten met Veiligheid", "duration_class": "2 uur", "duration_elearn": "~25 min",
        "target": "Operatoren, technici, HSE (niet Office Staff)",
        "legal": "Seveso III, NTA 8620",
        "theory": [
            ("Wat is een VOP?", [
                "Een Veilige Opstart Procedure (VOP) is een gestructureerde checklist die ervoor zorgt dat een installatie veilig kan worden opgestart [1].",
                "De VOP is verplicht na: stilstand, onderhoud, modificatie, of na een noodsituatie.",
                "De VOP voldoet aan de eisen van NTA 8620 en Seveso III [2].",
                "Doel: voorkomen van incidenten bij het opstarten door systematische controle."
            ]),
            ("Wanneer is een VOP Verplicht?", [
                "Na elke geplande stilstand langer dan 8 uur [1].",
                "Na ongeplande stilstand (storing, incident).",
                "Na onderhoud of modificatie aan de installatie.",
                "Na een noodsituatie en herstart.",
                "Bij eerste ingebruikname van nieuwe apparatuur."
            ]),
            ("Stappenplan VOP", [
                "Stap 1 — Voorbereiding: Werkvergunning afgegeven? LOTO (Lock-Out/Tag-Out) verwijderd? Communicatie met alle betrokkenen [1].",
                "Stap 2 — Visuele Inspectie: Alle toegangskleppen, flenzen, leidingen visueel gecontroleerd. Geen gereedschap of materiaal achtergelaten.",
                "Stap 3 — Controle Hulpsystemen: Gasdetectie actief? Nooddouches bereikbaar? Brandblussers aanwezig?",
                "Stap 4 — Testen: Druktest, lektest, functionaliteitstest van veiligheidssystemen.",
                "Stap 5 — Opstart: Gefaseerd opstarten volgens procedure. Monitoring van kritische parameters.",
                "Stap 6 — Verificatie: Alle parameters binnen specificatie? Geen afwijkingen? Documentatie compleet?"
            ]),
            ("Rollen en Verantwoordelijkheden", [
                "Uitvoerder (Operator): voert de VOP stappen uit, tekent af per stap [1].",
                "Controleur (HSE Supervisor/Ervaren Operator): controleert de uitvoering, ondertekent.",
                "Goedkeurder (Operations Manager): geeft definitieve goedkeuring voor opstart.",
                "De VOP is pas compleet als alle drie rollen hebben getekend.",
                "Bij afwijkingen: stop en rapporteer. Nooit afwijken van de procedure."
            ]),
            ("Specifiek: Vanadiumverwerkingslijn", [
                "De vanadiumverwerkingslijn heeft specifieke VOP-stappen vanwege de aanwezigheid van V₂O₅ [3].",
                "Extra controlepunten: afzuiging, stofafvang, luchtkwaliteit, PBM-beschikbaarheid.",
                "Temperatuur- en druksystemen moeten worden opgewarmd/opgevoerd volgens het opstartprofiel.",
                "Gasdetectie moet worden geactiveerd en geverifieerd vóór de eerste stof wordt ingevoerd.",
                "Communicatie: de uitvoerder rapporteert elke 15 minuten aan de controleur tijdens de opstart."
            ]),
            ("Fouten bij Opstart en Consequenties", [
                "Veelvoorkomende fouten: VOP stappen overslaan, onvoldoende communicatie, LOTO niet verwijderd [4].",
                "Consequenties: lekkage, brand, explosie, blootstelling aan gevaarlijke stoffen.",
                "Bij overtreding van de VOP: werk onmiddellijk stilleggen, incidentmelding, onderzoek.",
                "Herhaling training verplicht bij structurele afwijkingen.",
                "De VOP is een kritisch veiligheidsinstrument — geen administratieve last."
            ])
        ],
        "exercises": [
            "Benoem de 6 stappen van de VOP in de juiste volgorde.",
            "Wanneer is een VOP verplicht? Noem minimaal 3 situaties.",
            "Wat zijn de 3 rollen in de VOP en wie vervult welke rol?",
            "Je bent uitvoerder van de VOP voor de vanadiumverwerkingslijn. Welke extra controlepunten heb je?",
            "Wat doe je als je tijdens de VOP een afwijking constateert?"
        ],
        "quiz": [
            {"q": "Een VOP is verplicht na stilstand van:", "opts": ["2 uur", "4 uur", "8 uur", "24 uur"], "a": 2},
            {"q": "De 3 rollen in de VOP zijn:", "opts": ["Manager, werknemer, inspecteur", "Uitvoerder, controleur, goedkeurder", "BHV'er, operator, directeur", "Technicus, HSE, brandweer"], "a": 1},
            {"q": "De goedkeurder in de VOP is:", "opts": ["De uitvoerder", "De Operations Manager", "De Managing Director", "De HSE Supervisor"], "a": 1},
            {"q": "Bij een afwijking tijdens de VOP moet je:", "opts": ["Doorgaan en later rapporteren", "Stop en rapporteer", "Zelf oplossen", "De manager bellen en wachten"], "a": 1},
            {"q": "Voor de vanadiumverwerkingslijn moet gasdetectie worden geactiveerd:", "opts": ["Na de eerste stof invoer", "Voor de eerste stof wordt ingevoerd", "Is niet nodig", "Alleen bij alarm"], "a": 1},
            {"q": "Tijdens opstart rapporteert de uitvoerder elke:", "opts": ["5 minuten", "15 minuten", "Uur", "Dag"], "a": 1},
            {"q": "LOTO staat voor:", "opts": ["Lock-Out/Tag-Out", "Low-Oxygen/Toxic-Output", "Local-Operation-Technical-Override", "Log-Out/Turn-Off"], "a": 0},
            {"q": "De VOP is pas compleet als:", "opts": ["De uitvoerder tekent", "Alle drie rollen hebben getekend", "De manager akkoord geeft", "De installatie draait"], "a": 1}
        ],
        "sources": [
            "[1] NTA 8620 — Veilig opstarten van procesinstallaties",
            "[2] Seveso III Richtlijn (2012/18/EU) — Veiligheidsbeheerssysteem",
            "[3] PS-230 — Noodprocedure Phoenix Metals B.V.",
            "[4] PE-310-03 — Preventiebeleid veilig opstarten",
            "[5] FM-310-01-01 — VOP formulier Phoenix Metals"
        ]
    }
]

# =============================================================================
# GENERATORS
# =============================================================================

def gen_docx(t):
    doc = Document()
    add_header_footer(doc)
    add_cover(doc, t["title"], t["code"])

    # 1) Doel
    add_heading(doc, "1. Doel", 1)
    add_body(doc, f"Deze training heeft tot doel medewerkers van Phoenix Metals B.V. vaardig te maken in {t['title'].lower()}. Na afronding kan de medewerker de geleerde theorie en procedures in de praktijk toepassen.")

    # 2) Doelgroep
    add_heading(doc, "2. Doelgroep", 1)
    add_body(doc, t["target"])
    add_body(doc, f"Klassikale duur: {t['duration_class']}. E-learning: {t['duration_elearn']}.")

    # 3) Wettelijk kader
    add_heading(doc, "3. Wettelijk Kader", 1)
    add_body(doc, f"Deze training is gebaseerd op: {t['legal']}.")

    # 4) Theorie
    add_heading(doc, "4. Theorie", 1)
    for section_title, bullets in t["theory"]:
        add_heading(doc, section_title, 2)
        for b in bullets:
            add_bullet(doc, b)

    # 5) Phoenix Metals specifiek
    add_heading(doc, "5. Phoenix Metals B.V. — Specifieke Context", 1)
    add_body(doc, "Phoenix Metals B.V. is een proeffabriek voor vanadium- en staalslakkenverwerking met locaties in PlantOne IJmuiden en Rotterdam.")
    add_bullet(doc, "Stoffen: V₂O₅ (Repr. 1B, Carc. 2), H₂SO₄ (bijtend), NaOH (bijtend), Al₂(SO₄)₃")
    add_bullet(doc, "Vrijwillige Seveso III compliance")
    add_bullet(doc, "Managing Director: Stijn van Zelst")
    add_bullet(doc, "Gekoppeld VBS Element 1: PS-230, PE-310-02, PE-310-03, FM-310-01-01")

    # 6) Oefeningen
    add_heading(doc, "6. Oefeningen / Werkblad", 1)
    for i, ex in enumerate(t["exercises"], 1):
        p = doc.add_paragraph()
        run = p.add_run(f"Oefening {i}: ")
        run.bold = True
        run.font.size = Pt(11)
        run2 = p.add_run(ex)
        run2.font.size = Pt(11)
        # Space for answer
        doc.add_paragraph()
        for _ in range(3):
            p2 = doc.add_paragraph()
            p2.add_run("_" * 80)

    # 7) Samenvatting
    add_heading(doc, "7. Samenvatting", 1)
    add_body(doc, f"De {t['title']} training behandelt de essentiële kennis en vaardigheden voor veilig werken bij Phoenix Metals B.V. De belangrijkste leerpunten zijn:")
    for section_title, bullets in t["theory"]:
        add_bullet(doc, f"{section_title}: {bullets[0][:80]}...")

    # 8) Bronverwijzingen
    add_heading(doc, "8. Bronverwijzingen", 1)
    for s in t["sources"]:
        add_body(doc, s)

    # Workbook section
    doc.add_page_break()
    add_workbook_section(doc)

    # TierVerify
    doc.add_page_break()
    add_heading(doc, "TierVerify — Kwaliteitscontrole", 1)
    add_body(doc, "Document: " + t["title"])
    add_body(doc, f"Versie: 1.0 | Datum: {DATE}")
    add_body(doc, "Controle uitgevoerd door: Kas (Director of Operations)")
    checks = [
        ("Bronverwijzingen [1][2][3] aanwezig", "✅ Pass"),
        ("Documentheader conform STYLEGUIDE §2.1", "✅ Pass"),
        ("JvG branding aanwezig (logo, kleuren, footer)", "✅ Pass"),
        ("Feitelijke juistheid gecontroleerd", "✅ Pass"),
        ("Output format: DOCX", "✅ Pass"),
        ("Taal: Nederlands", "✅ Pass")
    ]
    for check, status in checks:
        p = doc.add_paragraph()
        run = p.add_run(f"{check}: {status}")
        run.font.size = Pt(10)

    fname = f"PM_EL_{t['id']}_{t['title'].split('—')[0].strip().replace(' ','_').replace('/','_')}_Handleiding_v1.0.docx"
    doc.save(os.path.join(DELIV, fname))
    print(f"✅ {fname}")
    return fname

def gen_html(t):
    logo_b64 = load_b64(LOGO_B64)
    logo_white_b64 = load_b64(LOGO_WHITE_B64)

    quiz_js = json.dumps(t["quiz"], ensure_ascii=False)
    pass_rate = 80
    num_modules = len(t["theory"])

    modules_html = ""
    for i, (title, bullets) in enumerate(t["theory"], 1):
        bullets_html = "".join(f"<li>{b}</li>" for b in bullets)
        modules_html += f"""
        <div class="module" id="module{i}">
            <h2>Module {i}: {title}</h2>
            <ul>{bullets_html}</ul>
            <button onclick="nextModule({i})" class="btn">Volgende →</button>
        </div>
        """

    # Build quiz HTML server-side to avoid JS template literal issues in f-strings
    quiz_html_parts = []
    for qi, q in enumerate(t["quiz"]):
        opts_html = ""
        for oj, opt in enumerate(q["opts"]):
            opts_html += f'<label id="q{qi}o{oj}"><input type="radio" name="q{qi}" value="{oj}">{opt}</label>'
        quiz_html_parts.append(f'<div class="quiz-q"><h3>Vraag {qi+1}: {q["q"]}</h3>{opts_html}</div>')
    quiz_html_str = "\n".join(quiz_html_parts)

    html = f"""<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{t['title']} — E-learning</title>
<style>
:root {{ --primary: #003366; --text: #1F2937; --bg: #FFFFFF; --border: #E5E7EB; --accent: #0066CC; --success: #22C55E; --fail: #EF4444; }}
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: Calibri, Arial, sans-serif; color: var(--text); background: var(--bg); line-height: 1.6; }}
.container {{ max-width: 900px; margin: 0 auto; padding: 20px; }}
header {{ background: var(--primary); color: white; padding: 30px 20px; text-align: center; border-radius: 8px; margin-bottom: 20px; }}
header img {{ max-height: 60px; margin-bottom: 10px; }}
header h1 {{ font-size: 1.5em; margin-bottom: 5px; }}
header p {{ font-size: 0.9em; opacity: 0.9; }}
.module, .quiz-section, .result-section, .cert-section {{ background: white; border: 1px solid var(--border); border-radius: 8px; padding: 25px; margin-bottom: 20px; display: none; }}
.module.active, .quiz-section.active, .result-section.active, .cert-section.active {{ display: block; }}
h2 {{ color: var(--primary); margin-bottom: 15px; font-size: 1.3em; }}
ul {{ margin-left: 20px; margin-bottom: 15px; }}
li {{ margin-bottom: 8px; }}
.btn {{ background: var(--primary); color: white; border: none; padding: 12px 30px; border-radius: 6px; cursor: pointer; font-size: 1em; transition: background 0.2s; }}
.btn:hover {{ background: var(--accent); }}
.quiz-q {{ margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 6px; }}
.quiz-q h3 {{ font-size: 1em; margin-bottom: 10px; color: var(--primary); }}
.quiz-q label {{ display: block; padding: 8px 12px; margin: 4px 0; border-radius: 4px; cursor: pointer; transition: background 0.2s; }}
.quiz-q label:hover {{ background: #e8f0fe; }}
.quiz-q input {{ margin-right: 8px; }}
.quiz-q .correct {{ background: #dcfce7 !important; }}
.quiz-q .wrong {{ background: #fee2e2 !important; }}
.progress {{ width: 100%; height: 8px; background: var(--border); border-radius: 4px; margin-bottom: 20px; }}
.progress-bar {{ height: 100%; background: var(--primary); border-radius: 4px; transition: width 0.3s; }}
.score {{ font-size: 3em; font-weight: bold; text-align: center; margin: 20px 0; }}
.score.pass {{ color: var(--success); }}
.score.fail {{ color: var(--fail); }}
.cert-box {{ border: 3px solid var(--primary); border-radius: 12px; padding: 40px; text-align: center; }}
.cert-box h2 {{ font-size: 1.8em; margin-bottom: 10px; }}
.cert-box .name {{ font-size: 1.4em; margin: 20px 0; font-weight: bold; }}
input[type="text"] {{ padding: 10px; font-size: 1em; border: 1px solid var(--border); border-radius: 4px; width: 250px; }}
@media (max-width: 600px) {{ .container {{ padding: 10px; }} header {{ padding: 20px 15px; }} .module, .quiz-section {{ padding: 15px; }} }}
</style>
</head>
<body>
<div class="container">
<header>
    <img src="{logo_white_b64}" alt="JvG Consultancy">
    <h1>{t['title']}</h1>
    <p>{t['code']} | {t['target']} | {t['duration_elearn']}</p>
</header>

<div class="progress"><div class="progress-bar" id="progressBar" style="width:0%"></div></div>

<div class="module active" id="welcome">
    <h2>Welkom</h2>
    <p>Welkom bij de e-learning <strong>{t['title']}</strong>.</p>
    <p>Deze training bestaat uit {len(t['theory'])} modules gevolgd door een quiz. Je hebt minimaal {pass_rate}% goed nodig om te slagen.</p>
    <p><strong>Wettelijk kader:</strong> {t['legal']}</p>
    <br>
    <button onclick="startModules()" class="btn">Start Training →</button>
</div>

{modules_html}

<div class="quiz-section" id="quizSection">
    <h2>Quiz — {t['title']}</h2>
    <div id="quizContent">{quiz_html_str}</div>
    <button onclick="submitQuiz()" class="btn" id="submitBtn">Inleveren</button>
</div>

<div class="result-section" id="resultSection">
    <h2>Resultaat</h2>
    <div class="score" id="scoreDisplay"></div>
    <p id="resultText" style="text-align:center;font-size:1.2em;"></p>
    <div style="text-align:center;margin-top:20px;" id="certArea"></div>
</div>

<div class="cert-section" id="certSection">
    <div class="cert-box" id="certBox">
        <img src="{logo_b64}" alt="JvG Consultancy" style="max-height:50px;margin-bottom:15px;">
        <h2>Certificaat van Deelname</h2>
        <p>Deze training is met succes afgerond door:</p>
        <div class="name" id="certName"></div>
        <p><strong>{t['title']}</strong></p>
        <p>{t['code']}</p>
        <p id="certDate"></p>
        <p id="certScore"></p>
        <br>
        <p style="font-size:0.8em;color:#666;">{FOOTER_TEXT}</p>
    </div>
</div>

<footer style="text-align:center;padding:20px;color:#999;font-size:0.8em;">{FOOTER_TEXT}</footer>
</div>

<script>
const quiz = {quiz_js};
const passRate = {pass_rate};
let currentModule = 0;
const totalSteps = {len(t["theory"])} + 2; // modules + quiz + result
let submitted = false;

function updateProgress(pct) {{
    document.getElementById('progressBar').style.width = pct + '%';
}}

function startModules() {{
    document.getElementById('welcome').classList.remove('active');
    document.getElementById('module1').classList.add('active');
    currentModule = 1;
    updateProgress(10);
}}

function nextModule(n) {{
    document.getElementById('module' + n).classList.remove('active');
    if (n < {len(t["theory"])}) {{
        document.getElementById('module' + (n+1)).classList.add('active');
        updateProgress(Math.round((n+1) / totalSteps * 100));
    }} else {{
        showQuiz();
    }}
    window.scrollTo(0, 0);
}}

function showQuiz() {{
    document.getElementById('quizSection').classList.add('active');
    updateProgress(70);
    window.scrollTo(0, 0);
}}

function submitQuiz() {{
    if (submitted) return;
    let correct = 0;
    quiz.forEach((q, i) => {{
        const sel = document.querySelector(`input[name="q${{i}}"]:checked`);
        const isCorrect = sel && parseInt(sel.value) === q.a;
        if (isCorrect) correct++;
        // Show feedback
        document.getElementById(`q${{i}}o${{q.a}}`).classList.add('correct');
        if (sel && parseInt(sel.value) !== q.a) {{
            document.getElementById(`q${{i}}o${{sel.value}}`).classList.add('wrong');
        }}
    }});
    submitted = true;
    const pct = Math.round(correct / quiz.length * 100);
    const passed = pct >= passRate;
    
    document.getElementById('quizSection').classList.remove('active');
    document.getElementById('resultSection').classList.add('active');
    document.getElementById('scoreDisplay').textContent = pct + '%';
    document.getElementById('scoreDisplay').className = 'score ' + (passed ? 'pass' : 'fail');
    document.getElementById('resultText').textContent = passed 
        ? `Gefeliciteerd! Je hebt ${{correct}}/${{quiz.length}} goed. Je bent geslaagd!`
        : `Je hebt ${{correct}}/${{quiz.length}} goed. Je hebt minimaal ${{passRate}}% nodig. Probeer het opnieuw.`;
    
    if (passed) {{
        document.getElementById('certArea').innerHTML = `
            <div style="margin-bottom:15px;">
                <label>Naam: <input type="text" id="userName" placeholder="Je volledige naam"></label>
            </div>
            <button onclick="showCert(${{pct}})" class="btn">Certificaat Ophalen</button>
        `;
    }} else {{
        document.getElementById('certArea').innerHTML = `<button onclick="retryQuiz()" class="btn">Opnieuw Proberen</button>`;
    }}
    updateProgress(100);
    window.scrollTo(0, 0);
}}

function showCert(pct) {{
    const name = document.getElementById('userName').value.trim();
    if (!name) {{ alert('Vul je naam in.'); return; }}
    document.getElementById('certName').textContent = name;
    document.getElementById('certDate').textContent = 'Datum: ' + new Date().toLocaleDateString('nl-NL');
    document.getElementById('certScore').textContent = 'Score: ' + pct + '%';
    document.getElementById('resultSection').classList.remove('active');
    document.getElementById('certSection').classList.add('active');
    window.scrollTo(0, 0);
}}

function retryQuiz() {{
    submitted = false;
    document.getElementById('resultSection').classList.remove('active');
    showQuiz();
    // Reset radio buttons
    document.querySelectorAll('input[type="radio"]').forEach(r => r.checked = false);
    document.querySelectorAll('.correct,.wrong').forEach(e => {{ e.classList.remove('correct','wrong'); }});
}}
</script>
</body>
</html>"""

    fname = f"PM_EL_{t['id']}_{t['title'].split('—')[0].strip().replace(' ','_').replace('/','_')}_eLearning_v1.0.html"
    with open(os.path.join(DELIV, fname), 'w', encoding='utf-8') as f:
        f.write(html)
    print(f"✅ {fname}")
    return fname

def gen_pptx(t):
    prs = Presentation()
    prs.slide_width = PInches(13.333)
    prs.slide_height = PInches(7.5)

    def add_slide(title_text, bullets=None, is_cover=False):
        slide = prs.slides.add_slide(prs.slide_layouts[6])  # blank
        
        # Background
        bg = slide.background.fill
        bg.solid()
        bg.fore_color.rgb = PRGBColor(0x00, 0x33, 0x66) if is_cover else PRGBColor(0xFF, 0xFF, 0xFF)
        
        # Title
        from pptx.util import Inches as I, Pt as P
        txBox = slide.shapes.add_textbox(I(0.5), I(0.5), I(12.3), I(1.2))
        tf = txBox.text_frame
        p = tf.paragraphs[0]
        p.text = title_text
        p.font.size = P(32 if is_cover else 28)
        p.font.bold = True
        p.font.color.rgb = PRGBColor(0xFF, 0xFF, 0xFF) if is_cover else PRGBColor(0x00, 0x33, 0x66)
        p.alignment = PP_ALIGN.CENTER if is_cover else PP_ALIGN.LEFT
        
        # Bullets
        if bullets:
            txBox2 = slide.shapes.add_textbox(I(0.8), I(1.8), I(11.7), I(5.0))
            tf2 = txBox2.text_frame
            tf2.word_wrap = True
            for i, b in enumerate(bullets[:6]):
                if i == 0:
                    p = tf2.paragraphs[0]
                else:
                    p = tf2.add_paragraph()
                p.text = "• " + b
                p.font.size = P(18)
                p.font.color.rgb = PRGBColor(0x1F, 0x29, 0x37)
                p.space_after = P(8)
        
        # Footer
        txBox3 = slide.shapes.add_textbox(I(0.5), I(6.8), I(12.3), I(0.5))
        tf3 = txBox3.text_frame
        p3 = tf3.paragraphs[0]
        p3.text = FOOTER_TEXT
        p3.font.size = P(10)
        p3.font.color.rgb = PRGBColor(0x99, 0x99, 0x99)
        p3.alignment = PP_ALIGN.CENTER
        
        return slide

    # Cover
    add_slide(
        f"{t['title']}\n{t['subtitle']}\n\n{t['code']} | {DATE}",
        is_cover=True
    )

    # Doel
    add_slide("Doel & Doelgroep", [
        f"Training: {t['title']}",
        f"Doelgroep: {t['target']}",
        f"Duur klassikaal: {t['duration_class']}",
        f"Duur e-learning: {t['duration_elearn']}",
        f"Wettelijk kader: {t['legal']}",
        "Phoenix Metals B.V. — PlantOne IJmuiden & Rotterdam"
    ])

    # Theory slides
    for section_title, bullets in t["theory"]:
        # Main slide
        add_slide(section_title, bullets[:6])
        # Extra slide if >6 bullets
        if len(bullets) > 6:
            add_slide(f"{section_title} (vervolg)", bullets[6:12])

    # Phoenix Metals context
    add_slide("Phoenix Metals B.V. — Context", [
        "Proeffabriek voor vanadium/staalslakkenverwerking",
        "Locaties: PlantOne IJmuiden, Rotterdam",
        "Stoffen: V₂O₅, H₂SO₄, NaOH, Al₂(SO₄)₃",
        "Vrijwillige Seveso III compliance",
        "Managing Director: Stijn van Zelst",
        "VBS Element 1: PS-230, PE-310-02, PE-310-03, FM-310-01-01"
    ])

    # Samenvatting
    summary_bullets = [f"{s[0]}" for s in t["theory"]]
    add_slide("Samenvatting", summary_bullets[:6])

    # Bronverwijzingen
    add_slide("Bronverwijzingen", t["sources"][:6])

    fname = f"PM_EL_{t['id']}_{t['title'].split('—')[0].strip().replace(' ','_').replace('/','_')}_Presentatie_v1.0.pptx"
    prs.save(os.path.join(DELIV, fname))
    print(f"✅ {fname}")
    return fname

# =============================================================================
# MAIN
# =============================================================================
if __name__ == "__main__":
    os.makedirs(DELIV, exist_ok=True)
    print("=" * 60)
    print("GENERATING E-LEARNING BATCH 1 — Phoenix Metals B.V.")
    print("=" * 60)
    
    for t in TRAININGS:
        print(f"\n--- {t['id']}: {t['title']} ---")
        gen_docx(t)
        gen_html(t)
        gen_pptx(t)
    
    print("\n" + "=" * 60)
    print("ALL 21 FILES GENERATED SUCCESSFULLY")
    print("=" * 60)
    print(f"Output: {DELIV}")
