#!/usr/bin/env python3
"""Generate Batch 3 e-learning files (T15-T21) for Phoenix Metals B.V."""

import os
from docx import Document
from docx.shared import Inches, Cm, Pt, RGBColor
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
from pptx.dml.color import RGBColor as PRGBColor
from pptx.enum.text import PP_ALIGN

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

# Read logo base64
with open(LOGO_BASE64_FILE, 'r') as f:
    LOGO_BASE64 = f.read().strip()

TRAININGS = [
    {
        "id": "T15", "slug": "Company_Induction", "title": "Company Induction (Inwerkprogramma)",
        "code": "TR-303-01", "target": "Alle nieuwe medewerkers", "duration": "~45 min",
        "legal": "Arbowet art. 3, Seveso III",
        "modules": [
            ("Welkom bij Phoenix Metals", [
                "Phoenix Metals B.V. is een proeffabriek voor vanadium- en staalslakkenverwerking [1].",
                "Locaties: PlantOne (IJmuiden) en Rotterdam.",
                "Managing Director: Stijn van Zelst.",
                "Phoenix Metals voldoet vrijwillig aan de Seveso III-richtlijn — dit betekent de hoogste veiligheidsstandaarden [2].",
                "De organisatie kent 8 functies: Managing Director, Operations Manager, HSE Manager, Maintenance Manager, HSE Supervisor, Field Operator, E&I Technician, Office Staff."
            ]),
            ("Organisatiestructuur", [
                "Het Management Team (MT) bestaat uit: Managing Director, Operations Manager, HSE Manager en Maintenance Manager.",
                "De HSE Supervisor rapporteert aan de HSE Manager en is dagelijks verantwoordelijk voor toezicht op de werkvloer.",
                "Field Operators voeren de productie- en verwerkingstaken uit.",
                "E&I Technicians zijn verantwoordelijk voor elektrotechnisch en instrumenteel onderhoud.",
                "Office Staff ondersteunt administratief en logistiek.",
                "Elke medewerker kent zijn directe leidinggevende en diens bereikbaarheid."
            ]),
            ("Veiligheidscultuur en PBZO/Seveso", [
                "Phoenix Metals hanteert een Preventiebeschermingsbeleid (PBZO) conform PS-230 [3].",
                "Vrijwillige Seveso III compliance: dit betekent strengere regels dan wettelijk vereist [4].",
                "De veiligheidscultuur is gebaseerd op: voorkomen is beter dan genezen, melden is een deugd, en samen zorgen we voor een veilige werkplek.",
                "Elke medewerker heeft het recht en de plicht om onveilige situaties te stoppen (Stop Work Authority).",
                "Arbowet artikel 3 legt de zorgplicht bij de werkgever; medewerkers moeten veiligheidsinstructies opvolgen [5]."
            ]),
            ("Belangrijkste Procedures", [
                "Melden van incidenten: direct melden bij HSE Supervisor of HSE Manager.",
                "Evacuatie: volg de aangegeven vluchtroutes, ga naar het verzamelpunt, meld je aanwezigheid.",
                "TRA (Task Risk Analysis): vooraf risicoanalyse bij elke niet-routinematige taak.",
                "PBM (Persoonlijke Beschermingsmiddelen): altijd dragen op de werkplek — helm, veiligheidsbril, werkhandschoenen, veiligheidsschoenen.",
                "PtW (Permit to Work): werkvergunning vereist voor risicovolle werkzaamheden."
            ]),
            ("Specifieke Risico's", [
                "V₂O₅ (Vanadiumpentoxide): Repr. 1B, Carc. 2 — voorkom inademing en huidcontact. Draag altijd adembescherming bij handelingen met V₂O₅ [6].",
                "H₂SO₄ (Zwavelzuur): Bijtend — ernstige brandwonden bij huid- en oogcontact. Spoelen met veel water bij contact [7].",
                "NaOH (Natriumhydroxide): Bijtend — zelfde maatregelen als H₂SO₄.",
                "Stofexplosiegevaar: bij verwerking van droge stoffen. ATEX-zones zijn aangeduid. Geen ontstekingsbronnen in deze zones [8].",
                "Al₂(SO₄)₃: irriterend bij contact — PBM's dragen."
            ]),
            ("Eerste Week — Wat wordt verwacht", [
                "Je ontvangt een mentor die je de eerste weken begeleidt.",
                "Je volgt alle verplichte instructies (BHV, BNP, Product Hazards, PBM).",
                "Je maakt kennis met de locatie, vluchtroutes en verzamelpunten.",
                "Je leest en ondertekent de HSE huisregels.",
                "Je ontvangt je PBM-set en instructie over gebruik.",
                "Na 4 weken volgt een evaluatiegesprek met je leidinggevende."
            ])
        ],
        "quiz": [
            ("Wat is Phoenix Metals B.V.?", "Een proeffabriek voor vanadium- en staalslakkenverwerking", "Een staalfabriek", "Een mijnbouwbedrijf", "Een chemische fabriek zonder Seveso", 1),
            ("Wie is de Managing Director?", "Jan de Vries", "Stijn van Zelst", "Peter Jansen", "Klaas van der Berg", 2),
            ("Hoeveel functies kent de organisatie?", "5", "6", "8", "10", 3),
            ("Wat betekent vrijwillige Seveso III compliance?", "Seveso is niet van toepassing", "Phoenix Metals voldoet vrijwillig aan de hoogste veiligheidsstandaarden", "Seveso is alleen voor grote bedrijven", "Seveso gaat alleen over brandveiligheid", 2),
            ("Wat is TRA?", "Total Risk Assessment", "Task Risk Analysis", "Technical Report Analysis", "Training Required Action", 2),
            ("Welk PBM is ALTIJD verplicht op de werkplek?", "Alleen helm", "Helm, veiligheidsbril, handschoenen, veiligheidsschoenen", "Alleen veiligheidsschoenen", "Geen PBM vereist", 2),
            ("V₂O₅ heeft welke H-zinnen?", "Brandbaar", "Repr. 1B, Carc. 2", "Ontplofbaar", "Alleen irriterend", 2),
            ("Wat doe je bij huidcontact met H₂SO₄?", "Afwassen met zeep", "Spoelen met veel water", "Afvegen met een doek", "Niets, het trekt vanzelf in", 2),
            ("Wat is Stop Work Authority?", "Het recht om onveilige situaties te stoppen", "Het recht om te pauzeren", "Een soort werkvergunning", "Het recht om eerder naar huis", 1),
            ("Wat gebeurt er na 4 weken?", "Niets", "Een evaluatiegesprek met je leidinggevende", "Je krijgt een bonus", "Je mag zelfstandig werken zonder instructie", 2),
            ("Waar bevinden zich de Phoenix Metals locaties?", "Amsterdam en Utrecht", "PlantOne IJmuiden en Rotterdam", "Rotterdam en Den Haag", "Alleen IJmuiden", 2),
            ("Wat is PtW?", "Permit to Work — werkvergunning voor risicovolle werkzaamheden", "Plan to Work — weekplanning", "Prevent to Work — afkeuring van werk", "Protect to Work — beschermingsmaatregel", 1)
        ]
    },
    {
        "id": "T16", "slug": "MT_Ownership_Responsibility", "title": "MT Ownership and Responsibility (incl. PBZO)",
        "code": "TR-303-06", "target": "MT (Managing Director, Ops Manager, HSE Manager, Maintenance Manager)", "duration": "~45 min",
        "legal": "Arbowet art. 3, Seveso III, NTA 8620",
        "modules": [
            ("Visible Leadership", [
                "Visible Leadership betekent dat MT-leden zichtbaar aanwezig zijn op de werkvloer en veiligheidsgedrag voortleven [1].",
                "'Walking the floor': minimaal wekelijks een gerichte ronde met HSE-focus.",
                "Doel: medewerkers zien dat het MT veiligheid serieus neemt — niet alleen op papier.",
                "Voorbeeld: stoppen bij onveilige situaties, complimenten geven bij goed gedrag, meedoen aan toolbox meetings.",
                "NTA 8620 benadrukt dat leiderschap de basis vormt van een veiligheidscultuur [2]."
            ]),
            ("MT Verantwoordelijkheden conform PBZO", [
                "Het PBZO (Preventiebeschermingsbeleid) PS-230 definieert de rollen en verantwoordelijkheden van het MT [3].",
                "Managing Director: eindverantwoordelijk voor het veiligheidsbeleid en de allocatie van resources.",
                "Operations Manager: verantwoordelijk voor veilige uitvoering van productieprocessen.",
                "HSE Manager: verantwoordelijk voor het HSE-managementsysteem, advies en toezicht.",
                "Maintenance Manager: verantwoordelijk voor veilig onderhoud en integriteit van installaties.",
                "Arbowet artikel 3: de werkgever heeft de zorgplicht om arbeidsomstandigheden zo veilig mogelijk te maken [4]."
            ]),
            ("Seveso III Managementeisen", [
                "Seveso III vereist een Major Accident Prevention Policy (MAPP) [5].",
                "Het MT moet een veiligheidsmanagementsysteem (VMS) implementeren en onderhouden.",
                "Management Review: minimaal jaarlijks een formele beoordeling van het VMS.",
                "KPI-analyse (MO-230-01): het MT monitort key performance indicators zoals TRIF, LTIF, near-miss ratio en openstaande acties [6].",
                "Resources en budget: het MT zorgt voor voldoende middelen om veiligheidsdoelstellingen te realiseren."
            ]),
            ("Cultuur Vormgeven en Compliance Assurance", [
                "Het MT vormt de veiligheidscultuur: gedrag bovenaan de hiërarchie wordt gekopieerd door de organisatie [7].",
                "Incidentbeheer op MT-niveau: elk serieus incident wordt besproken in het MT met focus op root cause en systemische verbetering.",
                "Compliance assurance: het MT zorgt dat regelmatige audits plaatsvinden en bevindingen worden opgevolgd.",
                "Communicatie: het MT communiceert proactief over veiligheidsresultaten en lessen geleerd.",
                "NTA 8620: continue verbetering (Plan-Do-Check-Act) is een MT-verantwoordelijkheid [8]."
            ])
        ],
        "quiz": [
            ("Wat is Visible Leadership?", "MT-leden alleen vergaderingen bijwonen", "MT-leden zichtbaar aanwezig op werkvloer en veiligheid voortleven", "MT-leden alleen emails sturen over veiligheid", "MT-leden de veiligheidskalender goedkeuren", 2),
            ("Wat betekent 'Walking the floor'?", "Naar huis lopen", "Minimaal wekelijks een gerichte ronde met HSE-focus", "Elke dag een rondje rennen", "Jaarlijks een inspectie lopen", 2),
            ("Wie is eindverantwoordelijk voor het veiligheidsbeleid?", "De HSE Manager", "De HSE Supervisor", "De Managing Director", "De Operations Manager", 3),
            ("Wat vereist Seveso III van het MT?", "Alleen een brandpreventieplan", "Een MAPP en veiligheidsmanagementsysteem", "Alleen een evacuatieplan", "Niets specifieks", 2),
            ("Hoe vaak moet Management Review plaatsvinden?", "Maandelijks", "Minimaal jaarlijks", "Elke 3 jaar", "Alleen na een incident", 2),
            ("Wat monitort KPI MO-230-01?", "Alleen financiële resultaten", "TRIF, LTIF, near-miss ratio en openstaande acties", "Alleen afwezigheid", "Productievolume", 2),
            ("Welke PDCA-cyclus is een MT-verantwoordelijkheid volgens NTA 8620?", "Plan-Do-Check-Act", "Produce-Deliver-Check-Archive", "Prevent-Detect-Correct-Analyse", "Geen van bovenstaande", 1),
            ("Wat is de zorgplicht volgens Arbowet art. 3?", "Alleen zorgen voor brandblussers", "De werkgever zorgt dat arbeidsomstandigheden zo veilig mogelijk zijn", "Medewerkers zorgen zelf voor veiligheid", "Alleen zorgen voor PBM's", 2),
            ("Hoe reageert het MT op een serieus incident?", "Het incident wordt genegeerd", "Het incident wordt besproken met focus op root cause en systemische verbetering", "Het incident wordt alleen geregistreerd", "Het MT wacht op het jaarlijkse overzicht", 2),
            ("Wat is compliance assurance?", "Zorgen dat niemand klachten heeft", "Regelmatige audits uitvoeren en bevindingen opvolgen", "Alleen wetgeving lezen", "Regels op papier zetten zonder uitvoering", 2)
        ]
    },
    {
        "id": "T17", "slug": "Management_of_Change", "title": "Management of Change (MoC)",
        "code": "TR-303-07", "target": "MT, HSE, Maintenance", "duration": "~25 min",
        "legal": "Seveso III Bijlage III, NTA 8620",
        "modules": [
            ("Wat is Management of Change?", [
                "Management of Change (MoC) is een systematische aanpak om risico's bij veranderingen te identificeren en beheersen [1].",
                "Onderzoek toont aan dat circa 80% van industriële ongevallen veroorzaakt wordt door niet-beheerste veranderingen [2].",
                "MoC is een kernvereiste van Seveso III Bijlage III en NTA 8620 [3].",
                "Elke verandering — groot of klein — kan onvoorziene risico's introduceren als deze niet systematisch wordt beoordeeld."
            ]),
            ("Soorten Veranderingen", [
                "Technische veranderingen: wijziging aan installaties, apparatuur, leidingen, instrumentatie.",
                "Organisatorische veranderingen: nieuwe procedures, veranderde verantwoordelijkheden, reorganisatie.",
                "Procedurele veranderingen: aanpassing van werkprocedures, operating limits, werkinstructies.",
                "Persoonlijke veranderingen: nieuwe medewerkers, gewijzigde ploegendiensten, tijdelijke vervangingen.",
                "Tijdelijke veranderingen: onderhoud, reparatie, proefdraaien — deze vereisen dezelfde MoC als permanente veranderingen."
            ]),
            ("Het MoC-Proces", [
                "Stap 1 — Initiatie: de verandering wordt geïdentificeerd en beschreven in een MoC-formulier.",
                "Stap 2 — Risicobeoordeling: een multidisciplinair team beoordeelt de risico's (HSE, techniek, operations).",
                "Stap 3 — Goedkeuring: het MT of een gemandateerde goedkeurt de verandering met voorwaarden.",
                "Stap 4 — Implementatie: de verandering wordt doorgevoerd met alle benodigde maatregelen.",
                "Stap 5 — Verificatie: na implementatie wordt gecontroleerd of de verandering werkt als beoogd en of er geen nieuwe risico's zijn ontstaan.",
                "Communicatie: alle betrokkenen worden geïnformeerd vóór, tijdens en na de verandering."
            ]),
            ("Phoenix Metals MoC-Triggers", [
                "Wijziging aan de V₂O₅ verwerkingsinstallatie.",
                "Nieuwe chemische stoffen introduceren in het proces.",
                "Wijziging van opslagcapaciteit of -locatie van gevaarlijke stoffen.",
                "Verandering in ploegendienst-regeling of personeelsbezetting.",
                "Aanpassing van onderhoudsprocedures voor kritieke apparatuur.",
                "Tijdelijke installaties of proefopstellingen.",
                "Documentatie: alle MoC's worden geregistreerd en zijn traceerbaar."
            ])
        ],
        "quiz": [
            ("Wat percentage ongevallen wordt veroorzaakt door niet-beheerste veranderingen?", "50%", "Circa 80%", "20%", "95%", 2),
            ("Welke soorten veranderingen zijn er?", "Alleen technische", "Technisch, organisatorisch, procedureel, personeel", "Alleen procedurele", "Alleen personele", 2),
            ("Wat is de eerste stap in het MoC-proces?", "Goedkeuring", "Implementatie", "Initiatie — verandering identificeren en beschrijven", "Verificatie", 3),
            ("Wie beoordeelt de risico's bij een MoC?", "Alleen de HSE Manager", "Een multidisciplinair team", "Alleen de Managing Director", "De ontdekker van de verandering", 2),
            ("Hebben tijdelijke veranderingen ook een MoC nodig?", "Nee, alleen permanente", "Ja, dezelfde MoC als permanente veranderingen", "Alleen als ze langer dan een week duren", "Alleen als het MT het vraagt", 2),
            ("Wat is stap 5 van het MoC-proces?", "Initiatie", "Risicobeoordeling", "Goedkeuring", "Verificatie — controleren of de verandering werkt als beoogd", 4),
            ("Welke wetgeving vereist MoC?", "Alleen de Arbowet", "Seveso III Bijlage III en NTA 8620", "Alleen NTA 8620", "Geen wetgeving vereist MoC", 2),
            ("Wat is een MoC-trigger bij Phoenix Metals?", "Alleen een brand", "Wijziging aan de V₂O₅ verwerkingsinstallatie", "Alleen nieuwe medewerkers", "Een vakantie", 2),
            ("Waarom is communicatie belangrijk bij MoC?", "Dat is niet belangrijk", "Alle betrokkenen moeten geïnformeerd worden vóór, tijdens en na de verandering", "Alleen na de verandering", "Alleen aan het MT", 2),
            ("Wat gebeurt er met MoC-documentatie?", "Wordt weggegooid na implementatie", "Wordt geregistreerd en is traceerbaar", "Wordt alleen in email bewaard", "Alleen het HSE-team bewaart het", 2)
        ]
    },
    {
        "id": "T18", "slug": "Besloten_Ruimten", "title": "Besloten Ruimten",
        "code": "TR-303-08", "target": "Operatoren, technici, HSE", "duration": "~20 min",
        "legal": "Arbowet, Arbobesluit art. 3.5a",
        "modules": [
            ("Definitie en Risico's", [
                "Een besloten ruimte is een ruimte die (deels) afgesloten is, niet ontworpen voor continue bewoning, en waar bij betreding specifieke risico's optreden [1].",
                "Risico's: zuurstoftekort (O₂ < 19,5%), giftige gassen (H₂S, CO, V₂O₅ stof), brandbare atmosfeer (LEL > 0%), mechanische gevaren (roterende delen, vloeistofstromen) [2].",
                "Arbobesluit artikel 3.5a vereist specifieke maatregelen voor besloten ruimten [3].",
                "Ongevallen in besloten ruimten hebben vaak dodelijke afloop — zowel voor het slachtoffer als voor reddingswerkers."
            ]),
            ("Besloten Ruimten bij Phoenix Metals", [
                "Silos: opslag van droge stoffen, risico op stikken door zuurstofverdringing en stofexplosie.",
                "Tanks: opslag van vloeistoffen (H₂SO₄, NaOH), risico op gifige dampen en verdrinking.",
                "Reactoren: chemische reactieruimtes, risico op residuale gassen en hoge temperaturen.",
                "Putten en kelders: risico op ophoping van zware gassen (H₂S) en zuurstoftekort.",
                "Leidingbruggen (besloten secties): risico op lekkage van gevaarlijke stoffen uit aangrenzende leidingen."
            ]),
            ("Procedure en Vergunning", [
                "Voor betreding van een besloten ruimte is altijd een werkvergunning (PtW) vereist [4].",
                "Gasmeting vóór betreding: O₂ (19,5–23,5%), LEL (< 1%), H₂S (< 5 ppm), CO (< 20 ppm).",
                "LOTO (Lock Out / Tag Out): alle energiebronnen vergrendeld en gemarkeerd.",
                "Bewaker: een toegewezen bewaker buiten de ruimte die voortdurend contact houdt.",
                "Reddingsplan: vooraf vastgesteld hoe een redding wordt uitgevoerd zonder de redder in gevaar te brengen.",
                "Adembescherming: indien gasmeting niet voldoet — altijd onafhankelijke adembescherming."
            ]),
            ("PBM's en Praktijk", [
                "Gasdetector (4-gas: O₂, LEL, H₂S, CO) — continue monitoring tijdens betreding.",
                "Adembescherming: zelfstandig werkend ademtoestel bij onvoldoende zuurstof of giftige gassen.",
                "Valbeveiliging: bij betreding via openingen op hoogte.",
                "Communicatiemiddel: radio of treklijn voor contact met bewaker.",
                "Nooit alleen een besloten ruimte betreden — altijd minimaal met twee personen (binnen + bewaker buiten)."
            ])
        ],
        "quiz": [
            ("Wat is een besloten ruimte?", "Elke kamer met een deur", "Een ruimte die (deels) afgesloten is, niet voor continue bewoning, met specifieke risico's bij betreding", "Een kantoorruimte", "Een magazijn", 2),
            ("Welk zuurstofpercentage is veilig?", "15–18%", "19,5–23,5%", "25–30%", "Elk percentage boven 10%", 2),
            ("Welke gassen worden gemeten bij een besloten ruimte?", "Alleen O₂", "O₂, LEL, H₂S, CO", "Alleen CO₂", "Alleen H₂S", 2),
            ("Wat is LOTO?", "Lock Out / Tag Out — energiebronnen vergrendelen en markeren", "Leave Out Turn Off", "Light On Turn Out", "Een type gasdetector", 1),
            ("Mag je alleen een besloten ruimte betreden?", "Ja, als je ervaren bent", "Nee, altijd minimaal met twee personen (binnen + bewaker buiten)", "Ja, met toestemming van de HSE Manager", "Ja, als je een gasdetector hebt", 2),
            ("Wat is de maximale LEL-waarde voor veilige betreding?", "< 10%", "< 1%", "< 5%", "< 50%", 2),
            ("Welk artikel van het Arbobesluit gaat over besloten ruimten?", "Art. 3.5a", "Art. 1.1", "Art. 8.2", "Art. 5.3", 1),
            ("Wat doet de bewaker bij een besloten ruimte?", "Hij gaat naar koffie", "Hij houdt voortdurend contact met de persoon binnen en coördineert bij nood", "Hij meet af en toe de gassen", "Hij vult de vergunning in na afloop", 2)
        ]
    },
    {
        "id": "T19", "slug": "Werken_op_Hoogte", "title": "Werken op Hoogte",
        "code": "TR-303-09", "target": "Operatoren, technici, HSE", "duration": "~30 min",
        "legal": "Arbowet, Arbobesluit art. 3.16",
        "modules": [
            ("Definitie en Risico's", [
                "Werken op hoogte = alle werkzaamheden op een hoogte van 2,5 meter of meer waarbij valgevaar bestaat [1].",
                "Risico's: vallen (letsel, overlijden), vallende voorwerpen, weersomstandigheden, instorting van werkmiddelen.",
                "Arbobesluit artikel 3.16 verplicht het nemen van maatregelen bij werkzaamheden op hoogte [2].",
                "Val van 2,5 meter op een harde ondergrond kan fataal zijn — zelfs lager kan ernstig letsel veroorzaken."
            ]),
            ("Hiërarchie van Maatregelen", [
                "De hiërarchie: collectieve maatregelen > individuele maatregelen [3].",
                "Niveau 1: voorkomen van werken op hoogte (werk op begane grond uitvoeren).",
                "Niveau 2: collectieve valbeveiliging (steigers, randbeveiliging, liften).",
                "Niveau 3: individuele valbeveiliging (harnas, lijn, ankerpunt).",
                "Niveau 4: persoonsgebonden middelen alleen als collectieve maatregelen niet mogelijk zijn."
            ]),
            ("Valbeveiliging — EN-Normen", [
                "Harnas: EN 361 — full body harness met D-ring op de rug [4].",
                "Energy absorber: EN 355 — schokdemper die de valkracht reduceert.",
                "Ankerpunt: EN 795 — minimaal 10 kN (1000 kg) belastbaar.",
                "Lifeline/lijn: EN 354/EN 355 — verbinding tussen harnas en ankerpunt.",
                "Inspectie: valbeveiliging jaarlijks geïnspecteerd door een bevoegde persoon. Vóór elk gebruik visuele controle."
            ]),
            ("Werkmiddelen", [
                "Ladders: alleen voor kortdurend, licht werk (max 15 min). Steigermateriaal verdient de voorkeur.",
                "Trappen: vaste trappen met leuning zijn veiliger dan ladders.",
                "Steigers: alleen opgebouwd door gecertificeerde steigerbouwers. Steigerkaart verplicht.",
                "Lifters (MEWP): alleen bediend door gecertificeerde personen. Horizontale uitschuiving > verticale.",
                "Phoenix Metals locaties met hoogtewerk: silo's, leidingbruggen, daken."
            ]),
            ("Procedure: TRA + PtW", [
                "Vooraf altijd een Task Risk Analysis (TRA) uitvoeren voor werkzaamheden op hoogte.",
                "Een Permit to Work (PtW) is verplicht voor alle hoogtewerkzaamheden.",
                "Controleer weersomstandigheden: bij windkracht ≥ 6 geen hoogtewerk met lifter.",
                "Zorg voor afzetting onder het werkgebied — vallende voorwerpen gevaar.",
                "Nooit alleen werken op hoogte — altijd een tweede persoon in de buurt.",
                "Bij nood: bel 112, meld locatie en aard van het incident."
            ])
        ],
        "quiz": [
            ("Vanaf welke hoogte spreken we van 'werken op hoogte'?", "1 meter", "2,5 meter", "5 meter", "10 meter", 2),
            ("Wat is de hiërarchie van maatregelen?", "Individueel > collectief", "Collectieve maatregelen > individuele maatregelen", "Goedkoopste oplossing eerst", "Alleen valharnas", 2),
            ("Welke EN-norm geldt voor een valharnas?", "EN 355", "EN 361", "EN 795", "EN 354", 2),
            ("Wat is het minimale belastingsniveau voor een ankerpunt?", "5 kN (500 kg)", "10 kN (1000 kg)", "2 kN (200 kg)", "20 kN (2000 kg)", 2),
            ("Hoe vaak moet valbeveiliging geïnspecteerd worden?", "Elke dag", "Jaarlijks door een bevoegde persoon + visuele controle vóór elk gebruik", "Elke 5 jaar", "Alleen na een val", 2),
            ("Mag je een ladder gebruiken voor langdurig werk?", "Ja, altijd", "Nee, alleen voor kortdurend licht werk (max 15 min)", "Ja, als je een harnas draagt", "Ja, als de ladder hoog genoeg is", 2),
            ("Welke procedure is VERPLICHT voor hoogtewerkzaamheden?", "Alleen een planbriefing", "TRA + PtW", "Alleen een werkvergunning", "Niets, als je ervaren bent", 2),
            ("Bij welke windkracht geen hoogtewerk met een lifter?", "Windkracht 4", "Windkracht ≥ 6", "Windkracht 8", "Windkracht 3", 2),
            ("Welke Phoenix Metals locaties vereisen hoogtewerk?", "Alleen het kantoor", "Silo's, leidingbruggen, daken", "Alleen het magazijn", "Geen locaties", 2),
            ("Mag je alleen werken op hoogte?", "Ja, altijd", "Nee, altijd een tweede persoon in de buurt", "Ja, met walkie-talkie", "Ja, als je gecertificeerd bent", 2)
        ]
    },
    {
        "id": "T20", "slug": "Incident_Response_Investigation", "title": "Incident Response & Investigation",
        "code": "TR-303-10", "target": "Alle medewerkers behalve Office Staff", "duration": "~20 min",
        "legal": "Arbowet art. 7, Brzo art. 17",
        "modules": [
            ("7-Stappen Meldingsprocedure", [
                "Stap 1: Beheers de directe situatie (eerst jezelf veiligstellen) [1].",
                "Stap 2: Alarmeer — bel 112 bij spoed, anders HSE Supervisor/HSE Manager.",
                "Stap 3: Meld het incident — binnen 1 uur via het meldsysteem of telefonisch.",
                "Stap 4: Zorg voor eerste hulp / BHV inzet.",
                "Stap 5: Bewaar bewijsmateriaal — foto's, getuigenverklaringen, monstermateriaal.",
                "Stap 6: Informeer leidinggevende — escalatie volgens escalatiematrix.",
                "Stap 7: Draag bij aan onderzoek — eerlijk en volledig meewerken."
            ]),
            ("Escalatiematrix en ICAM", [
                "Kleine incidenten (near-miss, kleine schade): HSE Supervisor behandelt.",
                "Middelgrote incidenten (eerste hulp, beperkte schade): HSE Manager + Operations Manager.",
                "Ernstige incidenten (ziekenhuisopname, milieu-impact): volledig MT + externe autoriteiten.",
                "ICAM-methode (Incident Cause Analysis Method): systematische analyse van bijdragende factoren [2].",
                "ICAM onderscheidt: actieve fouten, latente condities, organisatorische factoren."
            ]),
            ("5-Waarom en Root Cause", [
                "De 5-Waarom techniek: vraag 5 keer 'waarom' om door te dringen tot de werkelijke oorzaak [3].",
                "Root cause = de diepstliggende oorzaak die, indien aangepakt, herhaling voorkomt.",
                "Symptoom = wat je ziet (bijv. brand). Root cause = waarom het kon gebeuren (bijv. geen MoC uitgevoerd).",
                "Voorbeeld: V₂O₅ gemorst → waarom? zak gescheurd → waarom? slechte opslag → waarom? geen inspectie → waarom? geen procedure → waarom? geen MoC bij nieuwe leverancier."
            ]),
            ("Correctieve Acties en Lessen Delen", [
                "Correctieve acties worden SMART geformuleerd: Specifiek, Meetbaar, Acceptabel, Realistisch, Tijdgebonden [4].",
                "Elke correctieve actie krijgt een eigenaar, deadline en verantwoordelijke.",
                "Lessen delen via toolbox meetings: de belangrijkste les wordt besproken met alle betrokken teams.",
                "KPI-koppeling: TRIF (Total Recordable Incident Frequency) en LTIF (Lost Time Injury Frequency) monitoren de effectiviteit [5].",
                "Arbowet artikel 7 en Brzo artikel 17 verplichten registratie en melding van incidenten [6]."
            ])
        ],
        "quiz": [
            ("Wat is de eerste stap bij een incident?", "Foto's maken", "Jezelf veiligstellen en de directe situatie beheersen", "Bellen met het MT", "Doorwerken", 2),
            ("Binnen welke termijn moet een incident gemeld worden?", "Binnen 1 uur", "Binnen 24 uur", "Binnen 1 week", "Alleen als iemand gewond is", 1),
            ("Wat betekent ICAM?", "International Compliance Audit Method", "Incident Cause Analysis Method", "Incident Control And Management", "Integrated Cause Analysis Model", 2),
            ("Wat is een root cause?", "Het Symptoom", "De diepstliggende oorzaak die herhaling voorkomt indien aangepakt", "De directe oorzaak", "De persoon die de fout maakte", 2),
            ("Wat betekent SMART?", "Snel, Meetbaar, Actueel, Relevant, Toegankelijk", "Specifiek, Meetbaar, Acceptabel, Realistisch, Tijdgebonden", "Systematisch, Methodisch, Accuraat, Relevant, Toetsbaar", "Geen van bovenstaande", 2),
            ("Wat is TRIF?", "Total Risk Inspection Form", "Total Recordable Incident Frequency", "Training Required Incident Follow-up", "Technical Risk Investigation Form", 2),
            ("Hoe wordt een les gedeeld na een incident?", "Via email naar het MT", "Via toolbox meetings met alle betrokken teams", "Via een memo op het prikbord", "Lessen worden niet gedeeld", 2),
            ("Welke wetgeving verplicht incidentregistratie?", "Alleen de Arbowet", "Arbowet art. 7 en Brzo art. 17", "Alleen Seveso III", "Geen wetgeving vereist dit", 2)
        ]
    },
    {
        "id": "T21", "slug": "Fysieke_Belasting_Tillen", "title": "Fysieke Belasting / Tillen",
        "code": "TR-303-11", "target": "Alle medewerkers", "duration": "~15 min",
        "legal": "Arbowet art. 3, Arbobesluit hoofdstuk 5",
        "modules": [
            ("Fysieke Belasting — Soorten en Risico's", [
                "Fysieke belasting omvat: tillen, dragen, duwen, trekken, buigen, draaien en langdurig staan [1].",
                "Risico's: rugklachten, RSI (Repetitive Strain Injury), hernia, gewrichtsklachten, chronische pijn.",
                "Rugklachten zijn de belangrijkste oorzaak van langdurig verzuim in de industrie [2].",
                "Arbowet artikel 3 en Arbobesluit hoofdstuk 5 vereisen dat werkgevers maatregelen nemen om fysieke belasting te beperken [3]."
            ]),
            ("Maximale Gewichten en Tilltechniek", [
                "Maximaal tilgewicht: 25 kg voor mannen, 12,5 kg voor vrouwen (onder optimale omstandigheden) [4].",
                "Deze limieten zijn indicatief — bij frequent tillen, draaien of ongunstige houding zijn ze lager.",
                "Tilltechniek: been- en rugspieren gebruiken, rug recht houden, gewicht dicht bij het lichaam.",
                "Nooit tillen met gebogen rug en gestrekte benen.",
                "Draaien met de voeten, niet met de rug."
            ]),
            ("Hulpmiddelen en Ergonomie", [
                "Mechanische hulpmiddelen: heftruck, takel, kar, rolcontainer — altijd gebruiken bij zware lasten.",
                "Werkplekergonomie: werkhoogte aanpassen, anti-vermoeidheidsmatten, zit-sta werkplekken.",
                "Phoenix Metals specifieke taken: zakken V₂O₅ wegen 25 kg — altijd met twee personen of mechanisch tillen.",
                "Monsternames: gebruik de juiste gereedschappen en werkhoogte om buigen te voorkomen.",
                "Pauzes en afwisseling: regelmatig van houding wisselen om overbelasting te voorkomen."
            ])
        ],
        "quiz": [
            ("Wat is het maximale tilgewicht voor mannen (onder optimale omstandigheden)?", "15 kg", "20 kg", "25 kg", "50 kg", 3),
            ("Wat is het maximale tilgewicht voor vrouwen?", "10 kg", "12,5 kg", "20 kg", "25 kg", 2),
            ("Wat is de juiste tilltechniek?", "Rug gebogen, benen gestrekt", "Been- en rugspieren gebruiken, rug recht, gewicht dicht bij lichaam", "Met één hand tillen voor balans", "Snel tillen om de belasting te minimaliseren", 2),
            ("Wat wegen de V₂O₅ zakken bij Phoenix Metals?", "10 kg", "15 kg", "25 kg", "50 kg", 3),
            ("Hoe voorkom je rugklachten bij frequent tillen?", "Sneller tillen", "Mechanische hulpmiddelen gebruiken en taken afwisselen", "Minder pauzes nemen", "Alleen met gebogen rug tillen", 2),
            ("Welk Arbobesluit-hoofdstuk gaat over fysieke belasting?", "Hoofdstuk 1", "Hoofdstuk 3", "Hoofdstuk 5", "Hoofdstuk 8", 3),
            ("Wat is RSI?", "Rug en Schouder Infectie", "Repetitive Strain Injury — overbelasting door herhaalde bewegingen", "Risk Safety Inspection", "Geen relevante term", 2),
            ("Hoe hoog moet een werkblad zijn?", "Zo laag mogelijk", "Op navelhoogte om buigen te voorkomen", "Altijd 1 meter", "Dat maakt niet uit", 2)
        ]
    }
]


