#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Genereert T17 Werken in Besloten Ruimten e-learning - Phoenix Metals B.V."""
import os

OUTPUT = '/root/projects/jg/2026-PM-eLearning/deliverables/PM_EL_T17_Besloten_Ruimten_eLearning_v1.1.html'
TITLE = "T17: Werken in Besloten Ruimten"
SUBTITLE = "Training Werken in Besloten Ruimten (TR-302-09)"
CERT_TRAINING = "T17: Werken in Besloten Ruimten (TR-302-09)"
CERT_TITLE = "Werken in Besloten Ruimten"

MODULES = [
    {"title": "Definitie besloten ruimte (Arbobesluit bijlage IX)", "paragraphs": ["Een besloten ruimte is een ruimte die (grotendeels) is afgesloten van de buitenlucht en waardoor de natuurlijke ventilatie onvoldoende is om een veilig en gezond werkklimaat te waarborgen. De formele definitie staat in bijlage IX van het Arbobesluit.", "Kenmerken van een besloten ruimte zijn: beperkte toegang en uitgang (vaak via luiken, mangaten of smalle trappen), beperkte natuurlijke ventilatie, risico op ophoping van gevaarlijke gassen of zuurstoftekort, en beperkte mogelijkheden voor communicatie en hulpverlening.", "Typische besloten ruimten in de industrie: opslagtanks, reactorketels, silo's, leidingkelders, riolen, putten, holle pijlers, kruipruimtes, ketelhuizen en gescheiden compartimenten in installaties. Niet elke afgesloten ruimte is automatisch een besloten ruimte — het risico op gevaarlijke atmosfeer is het bepalende criterium.", "Het Arbobesluit vereist dat werkzaamheden in besloten ruimten alleen mogen worden uitgevoerd na een specifieke risicobeoordeling, met adequate veiligheidsmaatregelen en onder toezicht. De werkgever moet een procedure hebben voor het veilig betreden en verlaten van besloten ruimten.", "Phoenix Metals heeft diverse besloten ruimten op beide locaties: reactorvaten, opslagtanks voor chemicaliën, leidingkelders en gescheiden compartimenten in de proeffabriek. Elke betreding vereist een geldig Permit-to-Work (PtW) en een gasmeting vooraf."], "fact": "Ongeveer 60% van alle dodelijke ongevallen in besloten ruimten betreft reddingspogingen. Vaak overlijdt de redder terwijl het oorspronkelijke slachtoffer nog wordt gered — of ook overlijdt. Redding zonder juiste uitrusting is levensgevaarlijk.", "practice": "Phoenix Metals hanteert een verplichte procedure voor betreding van besloten ruimten: PtW + gasmeting + reddingsteam standby + communicatiemiddel. Zonder deze vier elementen mag de ruimte NIET worden betreden.", "question": "Wat is het bepalende criterium of een afgesloten ruimte als besloten ruimte geldt?", "options": ["De grootte van de ruimte", "Het risico op gevaarlijke atmosfeer", "De aanwezigheid van een deur", "De temperatuur in de ruimte"], "correct": 1},
    {"title": "Risico's: zuurstoftekort en giftige gassen", "paragraphs": ["De belangrijkste risico's in besloten ruimten zijn te verdelen in drie categorieën: atmosferische risico's (giftige gassen, zuurstoftekort, brandbare dampen), fysische risico's (beknelling, vallen, onderdompeling) en biologische risico's (bacteriën, schimmels).", "Zuurstoftekort (hypoxie) ontstaat wanneer het zuurstofgehalte daalt onder 19,5 vol%. Bij 16% optreden de eerste symptomen (versnelde ademhaling, versnelde hartslag). Bij 12% ontstaat bewusteloosheid en bij minder dan 6% is overlijden binnen minuten onvermijdelijk. Zuurstoftekort kan ontstaan door verdringing door andere gassen (stikstof, argon), door oxidatieprocessen (roesten) of door biologische afbraak.", "Giftige gassen die veel voorkomen in besloten ruimten: waterstofsulfide (H₂S — rotte-eierenlucht maar verdooft reukzin bij hogere concentraties), koolmonoxide (CO — kleurloos, reukloos, smaakloos), ammoniak (NH₃ — irriterend), en oplosmiddelen dampen. Bij Phoenix Metals is H₂S een relevant risico bij de verwerking van staalslakken.", "Brandbare of explosieve atmosferen ontstaan wanneer de concentratie van brandbaar gas of damp boven de LEL (Lower Explosive Limit) komt. Zelfs een kleine vonk kan een fatale explosie veroorzaken. De LEL verschilt per stof maar bedraagt voor methaan bijvoorbeeld 5 vol%.", "De combinatie van deze risico's maakt besloten ruimten tot een van de gevaarlijkste werkomgevingen. De risico's zijn vaak onzichtbaar: men ruikt, proeft of ziet ze niet. Alleen gasmetingen geven betrouwbare informatie over de atmosfeer in een besloten ruimte."], "fact": "Koolmonoxide (CO) is verantwoordelijk voor de meeste dodelijke vergiftigingen in besloten ruimten. CO bindt 200-300 keer sterker aan hemoglobine dan zuurstof, waardoor het lichaam snel verstikt terwijl de longen nog functioneren.", "practice": "Bij Phoenix Metals worden voor elke betreding van een besloten ruimte gasmetingen verricht op: O₂ (zuurstof), H₂S (waterstofsulfide), CO (koolmonoxide) en LEL (brandbare gassen). Pas wanneer alle vier de metingen binnen de veilige grenzen vallen, mag de ruimte worden betreden.", "question": "Bij welk zuurstofpercentage ontstaat bewusteloosheid?", "options": ["Onder 19,5%", "Onder 16%", "Onder 12%", "Onder 6%"], "correct": 2},
    {"title": "Gasmetingen: O₂ 20,9%, H₂S <10ppm, LEL <10%", "paragraphs": ["Gasmetingen zijn de ENIGE betrouwbare methode om de atmosfeer in een besloten ruimte te beoordelen. Visuele inspectie of reukproeven zijn volstrekt onvoldoende en levensgevaarlijk. Phoenix Metals hanteert strikte grenswaarden voor betreding.", "Zuurstof (O₂): De normale atmosferische concentratie is 20,9 vol%. De veilige range voor betreding is 19,5-23,5 vol%. Onder 19,5% is betreding verboden (zuurstoftekort). Boven 23,5% is betreding eveneens verboden (verhoogd brandgevaar — materialen ontbranden sneller).", "Waterstofsulfide (H₂S): De grenswaarde voor betreding is <10 ppm (TLV-TWA). Bij 10-20 ppm mag alleen met ademluchttoestel worden gewerkt. Boven 20 ppm is betreding verboden. H₂S is giftig, irriterend en verdooft de reukzin — men ruikt het initially (rotte eieren) maar ruikt het niet meer bij hogere concentraties.", "LEL (Lower Explosive Limit): De grenswaarde voor betreding is <10% van de LEL. Bij 10-25% LEL mag alleen met explosieveilige uitrusting worden gewerkt. Boven 25% LEL is betreding absoluut verboden. De LEL wordt gemeten als percentage van de onderste explosiegrens van de aanwezige gassen.", "Koolmonoxide (CO): De grenswaarde is <25 ppm (TLV-TWA). Bij hogere concentraties is ademluchttoestel verplicht. CO is kleurloos, reukloos en smaakloos — het 'stille gif' dat verantwoordelijk is voor veel dodelijke ongevallen."], "fact": "Bij 100 ppm H₂S treedt reukverlamming op: men ruikt het gas niet meer terwijl de concentratie levensgevaarlijk is. Bij 300 ppm treedt longontsteking op en bij 700 ppm is overlijden binnen 30 minuten mogelijk.", "practice": "Phoenix Metals gebruikt multi-gasdetectoren (4-gas meters) die tegelijk O₂, H₂S, CO en LEL meten. Elke medewerker die besloten ruimten betreedt moet getraind zijn in het gebruik van deze meters. Bij alarm: onmiddellijk evacueren.", "question": "Wat is de veilige range voor het zuurstofpercentage in een besloten ruimte?", "options": ["18,0-22,0%", "19,5-23,5%", "20,0-21,0%", "17,0-25,0%"], "correct": 1},
    {"title": "Gascalibratie en onderhoud detectieapparatuur", "paragraphs": ["Gasdetectieapparatuur is alleen betrouwbaar wanneer deze correct is gecalibreerd en onderhouden. Een niet-gecalibreerde gasmeter kan verkeerde waarden tonen, wat leidt tot een vals gevoel van veiligheid met potentieel fatale gevolgen.", "Bump test: Voor elk gebruik moet een bump test worden uitgevoerd. Hierbij wordt de sensor blootgesteld aan een bekende concentratie testgas om te controleren of de meter reageert. De reactietijd en alarmfunctie worden getest. Een bump test duurt slechts enkele seconden.", "Kalibratie: Naast de dagelijkse bump test moet de gasmeter periodiek worden gecalibreerd (meestal maandelijks of volgens fabrikantspecificatie). Bij kalibratie wordt de sensor afgesteld op een bekende gasconcentratie (testgas uit een kalibratiefles). Dit corrigeert eventuele drift van de sensor.", "Sensorvervanging: Elke type sensor heeft een beperkte levensduur. Elektrochemische sensors (CO, H₂S) gaan gemiddeld 2-3 jaar, katalytische sensors (LEL) 2-4 jaar en infraroodsensors (CO₂, koolwaterstoffen) 3-5 jaar. Verlopen sensors moeten tijdig worden vervangen.", "Phoenix Metals heeft een gasmeterbeheerprogramma: elke meter heeft een logboek met kalibratiedata, bump test resultaten en sensorvervangingsdata. Defecte of niet-gecalibreerde meters worden direct uit gebruik genomen en vervangen."], "fact": "Een gasmeter die niet regelmatig wordt gecalibreerd kan tot 50% afwijking vertonen. Dit betekent dat een meter 10% LEL kan tonen terwijl de werkelijke waarde 15-20% LEL bedraagt — een potentieel explosieve situatie.", "practice": "Voordat u een gasmeter gebruikt op de Phoenix Metals locatie, controleert u: (1) Is de bump test vandaag uitgevoerd? (2) Is de kalibratie nog geldig? (3) Zijn de sensors niet verlopen? (4) Is de batterij voldoende geladen? Bij twijfel: een andere meter pakken.", "question": "Wat is het verschil tussen een bump test en een kalibratie?", "options": ["Een bump test test of de meter reageert; kalibratie stelt de meter nauwkeurig af", "Ze zijn hetzelfde", "Kalibratie is sneller dan een bump test", "Een bump test is alleen voor LEL-sensors"], "correct": 0},
    {"title": "Ventilatievereisten voor besloten ruimten", "paragraphs": ["Ventilatie is de primaire beheersmaatregel voor het veilig stellen van de atmosfeer in een besloten ruimte. Natuurlijke ventilatie is in de meeste gevallen onvoldoende, waardoor mechanische ventilatie vereist is.", "Mechanische ventilatie werkt door schone lucht in te blazen (toevoer) of verontreinigde lucht af te zuigen (afzuiging). De voorkeur gaat uit naar toevoer-ventilatie: schone lucht wordt aan de ruimte toegevoegd waardoor de verontreinigde lucht wordt verdreven via natuurlijke openingen.", "De capaciteit van de ventilatie moet voldoende zijn om het volume van de ruimte meerdere keren per uur te verversen (minimaal 5-10 luchtverversingen per uur, afhankelijk van de risico's). De luchtstroom moet alle delen van de ruimte bereiken — dead spots moeten worden vermeden door de positie van de toevoer aan te passen.", "Belangrijk: ventilatie MAG NIET worden gebruikt als vervanging van gasmetingen. Zelfs met ventilatie moeten vóór en tijdens de betreding gasmetingen worden uitgevoerd. Ventilatie kan falen (stroomstoring, geblokkeerde slang) waardoor de atmosfeer snel verslechtert.", "Voor Phoenix Metals geldt dat elke besloten ruimte met potentieel gevaarlijke atmosfeer moet worden geventileerd vóór en tijdens betreding. De ventilatie-installatie moet explosieveilig zijn wanneer er een risico op brandbare atmosferen bestaat."], "fact": "In een ruimte van 3x3x3 meter (27 m³) kan een volwassene het zuurstofgehalte doen dalen tot een gevaarlijk niveau binnen 2-3 uur door ademhaling alleen, zonder enige andere bron van zuurstofverbruik.", "practice": "Phoenix Metals gebruikt mobiele ventilatieunits met flexibele slangen voor betreding van besloten ruimten. De ventilatie wordt gestart vóór de gasmeting en blijft draaien tijdens de gehele betreding. Bij stroomstoring: onmiddellijk evacueren.", "question": "Mag ventilatie gasmetingen vervangen?", "options": ["Ja, als de ventilatie krachtig genoeg is", "Ja, maar alleen voor korte betredingen", "Nee, gasmetingen zijn ALTIJD vereist, ook met ventilatie", "Ja, als de ruimte groot genoeg is"], "correct": 2},
    {"title": "Redding-team standby bij besloten ruimten", "paragraphs": ["Bij elke betreding van een besloten ruimte MOET een opgeleid reddingsteam standby staan. Dit is een wettelijke verplichting en een non-negotiable veiligheidsmaatregel. Het reddingsteam moet in staat zijn om binnen enkele minuten een redding uit te voeren.", "Het reddingsteam bestaat uit minimaal twee personen: één persoon die de betredende medewerker monitort en communiceert (de 'topman' of 'attendant'), en minimaal één persoon die de redding kan uitvoeren. De topman mag zijn positie NOOIT verlaten zolang er iemand in de ruimte is.", "De topman is verantwoordelijk voor: het bijhouden van een enterlog (wie is erin, sinds wanneer), het monitoren van de atmosfeer (continue gasmeting), het onderhouden van communicatie met de medewerker in de ruimte, en het alarmeren van het reddingsteam bij noodsituaties.", "Het reddingsteam moet beschikken over: ademluchttoestellen (SCBA of luchtlijn), reddingsharnas en lier, communicatiemiddelen (portofoon of treklijn), eerstehulpkit en zuurstof, en een evacuatieplan specifiek voor de betreffende ruimte.", "Phoenix Metals heeft getrainde reddingsteams op beide locaties die regelmatig oefenen met reddingsscenario's in besloten ruimten. De reddingstraining wordt minimaal tweemaal per jaar herhaald. Bij grote werkzaamheden wordt een extern professioneel reddingsteam ingeschakeld."], "fact": "Studies tonen aan dat de overlevingskans bij een incident in een besloten ruimte daalt met 10% per minuut zonder adequate redding. Na 10 minuten is de kans op overleving minder dan 10% bij zuurstoftekort of giftig gas.", "practice": "Phoenix Metals: bij elke betreding van een besloten ruimte staat het reddingsteam klaar met SCBA, reddingslier en communicatieapparatuur. De topman voert elke 5 minuten een contactcheck uit. Geen reactie = onmiddellijk alarmeren.", "question": "Wat is de minimale samenstelling van een reddingsteam bij betreding van een besloten ruimte?", "options": ["Eén persoon met een telefoon", "Twee personen: een topman en minimaal één redder", "Drie personen: topman, redder en EHBO'er", "Vier personen: volledig team"], "correct": 1},
    {"title": "Communicatieprotocol bij besloten ruimten", "paragraphs": ["Effectieve communicatie is essentieel voor de veiligheid bij werkzaamheden in besloten ruimten. Er moet altijd een betrouwbare communicatielijn bestaan tussen de medewerker in de ruimte en de topman buiten de ruimte.", "Communicatiemethoden: (1) Directe spraakcommunicatie — alleen bij korte, open ruimten met goede akoestiek. (2) Portofoons — de meest gebruikte methode bij Phoenix Metals, met een vast afgesproken kanaal. (3) Treklijn/touw — een mechanisch communicatiemiddel als back-up: één trek = 'alles goed', twee trekken = 'kom eruit', drie trekken = 'nood'.", "De topman voert regelmatig contactchecks uit (elke 5 minuten minimum). De medewerker in de ruimte moet ALTIJD direct reageren. Uitblijven van een reactie binnen 30 seconden wordt behandeld als een noodsituatie.", "Vooraf worden heldere signalen afgesproken: normaal communicatieprotocol, noodprotocol (hoe wordt alarm geslagen), evacuatieprotocol (hoe wordt de medewerker uit de ruimte gehaald), en het 'alles veilig' signaal bij afronding van de werkzaamheden.", "Phoenix Metals gebruikt een gestandaardiseerd communicatieformulier dat vooraf wordt ingevuld met: namen van betreders, topman en redders, gebruikte communicatiemiddelen, afgesproken signalen, noodcontacten en het evacuatieplan voor de specifieke ruimte."], "fact": "In meer dan 40% van de incidenten in besloten ruimten was er geen adequate communicatie tussen de medewerker binnen en de wacht buiten. Gebrek aan communicatie is een van de belangrijkste oorzaken van fatale afloop.", "practice": "Phoenix Metals topman protocol: (1) Enterlog bijhouden, (2) Elke 5 min contactcheck, (3) Continue gasmonitoring, (4) Bij geen reactie binnen 30 sec: alarm slaan, (5) NOOIT zelf naar binnen zonder SCBA.", "question": "Hoe vaak moet de topman minimaal een contactcheck uitvoeren?", "options": ["Elke 15 minuten", "Elke 10 minuten", "Elke 5 minuten", "Elke 30 minuten"], "correct": 2},
    {"title": "Permit-to-Work (PtW) voor besloten ruimten", "paragraphs": ["Een Permit-to-Work (PtW) is een formeel document dat toestemming geeft voor het uitvoeren van specifieke werkzaamheden onder vooraf gedefinieerde veiligheidsvoorwaarden. Voor besloten ruimten is een PtW VERPLICHT en mag niet mondeling worden vervangen.", "Het PtW voor besloten ruimten bevat minimaal: identificatie van de ruimte en de werkzaamheden, naam van de betredende medewerker(s), naam van de topman en reddingsteam, resultaten van de gasmetingen, toegepaste veiligheidsmaatregelen (ventilatie, PBM, communicatie), geldigheidsduur (meestal maximaal één dienst) en handtekeningen van alle betrokkenen.", "Het PtW wordt afgegeven door een bevoegde persoon (bij Phoenix Metals de HSE-officer of shiftleider) die de risicobeoordeling heeft goedgekeurd. Het PtW is pas geldig als alle voorwaarden zijn vervuld en alle partijen hebben getekend.", "Wijzigingen in de omstandigheden (andere gasmetingen, weersverandering, nieuwe risico's) vereisen een herziening of heruitgifte van het PtW. Bij twijfel wordt het PtW ingetrokken en moet een nieuwe worden aangevraagd.", "Phoenix Metals hanteert een digitaal PtW-systeem met tablet-app voor veldinvulling. Het systeem dwingt alle verplichte velden af en genereert automatisch een enterlog. Alle PtW's worden gearchiveerd voor audit en compliance-doeleinden."], "fact": "Onderzoek toont aan dat bedrijven met een formeel PtW-systeem 70% minder incidenten hebben bij werkzaamheden in besloten ruimten dan bedrijven zonder formeel systeem.", "practice": "Phoenix Metals PtW-procedure: (1) Aanvraag via app, (2) Risicobeoordeling door HSE, (3) Gasmeting + ventilatie opstarten, (4) Reddingsteam positioneren, (5) PtW ondertekenen, (6) Betreding, (7) Continue monitoring, (8) Aftekenen bij verlaten.", "question": "Wie mag een PtW voor een besloten ruimte afgeven?", "options": ["Elke medewerker", "De betredende medewerker zelf", "Een bevoegde persoon (HSE-officer of shiftleider)", "De directie"], "correct": 2},
    {"title": "Noodprocedure bij incidenten in besloten ruimten", "paragraphs": ["Een noodsituatie in een besloten ruimte vereist een snelle, gecoördineerde en veilige respons. De belangrijkste regel: NOOIT een besloten ruimte betreden om een collega te redden ZONDER adequaat ademluchttoestel (SCBA). Meer dan 60% van de slachtoffers bij besloten-ruimte-incidenten zijn redders.", "Stap 1 — Alarm slaan: De topman slaat onmiddellijk alarm via de portofoon en het algemeen alarmsysteem. Vermeld duidelijk: locatie, type incident (bewusteloosheid, gaslek, brand) en het aantal betrokkenen.", "Stap 2 — Ruimte NIET betreden zonder SCBA: De natuurlijke reactie om snel naar binnen te gaan en de collega eruit te trekken is begrijpelijk maar potentieel fataal. Zonder ademluchttoestel wordt de redder zelf slachtoffer binnen seconden.", "Stap 3 — Reddingsteam activeren: Het standby reddingsteam treedt in actie met SCBA en reddingsmateriaal. De redding wordt uitgevoerd volgens het vooraf opgestelde reddingsplan. De topman blijft op zijn post en coördineert de communicatie.", "Stap 4 — Eerste hulp en escalatie: Na de redding wordt eerste hulp verleend (ABC-methodiek). Bij bewusteloosheid: zuurstof toedienen en reanimatie indien nodig. Ambulance alarmeren via 112. Evacueren naar frisse lucht en medische follow-up garanderen."], "fact": "Het 'second victim syndrome' — waarbij redders zelf slachtoffer worden — is verantwoordelijk voor meer dan de helft van alle dodelijke ongevallen bij besloten ruimten. Training en discipline zijn de belangrijkste preventieve factoren.", "practice": "Phoenix Metals noodprocedure: ALARM → NIET BETREDEN zonder SCBA → Reddingsteam met SCBA → Eerste hulp → 112. De gouden regel: één slachtoffer is genoeg. Word geen tweede.", "question": "Wat is de belangrijkste regel bij een incident in een besloten ruimte?", "options": ["Direct naar binnen gaan om te helpen", "Eerst 112 bellen dan pas redden", "NOOIT betreden zonder adequaat ademluchttoestel (SCBA)", "Wachten op de brandweer"], "correct": 2},
    {"title": "Phoenix Metals ruimtes: reactor, opslagtanks, leidingkelders", "paragraphs": ["De Phoenix Metals locaties bevatten meerdere besloten ruimten die specifieke risico's en procedures vereisen. Hieronder een overzicht van de belangrijkste besloten ruimten per locatie.", "Reactorvaten: De reactorvaten bij de proeffabriek zijn de meest complexe besloten ruimten. Ze bevatten restanten van V₂O₅, H₂SO₄ en NaOH na productie. Risico's: giftige stoffen, zuurstoftekort door chemische reacties, en beperkte toegang via mangaten. Betreding vereist: volledige ontlading, spoelen, gasmeting, SCBA en PtW.", "Opslagtanks: De opslagtanks voor grondstoffen en producten zijn potentiële besloten ruimten. Na lediging kunnen dampen of gassen achterblijven. Risico's: H₂S-vorming bij staalslakkenverwerking, brandbare dampen, zuurstofverdringing door stikstof (inertisatie). Betreding vereist gasmeting op 4 gassen en continue ventilatie.", "Leidingkelders: De ondergrondse leidingkelders bevinden zich onder de procesinstallatie en herbergen leidingen voor chemicaliën, stoom en koelwater. Risico's: lekkage van chemicaliën, ophoping van zware gassen, beperkte vluchtwegen en risico op overstroming. Betreding vereist: gasmeting, verlichting en communicatie.", "Voor alle besloten ruimten geldt: alleen getrainde en bevoegde medewerkers mogen betreden. Ongeoefende medewerkers, ook van aannemers, moeten altijd worden begeleid door een ervaren Phoenix Metals medewerker die bekend is met de specifieke ruimte en de bijbehorende risico's."], "fact": "De Phoenix Metals PlantOne locatie in IJmuiden heeft 12 geïdentificeerde besloten ruimten, waarvan 4 hoogrisico (reactor, grote opslagtank, leidingkelder A en B). Rotterdam heeft 8 geïdentificeerde besloten ruimten.", "practice": "Phoenix Metals onderhoudt een besloten-ruimtenregister met voor elke ruimte: identificatie, specifieke risico's, vereiste PBM, reddingsprocedure en verantwoordelijke. Dit register is beschikbaar bij de HSE-afdeling en op de locatie-informatieborden.", "question": "Welke gassen moeten MINIMAAL worden gemeten bij betreding van een Phoenix Metals besloten ruimte?", "options": ["Alleen zuurstof", "Zuurstof en H₂S", "O₂, H₂S, CO en LEL (4-gas meting)", "Alleen LEL"], "correct": 2}
]