def create_docx(t):
    doc = Document()
    
    # Default font
    style = doc.styles['Normal']
    font = style.font
    font.name = 'Calibri'
    font.size = Pt(11)
    font.color.rgb = RGBColor(0x1F, 0x29, 0x37)
    
    # Cover page
    if os.path.exists(LOGO_PATH):
        doc.add_picture(LOGO_PATH, width=Cm(4.0), height=Cm(3.6))
    last_paragraph = doc.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT
    
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    run = p.add_run(f"\n{t['title']}")
    run.font.size = Pt(24)
    run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
    run.bold = True
    
    info_lines = [
        f"Documentcode: {t['code']}",
        f"Versie: 1.0",
        f"Datum: 28 april 2026",
        f"Auteur: JvG Consultancy",
        f"Status: Definitief",
        f"Doelgroep: {t['target']}",
        f"Duur: {t['duration']}",
        f"Wettelijk kader: {t['legal']}"
    ]
    for line in info_lines:
        p = doc.add_paragraph(line)
        p.style.font.size = Pt(11)
    
    doc.add_page_break()
    
    # Sections
    sections = [
        ("1. Doel", f"Deze training biedt inzicht in {t['title'].lower()} binnen Phoenix Metals B.V. Na afronding kent de medewerker de relevante risico's, procedures en wetgeving."),
        ("2. Doelgroep", t['target']),
        ("3. Wettelijk Kader", t['legal']),
    ]
    
    for title, content in sections:
        h = doc.add_heading(title, level=1)
        for run in h.runs:
            run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
        doc.add_paragraph(content)
    
    # Section 4: Theory
    h = doc.add_heading("4. Theorie", level=1)
    for run in h.runs:
        run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
    
    for i, (mod_title, items) in enumerate(t['modules'], 1):
        h2 = doc.add_heading(f"4.{i} {mod_title}", level=2)
        for run in h2.runs:
            run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
        for item in items:
            doc.add_paragraph(item, style='List Bullet')
    
    # Section 5: Phoenix Metals specifiek
    h = doc.add_heading("5. Phoenix Metals Specifiek", level=1)
    for run in h.runs:
        run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
    doc.add_paragraph("Phoenix Metals B.V. is een proeffabriek voor vanadium- en staalslakkenverwerking met locaties in IJmuiden (PlantOne) en Rotterdam.")
    doc.add_paragraph("Relevante stoffen: V₂O₅ (Repr. 1B, Carc. 2), H₂SO₄ (bijtend), NaOH (bijtend), Al₂(SO₄)₃.")
    doc.add_paragraph("De organisatie voldoet vrijwillig aan Seveso III en hanteert een PBZO conform PS-230.")
    doc.add_paragraph("Managing Director: Stijn van Zelst.")
    
    # Section 6: Oefeningen/Werkblad
    h = doc.add_heading("6. Oefeningen / Werkblad", level=1)
    for run in h.runs:
        run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
    
    doc.add_paragraph("Oefening 1: Noteer de 3 belangrijkste leerpunten uit deze training.")
    for _ in range(3):
        doc.add_paragraph("___________________________________________________________________________")
    
    doc.add_paragraph("\nOefening 2: Welke procedures zijn direct van toepassing op jouw functie?")
    for _ in range(3):
        doc.add_paragraph("___________________________________________________________________________")
    
    doc.add_paragraph("\nOefening 3: Checklist — Ik heb begrepen:")
    checklist_items = [
        "De relevante risico's op mijn werkplek",
        "De procedures die ik moet volgen",
        "Mijn rol bij incidentmelding",
        "De PBM's die ik moet dragen",
        "Mijn directe leidinggevende en diens bereikbaarheid"
    ]
    for item in checklist_items:
        p = doc.add_paragraph(f"☐  {item}")
    
    # Section 7: Samenvatting
    h = doc.add_heading("7. Samenvatting", level=1)
    for run in h.runs:
        run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
    doc.add_paragraph(f"Deze training behandelde {t['title'].lower()}. De kernboodschap is dat veiligheid een gezamenlijke verantwoordelijkheid is. Elke medewerker kent de risico's, procedures en zijn eigen rol daarin.")
    
    # Section 8: Bronverwijzingen
    h = doc.add_heading("8. Bronverwijzingen", level=1)
    for run in h.runs:
        run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
    
    refs = [
        "[1] Arbowet (Wet van 18 maart 1999, Stb. 1999, 189)",
        "[2] Arbobesluit (Besluit van 15 januari 1997, Stb. 1997, 60)",
        "[3] Seveso III-richtlijn (Richtlijn 2012/18/EU)",
        "[4] NTA 8620: Managementsystemen voor veiligheid (NEN)",
        "[5] Phoenix Metals B.V. PBZO PS-230",
        "[6] SDS Veiligheidsinformatiebladen V₂O₅, H₂SO₄, NaOH",
        "[7] EN-normen valbeveiliging (EN 361, EN 355, EN 795)",
        "[8] ICAM Incident Investigation Method (BHP Billiton)"
    ]
    for ref in refs:
        doc.add_paragraph(ref, style='List Bullet')
    
    # Footer info
    doc.add_paragraph("\n---")
    p = doc.add_paragraph("JvG Consultancy | Safety • Governance • Advisory | © 2026")
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    # TierVerify
    doc.add_paragraph("\n--- TierVerify Log ---")
    doc.add_paragraph("Document: " + t['code'])
    doc.add_paragraph("Versie: 1.0")
    doc.add_paragraph("Controle: inhoudelijk, feitelijk, bronverwijzingen — OK")
    doc.add_paragraph("Status: Definitief")
    
    path = os.path.join(DELIVERABLES, f"PM_EL_{t['id']}_{t['slug']}_Handleiding_v1.0.docx")
    doc.save(path)
    print(f"Created: {path}")
    return path