QUIZ = [
    {"question": "Wat is het bepalende criterium voor een besloten ruimte?", "options": ["De grootte", "Risico op gevaarlijke atmosfeer", "Aanwezigheid van een deur", "Temperatuur"], "correct": 1},
    {"question": "Bij welk O₂-percentage ontstaat bewusteloosheid?", "options": ["Onder 19,5%", "Onder 16%", "Onder 12%", "Onder 6%"], "correct": 2},
    {"question": "Wat is de veilige O₂-range voor betreding?", "options": ["18,0-22,0%", "19,5-23,5%", "20,0-21,0%", "17,0-25,0%"], "correct": 1},
    {"question": "Wat is het verschil tussen bump test en kalibratie?", "options": ["Bump test test reactie; kalibratie stelt nauwkeurig af", "Ze zijn hetzelfde", "Kalibratie is sneller", "Bump test is alleen voor LEL"], "correct": 0},
    {"question": "Mag ventilatie gasmetingen vervangen?", "options": ["Ja als krachtig genoeg", "Ja bij korte betredingen", "Nee, gasmetingen zijn ALTIJD vereist", "Ja als ruimte groot genoeg is"], "correct": 2},
    {"question": "Wat is de minimale samenstelling reddingsteam?", "options": ["1 persoon met telefoon", "Topman + minimaal 1 redder", "Topman + redder + EHBO'er", "Volledig team van 4"], "correct": 1},
    {"question": "Hoe vaak moet de topman een contactcheck doen?", "options": ["Elke 15 min", "Elke 10 min", "Elke 5 min", "Elke 30 min"], "correct": 2},
    {"question": "Wie mag een PtW afgeven?", "options": ["Elke medewerker", "De betredende medewerker", "Bevoegde persoon (HSE/shiftleider)", "De directie"], "correct": 2},
    {"question": "Wat is de belangrijkste regel bij een incident?", "options": ["Direct naar binnen", "Eerst 112 bellen", "NOOIT betreden zonder SCBA", "Wachten op brandweer"], "correct": 2},
    {"question": "Welke gassen MINIMAAL meten bij betreding?", "options": ["Alleen O₂", "O₂ en H₂S", "O₂, H₂S, CO en LEL", "Alleen LEL"], "correct": 2}
]