def create_html(t):
    quiz_js_items = []
    for i, (q, a, b, c, d, correct) in enumerate(t['quiz']):
        quiz_js_items.append(f'{{q:"{q.replace(chr(34), "")}",opts:["{a.replace(chr(34), "")}","{b.replace(chr(34), "")}","{c.replace(chr(34), "")}","{d.replace(chr(34), "")}"],correct:{correct-1}}}')
    
    modules_html = ""
    for i, (mod_title, items) in enumerate(t['modules']):
        lis = "".join(f"<li>{item}</li>" for item in items)
        modules_html += f'''
        <div class="module" id="module{i+1}">
            <h2>Module {i+1}: {mod_title}</h2>
            <ul>{lis}</ul>
            <button onclick="nextModule({i+1})" class="btn">Volgende →</button>
        </div>'''
    
    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_BASE64}" alt="JvG Consultancy">
    <h1>{t['title']}</h1>
    <p>{t['code']} | {t['target']} | {t['duration']}</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['modules'])} modules gevolgd door een quiz. Je hebt minimaal 80% 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="quiz">
    <h2>Quiz — {len(t['quiz'])} Vragen</h2>
    <p>Beantwoord alle vragen. Je hebt minimaal 80% goed nodig voor een certificaat.</p>
    <div id="quizQuestions"></div>
    <br>
    <button onclick="checkQuiz()" class="btn">Controleer Antwoorden →</button>
</div>

<div class="result-section" id="result">
    <h2>Resultaat</h2>
    <div class="score" id="scoreDisplay"></div>
    <p id="resultText" style="text-align:center;font-size:1.1em;"></p>
    <br>
    <div id="certInput" style="text-align:center;display:none;">
        <p>Vul je naam in voor het certificaat:</p>
        <input type="text" id="certName" placeholder="Je volledige naam">
        <br><br>
        <button onclick="showCert()" class="btn">Genereer Certificaat →</button>
    </div>
    <div id="retryBtn" style="text-align:center;display:none;">
        <button onclick="retryQuiz()" class="btn">Probeer Opnieuw</button>
    </div>
</div>

<div class="cert-section" id="cert">
    <div class="cert-box">
        <img src="{LOGO_BASE64}" alt="JvG Consultancy" style="max-height:50px;margin-bottom:15px;">
        <h2>Certificaat van Voltooiing</h2>
        <p>Dit certificeert dat</p>
        <div class="name" id="certNameDisplay"></div>
        <p>de e-learning <strong>{t['title']}</strong> ({t['code']}) succesvol heeft afgerond.</p>
        <p id="certScore"></p>
        <p id="certDate"></p>
        <br>
        <p style="font-size:0.85em;opacity:0.7;">JvG Consultancy | Safety • Governance • Advisory | © 2026</p>
    </div>
    <br>
    <button onclick="window.print()" class="btn">Print Certificaat</button>