def build_html():
    mods_js = "[\n"
    for m in MODULES:
        mods_js += "            {{\n"
        mods_js += '                title: "' + m["title"].replace("\\","\\\\").replace('"','\\"') + '",\n'
        mods_js += "                paragraphs: [\n"
        for p in m["paragraphs"]:
            mods_js += '                    "' + p.replace("\\","\\\\").replace('"','\\"').replace("\n"," ") + '",\n'
        mods_js += "                ],\n"
        mods_js += '                fact: "' + m["fact"].replace("\\","\\\\").replace('"','\\"').replace("\n"," ") + '",\n'
        mods_js += '                practice: "' + m["practice"].replace("\\","\\\\").replace('"','\\"').replace("\n"," ") + '",\n'
        mods_js += '                question: "' + m["question"].replace("\\","\\\\").replace('"','\\"').replace("\n"," ") + '",\n'
        mods_js += "                options: [\n"
        for o in m["options"]:
            mods_js += '                    "' + o.replace("\\","\\\\").replace('"','\\"').replace("\n"," ") + '",\n'
        mods_js += "                ],\n"
        mods_js += "                correct: " + str(m["correct"]) + "\n"
        mods_js += "            }},\n"
    mods_js += "        ]"
    quiz_js = "[\n"
    for q in QUIZ:
        quiz_js += "            {{\n"
        quiz_js += '                question: "' + q["question"] + '",\n'
        quiz_js += "                options: [\n"
        for o in q["options"]:
            quiz_js += '                    "' + o + '",\n'
        quiz_js += "                ],\n"
        quiz_js += "                correct: " + str(q["correct"]) + "\n"
        quiz_js += "            }},\n"
    quiz_js += "        ]"
    return f"""<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Phoenix Metals - {TITLE}</title>
<style>
:root{{--primary:#003366;--secondary:#1a5276;--bg:#e8f4f8;--accent:#f39c12;--error:#e74c3c;--correct:#27ae60;}}
*{{margin:0;padding:0;box-sizing:border-box;}}
body{{font-family:Arial,sans-serif;background:var(--bg);color:#333;line-height:1.6;}}
.container{{max-width:1200px;margin:0 auto;padding:20px;}}
header{{background:var(--primary);color:#fff;padding:20px 0;text-align:center;position:sticky;top:0;z-index:100;box-shadow:0 2px 10px rgba(0,0,0,.1);}}
.progress-bar{{background:#ddd;height:20px;width:100%;position:sticky;top:80px;z-index:99;margin-bottom:20px;border-radius:10px;overflow:hidden;}}
.progress{{background:var(--accent);height:100%;width:0%;transition:width .5s ease;}}
.module{{background:#fff;padding:25px;margin-bottom:25px;border-radius:10px;box-shadow:0 2px 8px rgba(0,0,0,.1);}}
.module h2{{color:var(--primary);border-bottom:2px solid var(--accent);padding-bottom:10px;margin-bottom:15px;}}
.paragraph{{margin-bottom:15px;text-align:justify;}}
.info-box{{background:#f0f8ff;border-left:4px solid var(--secondary);padding:15px;margin:15px 0;border-radius:0 5px 5px 0;}}
.info-box h3{{color:var(--secondary);margin-bottom:10px;display:flex;align-items:center;}}
.practice-box{{background:#fff8dc;border:1px solid var(--accent);padding:15px;margin:15px 0;border-radius:5px;}}
.practice-box h3{{color:var(--accent);margin-bottom:10px;}}
.question{{background:#fff;border:1px solid #ddd;padding:15px;margin:15px 0;border-radius:5px;}}
.question h3{{color:var(--primary);margin-bottom:10px;}}
.options{{display:grid;gap:10px;}}
.option{{padding:10px;border:1px solid #ddd;border-radius:5px;cursor:pointer;transition:all .3s;}}
.option:hover{{background:#f0f0f0;}}
.option.selected{{border-color:var(--primary);background:#f0f8ff;}}
.option.correct{{background:var(--correct);color:#fff;}}
.option.incorrect{{background:var(--error);color:#fff;}}
.certificate{{background:linear-gradient(135deg,#f5f7fa 0%,#c3cfe2 100%);padding:30px;text-align:center;border-radius:10px;margin-top:30px;}}
.certificate h2{{color:var(--primary);margin-bottom:20px;}}
.certificate-content{{background:#fff;padding:30px;border-radius:10px;box-shadow:0 4px 15px rgba(0,0,0,.1);}}
.button{{background:var(--primary);color:#fff;border:none;padding:12px 25px;border-radius:5px;cursor:pointer;font-size:16px;transition:background .3s;}}
.button:hover{{background:var(--secondary);}}
.hidden{{display:none;}}.quiz-container{{margin-top:30px;}}
footer{{background:var(--primary);color:#fff;text-align:center;padding:20px;margin-top:40px;}}
</style>
</head><body>
<div class="progress-bar"><div class="progress" id="progress"></div></div>
<header><h1>Phoenix Metals B.V. - {TITLE}</h1><p>{SUBTITLE}</p></header>
<div class="container" id="modules"></div>
<div class="quiz-container hidden" id="quiz-container"><div class="module">
<h2>Quiz - 10 vragen (80% cesuur)</h2><div id="quiz-questions"></div>
<button class="button" onclick="checkQuiz()">Resultaten Controleren</button>
<button class="button hidden" id="retry-button" onclick="resetQuiz()">Opnieuw Proberen</button>
<button class="button hidden" id="certificate-button" onclick="showCertificate()">Certificaat Bekijken</button>
</div></div>
<div class="certificate hidden" id="certificate"><h2>Bewijs van Voltooiing</h2>
<div class="certificate-content">
<p>Deze certificeert dat de deelnemer de training <strong>{CERT_TITLE}</strong> met succes heeft afgerond.</p>
<p><strong>Deelnemer:</strong> _________________________</p>
<p><strong>Bedrijf:</strong> Phoenix Metals B.V.</p>
<p><strong>Training:</strong> {CERT_TRAINING}</p>
<p><strong>Datum:</strong> _________________________</p>
<p><strong>Resultaat:</strong> _________________________</p>
</div></div>
<footer><p>Phoenix Metals B.V. | Safety &amp; Governance &amp; Advisory | &copy; 2026</p></footer>
<script>
const totalModules=10,totalQuestions=10;let currentProgress=0,correctAnswers=0;
const modules={mods_js};
const quizQuestions={quiz_js};
function initModules(){{const mc=document.getElementById('modules');modules.forEach((mod,i)=>{{const d=document.createElement('div');d.className='module';d.innerHTML='<h2>Module '+(i+1)+': '+mod.title+'</h2>'+mod.paragraphs.map(p=>'<p class="paragraph">'+p+'</p>').join('')+'<div class="info-box"><h3>&#x1F4D6; Wist je dat?</h3><p>'+mod.fact+'</p></div><div class="practice-box"><h3>&#x1F3ED; Phoenix Metals Praktijk</h3><p>'+mod.practice+'</p></div><div class="question"><h3>Controlevraag</h3><p><strong>'+mod.question+'</strong></p><div class="options">'+mod.options.map((o,oi)=>'<div class="option" onclick="selectOption(this,'+oi+','+mod.correct+')">'+String.fromCharCode(65+oi)+'. '+o+'</div>').join('')+'</div></div>';mc.appendChild(d);updateProgress(((i+1)/totalModules)*100);}});const qb=document.createElement('div');qb.innerHTML='<button class="button" onclick="showQuiz()">Quiz Voltooien</button>';mc.appendChild(qb);}}
function updateProgress(p){{document.getElementById('progress').style.width=p+'%';currentProgress=p;}}
function selectOption(el,si,ci){{const opts=el.parentElement.querySelectorAll('.option');opts.forEach(o=>o.classList.remove('selected','correct','incorrect'));el.classList.add('selected');setTimeout(()=>{{if(si===ci)el.classList.add('correct');else{{el.classList.add('incorrect');opts[ci].classList.add('correct');}}opts.forEach(o=>o.style.pointerEvents='none');}},300);}}
function showQuiz(){{document.getElementById('modules').classList.add('hidden');document.getElementById('quiz-container').classList.remove('hidden');updateProgress(100);initQuiz();}}
function initQuiz(){{const qc=document.getElementById('quiz-questions');quizQuestions.forEach((q,i)=>{{const d=document.createElement('div');d.className='question';d.innerHTML='<h3>Vraag '+(i+1)+': '+q.question+'</h3><div class="options">'+q.options.map((o,oi)=>'<div class="option quiz-option" onclick="selectQuizOption(this,'+oi+','+q.correct+')">'+String.fromCharCode(65+oi)+'. '+o+'</div>').join('')+'</div>';qc.appendChild(d);}});}}
function selectQuizOption(el,si,ci){{const qe=el.closest('.question');const opts=qe.querySelectorAll('.option');opts.forEach(o=>{{o.classList.remove('selected','correct','incorrect');o.style.pointerEvents='none';}});el.classList.add('selected');if(si===ci){{el.classList.add('correct');correctAnswers++;}}else{{el.classList.add('incorrect');opts[ci].classList.add('correct');}}}}
function checkQuiz(){{const pct=(correctAnswers/totalQuestions)*100;const msg=pct>=80?'Gefeliciteerd! U heeft '+correctAnswers+'/'+totalQuestions+' vragen correct ('+pct.toFixed(1)+'%).':'Helaas, '+correctAnswers+'/'+totalQuestions+' correct ('+pct.toFixed(1)+'%). 80% vereist.';alert(msg);if(pct>=80)document.getElementById('certificate-button').classList.remove('hidden');else document.getElementById('retry-button').classList.remove('hidden');}}
function resetQuiz(){{document.getElementById('quiz-questions').innerHTML='';document.getElementById('retry-button').classList.add('hidden');document.getElementById('certificate-button').classList.add('hidden');correctAnswers=0;initQuiz();}}
function showCertificate(){{document.getElementById('quiz-container').classList.add('hidden');document.getElementById('certificate').classList.remove('hidden');}}
window.onload=function(){{initModules();}};
</script></body></html>"""

if __name__ == "__main__":
    os.makedirs(os.path.dirname(OUTPUT), exist_ok=True)
    with open(OUTPUT, 'w', encoding='utf-8') as f:
        f.write(build_html())
    sz = os.path.getsize(OUTPUT)
    print(f"T17: {sz} bytes ({sz/1024:.1f} KB)" + (" ⚠️ <30KB!" if sz < 30000 else " ✅"))