</div>

<footer style="text-align:center;padding:20px;color:#666;font-size:0.85em;border-top:1px solid #E5E7EB;margin-top:30px;">
    JvG Consultancy | Safety • Governance • Advisory | © 2026
</footer>
</div>

<script>
const totalSteps = {len(t['modules'])} + 2;
let currentStep = 0;
const quizData = [{','.join(quiz_js_items)}];

function updateProgress() {{
    const pct = ((currentStep + 1) / totalSteps) * 100;
    document.getElementById('progressBar').style.width = pct + '%';
}}

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

function nextModule(n) {{
    document.getElementById('module' + n).classList.remove('active');
    const next = n + 1;
    if (document.getElementById('module' + next)) {{
        document.getElementById('module' + next).classList.add('active');
        currentStep = next;
    }} else {{
        document.getElementById('quiz').classList.add('active');
        currentStep = {len(t['modules'])};
        buildQuiz();
    }}
    updateProgress();
    window.scrollTo(0, 0);
}}

function buildQuiz() {{
    let html = '';
    quizData.forEach((item, i) => {{
        html += '<div class="quiz-q" id="qq' + i + '"><h3>Vraag ' + (i+1) + ': ' + item.q + '</h3>';
        item.opts.forEach((opt, j) => {{
            html += '<label id="lbl' + i + '_' + j + '"><input type="radio" name="q' + i + '" value="' + j + '"> ' + opt + '</label>';
        }});
        html += '</div>';
    }});
    document.getElementById('quizQuestions').innerHTML = html;
}}

function checkQuiz() {{
    let correct = 0;
    quizData.forEach((item, i) => {{
        const sel = document.querySelector('input[name="q' + i + '"]:checked');
        const chosen = sel ? parseInt(sel.value) : -1;
        if (chosen === item.correct) {{
            correct++;
            document.getElementById('lbl' + i + '_' + chosen).classList.add('correct');
        }} else {{
            if (chosen >= 0) document.getElementById('lbl' + i + '_' + chosen).classList.add('wrong');
            document.getElementById('lbl' + i + '_' + item.correct).classList.add('correct');
        }}
    }});
    const pct = Math.round((correct / quizData.length) * 100);
    document.getElementById('quiz').classList.remove('active');
    document.getElementById('result').classList.add('active');
    const scoreEl = document.getElementById('scoreDisplay');
    scoreEl.textContent = pct + '%';
    scoreEl.className = 'score ' + (pct >= 80 ? 'pass' : 'fail');
    document.getElementById('resultText').textContent = pct >= 80 
        ? 'Gefeliciteerd! Je hebt de quiz succesvol afgerond (' + correct + '/' + quizData.length + ' goed).'
        : 'Helaas, je hebt minder dan 80% goed (' + correct + '/' + quizData.length + '). Probeer het opnieuw.';
    document.getElementById('certInput').style.display = pct >= 80 ? 'block' : 'none';
    document.getElementById('retryBtn').style.display = pct >= 80 ? 'none' : 'block';
    currentStep = totalSteps - 1;
    updateProgress();
    window.scrollTo(0, 0);
}}

function showCert() {{
    const name = document.getElementById('certName').value.trim();
    if (!name) {{ alert('Vul je naam in.'); return; }}
    document.getElementById('certNameDisplay').textContent = name;
    document.getElementById('certScore').textContent = 'Score: ' + document.getElementById('scoreDisplay').textContent;
    document.getElementById('certDate').textContent = 'Datum: ' + new Date().toLocaleDateString('nl-NL');
    document.getElementById('result').classList.remove('active');
    document.getElementById('cert').classList.add('active');
    currentStep = totalSteps;
    updateProgress();
    window.scrollTo(0, 0);
}}

function retryQuiz() {{
    document.getElementById('result').classList.remove('active');
    document.getElementById('quiz').classList.add('active');
    buildQuiz();
    window.scrollTo(0, 0);
}}
</script>
</body>
</html>'''
    
    path = os.path.join(DELIVERABLES, f"PM_EL_{t['id']}_{t['slug']}_eLearning_v1.0.html")
    with open(path, 'w', encoding='utf-8') as f:
        f.write(html)
    print(f"Created: {path}")
    return path


def create_pptx(t):
    prs = Presentation()
    prs.slide_width = PInches(13.333)
    prs.slide_height = PInches(7.5)
    
    PRIMARY = PRGBColor(0x00, 0x33, 0x66)
    WHITE = PRGBColor(0xFF, 0xFF, 0xFF)
    DARK = PRGBColor(0x1F, 0x29, 0x37)
    
    def add_slide(title_text, bullets=None, is_cover=False):
        slide = prs.slides.add_slide(prs.slide_layouts[6])  # blank
        
        # Background bar
        from pptx.util import Emu
        shape = slide.shapes.add_shape(
            1, PInches(0), PInches(0), prs.slide_width, PInches(1.5)
        )
        shape.fill.solid()
        shape.fill.fore_color.rgb = PRIMARY
        shape.line.fill.background()
        
        # Logo
        if os.path.exists(LOGO_PATH):
            slide.shapes.add_picture(LOGO_PATH, PInches(0.4), PInches(0.15), PInches(1.5), PInches(1.35))
        
        # Title
        txBox = slide.shapes.add_textbox(PInches(2.2), PInches(0.3), PInches(10), PInches(1.0))
        tf = txBox.text_frame
        tf.word_wrap = True
        p = tf.paragraphs[0]
        p.text = title_text
        p.font.size = PPt(28)
        p.font.color.rgb = WHITE
        p.font.bold = True
        
        if bullets:
            txBox2 = slide.shapes.add_textbox(PInches(0.8), PInches(1.8), PInches(11.5), PInches(5.2))
            tf2 = txBox2.text_frame
            tf2.word_wrap = True
            for bullet in bullets:
                p = tf2.add_paragraph()
                p.text = bullet
                p.font.size = PPt(18)
                p.font.color.rgb = DARK
                p.space_after = PPt(8)
        
        if is_cover:
            info_items = [t['code'], f"Versie 1.0 | 28 april 2026", f"Doelgroep: {t['target']}", f"Duur: {t['duration']}", f"Wettelijk: {t['legal']}"]
            txBox3 = slide.shapes.add_textbox(PInches(0.8), PInches(4.5), PInches(11.5), PInches(2.5))
            tf3 = txBox3.text_frame
            tf3.word_wrap = True
            for item in info_items:
                p = tf3.add_paragraph()
                p.text = item
                p.font.size = PPt(16)
                p.font.color.rgb = DARK
        
        # Footer
        txBox4 = slide.shapes.add_textbox(PInches(0.5), PInches(7.0), PInches(12), PInches(0.4))
        tf4 = txBox4.text_frame
        p = tf4.paragraphs[0]
        p.text = "JvG Consultancy | Safety • Governance • Advisory | © 2026"
        p.font.size = PPt(10)
        p.font.color.rgb = PRGBColor(0x99, 0x99, 0x99)
        p.alignment = PP_ALIGN.CENTER
        
        return slide
    
    # Cover slide
    add_slide(t['title'], is_cover=True)
    
    # Agenda
    add_slide("Agenda", [f"{i+1}. {mod[0]}" for i, mod in enumerate(t['modules'])] + [f"{len(t['modules'])+1}. Quiz ({len(t['quiz'])} vragen)"])
    
    # Module slides
    for i, (mod_title, items) in enumerate(t['modules']):
        # Split into chunks of 5 for readability
        chunk_size = 5
        chunks = [items[j:j+chunk_size] for j in range(0, len(items), chunk_size)]
        for ci, chunk in enumerate(chunks):
            suffix = f" ({ci+1}/{len(chunks)})" if len(chunks) > 1 else ""
            add_slide(f"Module {i+1}: {mod_title}{suffix}", chunk)
    
    # Quiz slide
    add_slide("Quiz", [f"Aantal vragen: {len(t['quiz'])}", "Cesuur: 80% correct", "Multiple choice", "Direct feedback per vraag", "Certificaat bij voldoende resultaat"])
    
    # Summary
    add_slide("Samenvatting", [
        f"Training: {t['title']}",
        f"Wettelijk kader: {t['legal']}",
        "Veiligheid is een gezamenlijke verantwoordelijkheid",
        "Ken de risico's, procedures en je eigen rol",
        "Meld altijd onveilige situaties",
        "Vragen? Neem contact op met de HSE Manager"
    ])
    
    # Sources
    add_slide("Bronverwijzingen", [
        "[1] Arbowet (Stb. 1999, 189)",
        "[2] Arbobesluit (Stb. 1997, 60)",
        "[3] Seveso III-richtlijn (2012/18/EU)",
        "[4] NTA 8620 (NEN)",
        "[5] Phoenix Metals B.V. PBZO PS-230",
        "[6] SDS V₂O₅, H₂SO₄, NaOH",
        "[7] EN-normen valbeveiliging",
        "[8] ICAM Incident Investigation Method"
    ])
    
    # End slide
    add_slide("Bedankt!", ["Vragen of opmerkingen?", "Neem contact op met de HSE Manager", "JvG Consultancy | Safety • Governance • Advisory"])
    
    path = os.path.join(DELIVERABLES, f"PM_EL_{t['id']}_{t['slug']}_Presentatie_v1.0.pptx")
    prs.save(path)
    print(f"Created: {path}")
    return path


# Generate all
print("=" * 60)
print("Batch 3 — E-learning Modules (T15-T21)")
print("=" * 60)

for t in TRAININGS:
    print(f"\n--- {t['id']}: {t['title']} ---")
    create_docx(t)
    create_html(t)
    create_pptx(t)

print("\n" + "=" * 60)
print("ALLE 21 BESTANDEN GENEREERD")
print("=" * 60)
