#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Generator: Compliance Audit ChroomVI 003.056 — v3.0 (JvG Consultancy)
=====================================================================
Basis (gevalideerde bronnen):
- working/audit_analysis.md          — integrale artikelsgewijze toetsing B1-B18 (master)
- deliverables/docx/Compliance_Audit_ChroomVI_003056_v2.0.docx — alle v2.0-bevindingen
- deliverables/docx/Herzieningsvoorstel_003.056_CrVI_v3.0.docx — H1-H12 + H13-H15
- working/vergelijking_audit.md      — consistentietoets 003.056/003.057/003.059
- research/bronnen.md                — bronnenclassificatie

Branding conform MASTER_STYLEGUIDE v1.5 (§1, §8) + SOP_BRIEFING v9.0:
- Logo ratio 1.115:1 (754x676 px) — NOOIT uitrekken
  · Cover: kleurlogo 4.0 x 3.59 cm
  · Header: witlogo 2.0 x 1.79 cm (op donkerblauwe balk)
- Kleuren: primair #003366 · succes #00A859 · fout #EF4444 · info #3B82F6
- Font: Calibri (sans-serif DOCX-standaard)
- Bronverwijzingen in wetenschappelijke notatie [n] (§1.8)
"""
import os
import sys
from datetime import datetime, timezone

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import docx_helpers as H

from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
from docx.oxml import OxmlElement

OUT = "/root/projects/jg/2026-audit-koole-chromium-vi/deliverables/audit/Compliance_Audit_ChroomVI_003056_v3.0.docx"
DOC_TITLE = "Compliance Audit ChroomVI 003.056"
VERSION = "3.0"
DATUM = "2026-08-25"
AUTHOR = "JvG Consultancy — HSEQ Divisie"

P1C, P2C, P3C, OKC = "EF4444", "B45309", "3B82F6", "00A859"
P1BG, P2BG, P3BG, OKBG = "FEF2F2", "FFFBEB", "EFF6FF", "F0FDF4"
KLASSE_KLEUR = {"P1": (P1C, P1BG), "P2": (P2C, P2BG), "P3": (P3C, P3BG), "Conform": (OKC, OKBG)}

# ------------------------------------------------------- bevindingen B1-B18
# Bron: working/audit_analysis.md (gevalideerd) — mapping identiek aan v2.0.
FINDINGS = [
    dict(code="B1", titel="Meetstrategie en toetsing grenswaarde", klasse="P1",
         actie="H1 — nieuw hoofdstuk “Exposure Assessment & Monitoring” (NEN-EN 689/482, toetsing 1 µg/m³, verantwoordelijke QHSE Manager)",
         wetsartikel="Arbobesluit art. 4.2 lid 4; NEN-EN 689; NEN-EN 482 [2][3][8]",
         eis="Blootstelling wordt beoordeeld en gemeten volgens een meetstrategie conform NEN-EN 689, met meetprocedures conform NEN-EN 482 en toetsing aan de wettelijke grenswaarde van 1 µg/m³ (drempelwaarde sinds 1 maart 2017) [3][8].",
         bevinding="Het brondocument noemt in §5 (Monitoring and Review) “review of exposure monitoring data” en “exposure monitoring results” als reviewtrigger. Een meetstrategie is niet gedefinieerd: geen baseline- of taakmetingen, geen meetfrequentie, geen bemonsteringsduur, geen toetskader (bijv. het NEN-EN 689-toetskader waarbij drie metingen alle < 10% van de grenswaarde voldoende zekerheid bieden [3]), geen verantwoordelijke en geen koppeling van meetresultaten aan de 1 µg/m³-grenswaarde. Zonder metingen kan niet worden aangetoond dat het ALARP-doel (§1) en het P3/PBM-regime (§4.5) adequaat zijn."),
    dict(code="B2", titel="Arbeidsgezondheidskundig onderzoek (AGO)", klasse="P1",
         actie="H2 — paragraaf “Health Surveillance” + rol bedrijfsarts in §3",
         wetsartikel="Arbobesluit art. 4.10a [2]",
         eis="Werknemers die (mogelijk) worden blootgesteld aan kankerverwekkende stoffen hebben recht op arbeidsgezondheidskundig onderzoek, bij voorkeur vóór aanvang van de blootstellingswerkzaamheden en periodiek daarna.",
         bevinding="Het brondocument bevat nergens een verwijzing naar (P)AGO, bedrijfsarts, medische keuring of gezondheidssurveillance. De rollenbeschrijving in §3 bevat geen arbodienst-/bedrijfsartsrol."),
    dict(code="B3", titel="Bewaartermijn blootstellingsgegevens (40 jaar)", klasse="P1",
         actie="H3 — bewaar- en overdrachtsregeling (ook na projecteinde)",
         wetsartikel="Arbobesluit art. 4.10c [2][3]",
         eis="Gegevens over blootstelling en meetresultaten worden gedurende 40 jaar na beëindiging van de blootstelling bewaard en beschikbaar gehouden [3].",
         bevinding="Geen enkele bepaling over registratie-, bewaar- of overdrachtplicht van blootstellings- en meetgegevens. Voor een tijdelijk decommissioningproject is bovendien een overdrachtsregeling naar het KOO-dossier noodzakelijk."),
    dict(code="B4", titel="RI&E incl. tox-elementen en vervangingsonderbouwing", klasse="P1",
         actie="H5 — expliciete RI&E-koppeling + gedocumenteerde vervangings-/bronmaatregelafweging",
         wetsartikel="Arbobesluit art. 4.13 i.c.m. art. 4.2 lid 1 [2][3]",
         eis="De werkgever documenteert in de RI&E o.a. aard, mate en duur van de blootstelling, de genomen maatregelen en de gevallen waarin kankerverwekkende stoffen worden vervangen dan wel waarom vervanging (nog) niet mogelijk is; voor chroom-6 is vervangen de belangrijkste inspanningsverplichting [3].",
         bevinding="§4.4 verplicht dat risico’s worden “addressed in the relevant TRA/JSA and Permit to Work”. Een TRA/JSA is taakgebonden en vult de RI&E-registratieplicht (aard/mate/duur, hoeveelheden, aantal blootgestelde werknemers, arbeidsvorm) niet in. Een vervangings-/bronmaatregelafweging (bijv. koude versus thermische snijtechniek) ontbreekt als verplichte documentatiestap — terwijl §5 alternatieven voor perslucht en vacuumsystemen wél overweegt."),
    dict(code="B5", titel="Lijst van blootgestelde werknemers + inzagerecht", klasse="P1",
         actie="H4 — registratieparagraaf “Exposure Register” incl. subcontractors en inzage-/afschriftrecht",
         wetsartikel="Arbobesluit art. 4.15 [2][3]",
         eis="Een lijst wordt bijgehouden van werknemers die worden of kunnen worden blootgesteld, onder vermelding van de ondergane blootstelling; werknemers hebben inzage- en afschriftrecht [3].",
         bevinding="Volledig afwezig. Geen registratieplicht voor blootgesteld personeel (eigen personeel én subcontractors — §3 verplicht contractors tot naleving, zodat de lijstplicht doorloopt in de keten) en geen inzagerechtregeling."),
    dict(code="B6", titel="Abnormale blootstelling: melding + OR/PVT", klasse="P2",
         actie="H6 — calamiteitenprotocol abnormale blootstelling incl. OR/PVT-kanaal",
         wetsartikel="Arbobesluit art. 4.21 [2]",
         eis="Bij abnormale blootstelling (o.a. overschrijding van de grenswaarde, incidenten) worden werknemers onmiddellijk geïnformeerd, maatregelen genomen en de OR/PVT ingeschakeld.",
         bevinding="§4.3.3 en §4.5.2 bevatten een goede meldplicht aan de leidinggevende (“stop work and inform supervision”). Het formele kanaal naar OR/PVT (of het offshore PVT-equivalent) en de diagnose-/onderzoeksstap na (mogelijke) overschrijding ontbreken."),
    dict(code="B7", titel="Wettelijke grenswaarde 1 µg/m³", klasse="Conform",
         actie="Handhaven als best practice",
         wetsartikel="Arbobesluit / SER-grenswaardenlijst [2][7]",
         eis="Toepassing van de wettelijke grenswaarde voor chroom VI: 1 µg/m³ (8-uurs TGG) [7].",
         bevinding="§6 vermeldt de NL-grenswaarde 1 µg/m³, het aanvullende streefrisiconiveau 0,01 µg/m³, het beginsel “most stringent requirement shall be followed” en de Belgische OEL (0,005 mg/m³) — feitelijk juist [3]. Kanttekening: de grenswaarde wordt nergens in de procedure zelf als toetskader gebruikt (raakt B1)."),
    dict(code="B8", titel="REACH Bijlage XIV (autorisatie Cr(VI))", klasse="P2",
         actie="H7 — REACH-referentie in §6 met afbakening wanneer de autorisatieplicht geldt",
         wetsartikel="REACH Verordening (EG) Nr. 1907/2006, Bijlage XIV [4]",
         eis="Chroomtrioxide en diverse zuurchromaten/zichromaten staan op de autorisatielijst (sunset dates 2017/2019); gebruik zonder autorisatie is verboden [4].",
         bevinding="Niet in de referentielijst (§6). Voor puur verwijderingswerk is de autorisatieverplichting beperkt relevant, maar bij bijkomstig gebruik van Cr(VI)-houdende producten (bijv. touch-up primers, passiveringsmiddelen) is de autorisatie- respectievelijk vervangingscheck relevant. Minimaal verdient een toelichting in §6 plaats."),
    dict(code="B9", titel="Afvalstroom Cr(VI): klassering en afvoer", klasse="P2",
         actie="H8 — uitgewerkte afvalparagraaf incl. zeetransport en bewijslast",
         wetsartikel="Besluit activiteiten leefomgeving (Bal); Wet milieubeheer / Landelijk Afvalbeheerplan [5]",
         eis="Gevaarlijk afval (Cr(VI)-gecontamineerd PBM, reinigingsresten, filters) vereist afvalstroomherkenning, gescheiden inzameling en gecontroleerde afvoer/verwerking door erkende afzender-ontvangers.",
         bevinding="§4.5.1–4.5.3 verplicht disposie “as hazardous or chemical waste in accordance with applicable waste management requirements” — een juist maar generiek beginsel. Er is geen afvalstroomklassering, geen afgiftebewijsregeling, geen verpakkings-/etiketteringseis (offshore inclusief zeetransport) en geen verantwoordelijke voor afvalstroombeheer benoemd."),
]
FINDINGS += [
    dict(code="B10", titel="AI-bladen 67, 31, 18, 77 als praktijkkader", klasse="P2",
         actie="H9 — AI-bladen opnemen in §6 als toetsingskader voor revisies",
         wetsartikel="Arbo-Informatiebladen SZW: 67 (kankerverwekkende stoffen), 31 (gezondheidsrisico’s gevaarlijke stoffen), 18 (PBM), 77 (chroom-6) [3][10]",
         eis="De AI-bladen vormen het implementatie- en praktijkkader voor Arbobesluit Hoofdstuk 4 Afdeling 3.",
         bevinding="Niet opgenomen in §6. De inhoud van het brondocument is grotendeels congruent met AI-77 (gezondheidseffecten, blootstellingsroutes, beheersmaatregelen), maar door de ontbrekende referenties is de afstemming niet aantoonbaar en mist de redacteur het toetsingskader voor toekomstige revisies."),
    dict(code="B11", titel="Beheersregime Chroom-6", klasse="P2",
         actie="H10 — beheersmaatregelenmatrix per activiteit of verwijzing per subparagraaf",
         wetsartikel="Beheersregime Chroom-6 (Rijkswaterstaat, Defensie, ProRail, Rijksvastgoedbedrijf) [6]",
         eis="Vertaling van het branchekader naar concrete, per activiteit gestandaardiseerde beheersmaatregelen.",
         bevinding="§6 noemt het “Dutch ‘Beheersregime Chroom-6’” in de referentielijst — juist en relevant. Het regime wordt echter niet operationeel vertaald: geen beheersmaatregelenmatrix, geen koppeling aan de subparagrafen 4.5.1–4.5.3."),
    dict(code="B12", titel="5M-methode / hiërarchie incl. substitutie", klasse="P2",
         actie="H11 — volledige maatregelenhiërarchie verankeren incl. verplichte documentatie van de afweging",
         wetsartikel="Arbocatalogi (5M/MAC-methodiek); Arbobesluit art. 4.4-structuur [2][3]",
         eis="De volledige hiërarchie van beheersmaatregelen — van substitutie/bronmaatregelen tot en met PBM — wordt afgewogen en gedocumenteerd.",
         bevinding="§4.5 beschrijft expliciet een hiërarchie (minimaliseren verstoring, beperken verspreiding, toegangsbeperking, ventilatie/extractie/natte methoden, daarna pas PBM) — dit sluit aan bij de 5M/MAC-methodiek. De hoogste treden ontbreken als documentatieverplichting: substitutie-/bronmaatregelen (koude technieken, extractie) en organisatorische maatregelen (taakroulatie, blootstellingstijdbeperking) zijn niet aantoonbaar afgewogen. Voor decommissioning is substitutie van de coating geen optie — ook dat moet als onderbouwing zijn vastgelegd (zie B4)."),
    dict(code="B13", titel="NEN-EN 689/482-normverwijzing in referentielijst", klasse="P1*",
         actie="Valt onder H1; separaat benoemd omdat de normreferentie in §6 afzonderlijk ontbreekt",
         wetsartikel="NEN-EN 689; NEN-EN 482 [8]",
         eis="De toegepaste meetstrategie en -procedures zijn in de referentielijst van de procedure verankerd.",
         bevinding="Niet in de referentielijst (§6). B13 is formeel onderdeel van B1 en wordt slechts separaat genummerd omdat de normreferentie in §6 zelfstandig ontbreekt; zonder B13 telt de matrix 17 items."),
    dict(code="B14", titel="Documentbeheer / versieconsistentie", klasse="P2",
         actie="H12 — één consistente revisieaanduiding, gevulde revision log, consistente issue-datum, datering interne referenties",
         wetsartikel="Kwaliteitsysteemlogica / ISO 9001-documentbeheer",
         eis="Een “Issued for Implementation”-procedure heeft een eenduidige revisiestatus, een gevulde wijzigingslog en gedateerde referenties.",
         bevinding="De coverpagina vermeldt zowel Rev A (02-03-2026, “Issued for Comments”) als Rev 1 (15-04-2026, “Issued for Implementation”); de revision log bevat uitsluitend een Rev 1-regel met lege velden voor Section en Description of Change; de voettekst vermeldt “Rev. 1 — May 2026” terwijl de issue-datum 15-04-2026 is. Daarnaast is de “Hoondert Study” (§6, Ref. [2]) als interne referentie zonder datum/versie opgenomen."),
    dict(code="B15", titel="Gevareninformatie & gezondheidseffecten", klasse="Conform",
         actie="Handhaven als best practice",
         wetsartikel="AI-77; CLP [3]",
         eis="Volledige en feitelijk juiste gevareninformatie over chroom-6.",
         bevinding="§4.1–4.3: stofeigenschappen, bronnen (coatings, rvs-lassen, verduurzaamd hout, cement), blootstellingsroutes en gezondheidseffecten (longkanker, neusholtekanker, neusseptumperforatie, COPD, astma, dermatitis; vermoedelijk maag- en strottenhoofdkanker) — feitelijk consistent met AI-77 [3]."),
    dict(code="B16", titel="Identificatie, marking, surveys, PTW/TRA-koppeling", klasse="Conform",
         actie="Handhaven als best practice",
         wetsartikel="AI-77 praktijkkader; Arbobesluit art. 4.5-structuur [2][3]",
         eis="Herkenbare identificatie en marking van Cr(VI)-verdachte materialen, gekoppeld aan taakrisicobeoordeling en werkvergunning.",
         bevinding="§4.4: third-party surveys, markering (verf, borden, “Cr6”-spray), het beginsel “treat as contaminated until confirmed otherwise” en de verplichte koppeling aan TRA/JSA + PTW vóór aanvang van werk — goed uitgewerkt."),
    dict(code="B17", titel="Hygiëne, decontaminatie, PBM-regime (P3, volgelaatsmasker)", klasse="Conform",
         actie="Handhaven als best practice",
         wetsartikel="AI-blad 18; Arbobesluit art. 4.5/8.35-structuur [2][10]",
         eis="Concreet, per activiteit uitvoerbaar hygiëne- en PBM-regime.",
         bevinding="§4.5.1–4.5.3: eet-/rookverbod in het werkgebied, wassen/douchen, decontaminatiestations, 5 m-restrictiezone (of ruimer volgens TRA), volgelaatsmasker met P3-filter (≥ 99,95% filtratie), filterinspectie/-vervanging, minimalisering van perslucht, stofzuigsystemen met filtratie en afvoer van PBM als gevaarlijk afval — concreet en uitvoerbaar per activiteit."),
    dict(code="B18", titel="Rollen & verantwoordelijkheden", klasse="Conform",
         actie="Handhaven als best practice",
         wetsartikel="Arbobesluit art. 3.4-structuur (verantwoordelijkheidsverdeling werkgever) [2]",
         eis="Volledige en afdwingbare verantwoordelijkheidsverdeling.",
         bevinding="§3: Management, QHSE Manager, OIM/Site Manager, Supervisors, Personnel en Contractors — compleet, inclusief stop-work-bevoegdheid op de werkvloer."),
]

# ------------------------------------------------- nieuwe P3-items (v3.0)
GAPS_NIEUW = [
    dict(code="G-01", titel="Instructie en training Cr(VI)-specifiek", klasse="P3",
         kader="AI-blad 18/77-praktijk [3][10]",
         hiaat="De procedure koppelt werk aan TRA/JSA + PTW (§4.4), maar bevat geen Cr(VI)-specifiek instructie- of competentie-element voor uitvoerenden.",
         actie="Voeg een instructie-/competentieparagraaf toe: Cr(VI)-bewustzijnstraining bij onboarding, registratieplicht voor PBM-fit-tests (volgelaatsmasker, koppeling B17) en periodieke spill-response-instructie."),
    dict(code="G-02", titel="Kwaliteitsborging meetproces", klasse="P3",
         kader="NEN-EN 689/482 [8]",
         hiaat="B1/H1 introduceert een meetstrategie; de kwaliteitsborging van het meetproces zelf (laboratorium, apparatuur, data) is nog niet ingevuld.",
         actie="Vul H1 aan met: analyses uitsluitend door een ISO/IEC 17025-geaccrediteerd laboratorium, kalibratie- en onderhoudsregime voor bemonsteringsapparatuur, en een validatieprotocol voor meetdata met bewaartermijn van meetrapporten (koppeling B3/H3)."),
    dict(code="G-03", titel="Continue verbetercyclus", klasse="P3",
         kader="Kwaliteitsysteemlogica / ISO 9001",
         hiaat="§5 (Continuous Improvement) kent reviewtriggers (incidenten, monitoringresultaten, wijzigingen), maar geen vaste reviewfrequentie en geen lessons-learned-structuur.",
         actie="Veranker een jaarlijkse periodieke review van de procedure, een gestandaardiseerde incidentanalyse (koppeling H6) en een feedbackloop met de werkvloer via toolbox- en Lessen-Geleerd-momenten."),
]

# --------------------------------------------------------------- branding
def header_footer_v3(doc, title_short, footer_left):
    """Header: donkerblauwe balk met titel + witlogo 2.0 x 1.79 cm (ratio 1.115:1)."""
    sec = doc.sections[0]
    sec.header_distance = Cm(0.6); sec.footer_distance = Cm(0.6)
    hdr = sec.header
    ht = hdr.add_table(rows=1, cols=2, width=Cm(17.0))
    ht.style = 'Table Grid'; ht.autofit = False
    c0, c1 = ht.rows[0].cells
    c0.width = Cm(13.8); c1.width = Cm(3.2)
    for c in (c0, c1):
        H.shade(c, H.PRIMARY); H.set_cell_margins(c, 80, 140, 80, 140)
    c0.text = ""
    p = c0.paragraphs[0]
    r = p.add_run(title_short); r.bold = True; r.font.size = Pt(10)
    r.font.color.rgb = RGBColor.from_string("FFFFFF"); r.font.name = "Calibri"
    r2 = p.add_run("\nJvG Consultancy — Safety • Governance • Advisory  |  HSEQ Divisie")
    r2.font.size = Pt(8); r2.font.color.rgb = RGBColor.from_string("D1D5DB"); r2.font.name = "Calibri"
    c1.text = ""
    p1 = c1.paragraphs[0]; p1.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    p1.add_run().add_picture(H.LOGO_WHITE, width=Cm(2.0), height=Cm(1.79))
    ftr = sec.footer
    ft = ftr.add_table(rows=1, cols=1, width=Cm(17.0))
    ft.style = 'Table Grid'
    fc = ft.rows[0].cells[0]; H.shade(fc, H.TEXT_SEC); H.set_cell_margins(fc, 60, 140, 60, 140)
    fc.text = ""
    fp = fc.paragraphs[0]; fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = fp.add_run(f"{footer_left}  |  v{VERSION}  |  {DATUM}  |  Pagina ")
    r.font.size = Pt(8.5); r.font.color.rgb = RGBColor.from_string("FFFFFF"); r.font.name = "Calibri"
    H.add_page_field(fp)
    r = fp.add_run("  |  JvG Consultancy")
    r.font.size = Pt(8.5); r.font.color.rgb = RGBColor.from_string("FFFFFF"); r.font.name = "Calibri"


def kpi_card(cell, aantal, label, toelichting, kleur, bg):
    """KPI-card: groot getal in merkkleur + label + toelichting (§1.3 card-stijl)."""
    H.shade(cell, bg); H.set_cell_margins(cell, 140, 200, 140, 200)
    cell.text = ""
    p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.space_after = Pt(2)
    r = p.add_run(str(aantal)); r.bold = True; r.font.size = Pt(30)
    r.font.color.rgb = RGBColor.from_string(kleur); r.font.name = "Calibri"
    p2 = cell.add_paragraph(); p2.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p2.paragraph_format.space_after = Pt(2)
    r2 = p2.add_run(label); r.bold = True; r.font.size = Pt(11)
    r2.font.color.rgb = RGBColor.from_string(kleur); r2.font.name = "Calibri"
    p3 = cell.add_paragraph(); p3.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r3 = p3.add_run(toelichting); r3.font.size = Pt(8.5)
    r3.font.color.rgb = RGBColor.from_string(H.TEXT_SEC); r3.font.name = "Calibri"


def kpi_cards(doc):
    t = doc.add_table(rows=2, cols=2)
    t.style = 'Table Grid'; t.alignment = WD_TABLE_ALIGNMENT.CENTER; t.autofit = False
    cards = [
        ("5", "P1 — Kritiek", "Wettelijk verplicht en ontbreekt (B1–B5; B13*)", P1C, P1BG),
        ("7", "P2 — Verbetering", "Verplicht/verwacht en onvolledig (B6, B8–B12, B14)", P2C, P2BG),
        ("3", "P3 — Aanvullend", "Nieuw in v3.0 (G-01 t/m G-03)", P3C, P3BG),
        ("6", "Conform", "Voldoet — handhaven als best practice (B7, B15–B18)", OKC, OKBG),
    ]
    for i, (n, lab, sub, kl, bg) in enumerate(cards):
        c = t.rows[i // 2].cells[i % 2]
        c.width = Cm(8.0)
        kpi_card(c, n, lab, sub, kl, bg)
    doc.add_paragraph().paragraph_format.space_after = Pt(2)


def status_regel(doc, klasse, extra=""):
    """Gekleurde statusregel met lichte achtergrond als badge-blok."""
    key = klasse.rstrip("*")
    kleur, bg = KLASSE_KLEUR.get(key, (H.TEXT_DARK, "F8F9FA"))
    t = doc.add_table(rows=1, cols=1); t.style = 'Table Grid'; t.autofit = False
    c = t.rows[0].cells[0]; H.shade(c, bg); H.set_cell_margins(c, 60, 120, 60, 120)
    c.text = ""
    p = c.paragraphs[0]
    r = p.add_run(f"Classificatie: {klasse}" + (f" — {extra}" if extra else ""))
    r.bold = True; r.font.size = Pt(9.5)
    r.font.color.rgb = RGBColor.from_string(kleur); r.font.name = "Calibri"
    doc.add_paragraph().paragraph_format.space_after = Pt(2)


def finding_block(doc, f):
    H.h3(doc, f"{f['code']} — {f['titel']}")
    status_regel(doc, f["klasse"])
    H.body(doc, f["wetsartikel"], bold_lead="Toetsingskader: ", size=10)
    H.body(doc, f["eis"], bold_lead="Eis: ", size=10)
    H.body(doc, f["bevinding"], bold_lead="Bevinding: ", size=10)
    H.body(doc, f["actie"], bold_lead="Actie: ", size=10)


def gap_block(doc, g):
    H.h3(doc, f"{g['code']} — {g['titel']}  (nieuw in v3.0)")
    status_regel(doc, g["klasse"], "aanvullend verbeterpunt, geen wettelijk hiaat")
    H.body(doc, g["kader"], bold_lead="Kader: ", size=10)
    H.body(doc, g["hiaat"], bold_lead="Waarneming: ", size=10)
    H.body(doc, g["actie"], bold_lead="Actie: ", size=10)

# ------------------------------------------------------------ documentbouw
def build():
    doc = Document()
    H.style_base(doc)

    header_footer_v3(doc, "Compliance Audit 003.056 Chromium VI",
                     "Compliance Audit 003.056 ChroomVI — Vertrouwelijk")

    # -- Cover --------------------------------------------------------
    H.cover(doc, "COMPLIANCE AUDIT — VERSIE 3.0",
            "ChroomVI — Koole 003.056",
            "Artikelsgewijze toetsing B1–B18 · KPI-overzicht · Gap-analyse v2.0 → v3.0",
            [
                ["Opdrachtgever", "Wintershall"],
                ["Geadresseerde (auditee)", "Koole (KOO B.V.)"],
                ["Brondocument", "003.056 Chromium VI Exposure Control Procedure — Rev A (02-03-2026) / Rev 1 (15-04-2026) [1]"],
                ["Project", "2026-audit-koole-chromium-vi — decommissioning offshore platform (Noordzee)"],
                ["Audittype", "Document-based compliance audit tegen het Nederlandse arbo-juridisch kader"],
                ["Iteratie", "v1.0 → v2.0 → v3.0 (alle v2.0-bevindingen overgenomen en her valideerd)"],
                ["Datum / versie", f"{DATUM} · v{VERSION}"],
                ["Samensteller", AUTHOR],
                ["Status", "Definitief — Vertrouwelijk"],
            ])

    # -- 1. Managementsamenvatting ------------------------------------
    H.h1(doc, "1. Managementsamenvatting")
    H.body(doc, "Het brondocument 003.056 Chromium VI Exposure Control Procedure (Rev A/Rev 1, 2026) is een degelijke, praktisch uitvoerbare operationele procedure op het gebied van gevareninformatie, marking, hiërarchie van beheersmaatregelen, hygiëne/decontaminatie en PBM-regime. De procedure noemt de Nederlandse grenswaarde (1 µg/m³) en het streefrisiconiveau (0,01 µg/m³) correct (§6) [3][7]. Echter, de formele arbo-juridische verslagleggings- en zorgstructuur ontbreekt vrijwel volledig: geen meetstrategie conform NEN-EN 689, geen arbeidsgezondheidskundig onderzoek (AGO), geen 40-jaar bewaartermijn, geen lijst van blootgestelde werknemers en geen expliciete RI&E-registratie met vervangingsonderbouwing. Dit zijn verplichtingen uit het Arbobesluit (Hoofdstuk 4, Afdeling 3) die niet via TRA/JSA-verwijzingen kunnen worden afgedekt [2].")
    H.body(doc, "Kernoordeel: herziening H1–H5 is vereist vóór projectuitvoering; H6–H12 in dezelfde of een opvolgende revisie. Voor kankerverwekkende stoffen geldt in Nederland verscherpte aandacht, mede gezien de chroom-6-dossiers bij Defensie, ProRail en de gemeente Tilburg [3]. In deze versie 3.0 zijn alle v2.0-bevindingen overgenomen en opnieuw gevalideerd tegen het brondocument, is de artikelsgewijze toetsing volledig in dit rapport geïntegreerd en is een P3-laag met drie aanvullende verbeterpunten (G-01 t/m G-03) toegevoegd.")

    # -- 2. KPI-overzicht ----------------------------------------------
    H.h1(doc, "2. KPI-overzicht")
    kpi_cards(doc)
    H.make_table(doc,
                 ["KPI", "v1.0 (24-08)", "v2.0 (24-08)", "v3.0 (25-08)"],
                 [["P1 — Kritiek", "5", "5", "5"],
                  ["P2 — Verbetering", "7", "7", "7"],
                  ["P3 — Aanvullend", "0", "0", "3"],
                  ["Conform", "6", "6", "6"],
                  ["Totaal beoordeelde items", "18", "18", "21"]],
                 widths=[6.0, 3.4, 3.4, 3.4], size=9.5,
                 color_cols={1: H.prio_color, 2: H.prio_color, 3: H.prio_color})
    H.body(doc, "B13 valt formeel onder B1 en is slechts separaat genummerd omdat de NEN-EN 689/482-referentie in §6 zelfstandig ontbreekt; zonder B13 telt de matrix 17 items. De P1/P2/Conform-verdeling is over alle iteraties stabiel — de auditbevindingen zijn consistent gebleken, ook in de cross-projecttoets tegen de kwik- (003.057) en asbestaudits (003.059).", size=9.5)

    # -- 3. Toetsingskader ---------------------------------------------
    H.h1(doc, "3. Toetsingskader")
    H.body(doc, "Toetsing vindt plaats tegen het Nederlandse arbo-juridische kader voor kankerverwekkende stoffen, aangevuld met relevant milieurecht en praktijkkaders:")
    for b in [
        "Arbobesluit Hoofdstuk 4, Afdeling 3 — art. 4.2 lid 1 en 4, 4.2a, 4.10a, 4.10c, 4.13, 4.15, 4.21 [2]",
        "SER-wettelijke grenswaardenlijst — chroom VI: 1 µg/m³ (8-uurs TGG) [7]",
        "REACH Verordening (EG) Nr. 1907/2006, Bijlage XIV — autorisatie chroomtrioxide/zuurchromaten [4]",
        "Richtlijn (EU) 2017/2398 (wijziging KMR-richtlijn 2004/37/EG) — reeds in §6 van het brondocument [9]",
        "Besluit activiteiten leefomgeving (Bal) en Wet milieubeheer/LAP — gevaarlijk afvalregime [5]",
        "Beheersregime Chroom-6 (RWS, Defensie, ProRail, Rijksvastgoedbedrijf) [6]",
        "Arbo-Informatiebladen 67, 31, 18 en 77 [3][10]",
        "NEN-EN 689 (meetstrategie) en NEN-EN 482 (meetprocedures) [8]",
    ]:
        H.bullet(doc, b)
    H.h3(doc, "Kernfeiten")
    H.make_table(doc,
                 ["Kernfeit", "Waarde", "Bron"],
                 [["Grenswaarde Cr(VI)", "1 µg/m³ (8-uurs TGG), per 1-3-2017", "SER / AI-77 [3][7]"],
                  ["Streefrisiconiveau", "0,01 µg/m³", "SER / AI-77 [3]"],
                  ["Gezondheidsraad-beredenering", "≈ 4 extra longkankergevallen per 1.000 bij 40 jaar × 1 µg/m³", "AI-77 [3]"],
                  ["REACH Bijlage XIV", "Chroomtrioxide/zuurchromaten — sunset dates 2017/2019", "REACH [4]"],
                  ["Versie-inconsistentie brondocument", "Rev A vs Rev 1; lege revision log; “May 2026”-voettekst", "Brondocument [1]"]],
                 widths=[5.0, 7.4, 3.8], size=9.0)
    doc.add_page_break()

    # -- 4. Artikelsgewijze toetsing -----------------------------------
    H.h1(doc, "4. Artikelsgewijze toetsing B1–B18")
    H.body(doc, "Elke eis is getoetst aan de integrale tekst van het brondocument (12 pagina's) [1]. De classificatie kent drie niveaus: P1 (wettelijk verplicht, ontbreekt — kritiek), P2 (verplicht/verwacht, onvolledig — verbetering) en Conform. Elke bevinding is 1-op-1 gekoppeld aan een herstelactie (H-nummer) uit het Herzieningsvoorstel [11].", size=10)
    H.h3(doc, "4.1 Overzichtsmatrix")
    H.make_table(doc, ["#", "Eis", "Klasse"],
                 [[f["code"], f["titel"], f["klasse"]] for f in FINDINGS],
                 widths=[1.6, 11.0, 3.6], size=9.0,
                 color_cols={2: H.prio_color})
    H.h3(doc, "4.2 Detailtoetsing per bevinding")
    for f in FINDINGS:
        finding_block(doc, f)
    doc.add_page_break()

    # -- 5. Gap Analysis v2.0 -> v3.0 ----------------------------------
    H.h1(doc, "5. Gap Analysis v2.0 → v3.0")
    H.body(doc, "Versie 3.0 is geen gewijzigde auditopvatting, maar een verdieping en verbreding van v2.0. De P1/P2/Conform-kern is ongewijzigd en opnieuw gevalideerd; de verbeteringen zitten in rapportagediepte, traceerbaarheid en een nieuwe P3-laag.")
    H.h3(doc, "5.1 Verbeterpunten v2.0 → v3.0")
    H.make_table(doc,
                 ["Aspect", "v2.0", "v3.0"],
                 [["Artikelsgewijze toetsing", "Samengevat; verwezen naar Golden Standard Audit en Conformiteitsmatrix (XLSX)", "Volledig geïntegreerd in dit rapport: per B-item toetsingskader, eis, bevinding en actie"],
                  ["KPI-presentatie", "Classificatietabel in managementsamenvatting", "KPI-cards (P1/P2/P3/Conform) + evolutietabel v1.0 → v2.0 → v3.0"],
                  ["P3-laag", "Niet aanwezig", "3 aanvullende verbeterpunten (G-01 t/m G-03): instructie/training, meetkwaliteitsborging, continue verbetercyclus"],
                  ["Consistentietoets", "Niet opgenomen", "Cross-project afgezet tegen kwik-audit 003.057 en asbest-audit 003.059 (zelfde systeemhiaten) [12]"],
                  ["Bronnotatie", "Bronvermelding in lopende tekst", "Wetenschappelijke notatie [n] met volledige bronnenlijst (hoofdstuk 8)"],
                  ["Actiekoppeling", "H1–H12", "B ↔ H 1-op-1 koppeling + G-items met concreet Rev 3-advies (hoofdstuk 6)"]],
                 widths=[3.8, 5.4, 7.0], size=9.0)
    H.h3(doc, "5.2 Nieuwe bevindingen in v3.0 (P3 — aanvullend)")
    H.body(doc, "De G-items zijn geen wettelijke hiaten, maar aanvullende verbeterpunten die de effectiviteit van de P1/P2-herstelacties versterken. Ze zijn voorgesteld voor opname in de Rev 3-implementatie.", size=10)
    for g in GAPS_NIEUW:
        gap_block(doc, g)
    H.h3(doc, "5.3 Consistentietoets binnen de 003-procedureserie")
    H.body(doc, "De parallelle JvG-audits op de Koole MSD 003-procedureserie (kwik 003.057, asbest 003.059) wijzen onafhankelijk hetzelfde systeemprobleem uit: medische surveillance (AGO), blootstellingsregister (art. 4.15) en 40-jaar bewaring ontbreken in alle drie de procedures [12]. Deze overlap bevestigt de betrouwbaarheid van de toetsingsmethodiek en rechtvaardigt een horizontale herzieningsopdracht op serieniveau: één CMR-basisparagraaf die in 003.056, 003.057 en 003.059 wordt geïmplementeerd, in plaats van drie losse herzieningstrajecten.")

    # -- 6. Conclusie en advies ----------------------------------------
    H.h1(doc, "6. Conclusie en advies")
    H.body(doc, "De operationele kern van de procedure is op orde: gevareninformatie, identificatie/marking, hygiëneregime en PBM-regime zijn conform (B7, B15–B18). De formele arbo-juridische verslagleggings- en zorgstructuur ontbreekt echter vrijwel volledig (5× P1). Zonder meetstrategie, AGO, bewaarregeling, RI&E-verankering en blootstellingsregister is de procedure in de huidige versie niet aantoonbaar conform Arbobesluit Hoofdstuk 4 Afdeling 3 [2].")
    H.h3(doc, "6.1 Spoedrevisie (P1 — vóór projectuitvoering)")
    for b in [
        "H1 — Meetstrategie conform NEN-EN 689/482 met toetsing aan 1 µg/m³, inclusief de normreferenties in §6 (B1, B13) [8]",
        "H2 — AGO-regeling en bedrijfsartsrol (B2) [2]",
        "H3 — bewaar- en overdrachtsregeling blootstellingsgegevens, 40 jaar (B3) [2][3]",
        "H5 — expliciete RI&E-koppeling met vervangings-/bronmaatregelonderbouwing (B4) [2][3]",
        "H4 — exposure register: lijst blootgestelde werknemers incl. subcontractors, met inzage- en afschriftrecht (B5) [2][3]",
    ]:
        H.bullet(doc, b)
    H.h3(doc, "6.2 Advies Rev 3-implementatie (P2 + P3)")
    H.body(doc, "JvG adviseert de P2-herstelacties (H6–H12) en de drie P3-aanvullingen (G-01 t/m G-03) niet te versnipperen over meerdere revisies, maar integraal op te nemen in één Rev 3-release, binnen twee tot vier weken na de P1-spoedrevisie. De herstelteksten zijn reeds volledig uitgewerkt in het Tekstueel Verbeteradvies v3.0 (T-01 t/m T-24) en het Herzieningsvoorstel v3.0 [11][13], waardoor de implementatie-inspanning beperkt blijft tot redactionele verwerking en goedkeuring. De Rev 3-release dient minimaal te omvatten:")
    for b in [
        "H6 — calamiteitenprotocol abnormale blootstelling incl. OR/PVT-kanaal (B6) [2]",
        "H7 — REACH Bijlage XIV-referentie met afbakening (B8) [4]",
        "H8 — uitgewerkte afvalstroombepalingen incl. zeetransport en bewijslast (B9) [5]",
        "H9 — AI-bladen 67/31/18/77 in §6 (B10) [3][10]",
        "H10 — operationele vertaling van het Beheersregime Chroom-6 (B11) [6]",
        "H11 — volledige maatregelenhiërarchie incl. substitutie-/bronmaatregeldocumentatie (B12)",
        "H12 — versieconsistentie en documentbeheer herstellen (B14)",
        "G-01 t/m G-03 — instructie/training, meetkwaliteitsborging (ISO/IEC 17025) en continue verbetercyclus",
        "Horizontale CMR-basisparagraaf op 003-serieniveau (gedeeld met 003.057 en 003.059) [12]",
    ]:
        H.bullet(doc, b)
    H.h3(doc, "6.3 Vastlegging")
    H.make_table(doc, ["Element", "Bepaling"],
                 [["Eigenaar herstelacties", "QHSE Manager Koole"],
                  ["P1-spoedrevisie", "Vóór aanvang projectuitvoering"],
                  ["Rev 3 (P2 + P3, integraal)", "2–4 weken na P1-revisie"],
                  ["Verificatie na implementatie", "Meetrapporten, AGO-aanbiedingen, exposure register (V-3)"],
                  ["Gekoppelde documenten", "Herzieningsvoorstel v3.0 · Tekstueel Verbeteradvies v3.0 · Conformiteitsmatrix v1.0 (XLSX)"]],
                 widths=[5.4, 10.8], size=9.5)
    H.body(doc, "Na implementatie van de P1- en P2/herstelacties wordt een conformiteitsniveau van 18 van 18 basiseitems (B1–B18) bereikt, behoudens praktijkverificatie van de implementatie (V-3).", size=10)

    # -- 7. Beperkingen en verificatiepunten ---------------------------
    H.h1(doc, "7. Beperkingen en verificatiepunten")
    H.body(doc, "Dit is een document-based audit op de integrale tekst van het brondocument (12 pagina's) [1]; er heeft geen veldverificatie plaatsgevonden. Verificatie is vereist voor:")
    for b in [
        "V-1 — marking- en surveypraktijk op het platform ( conformiteit van B16 in de praktijk)",
        "V-2 — feitelijke afvalafvoer en verwerkerserkenning (koppeling B9/H8)",
        "V-3 — toekomstige implementatie van H1–H5: meetrapporten, AGO-aanbiedingen en de registratielijst van blootgestelde werknemers",
    ]:
        H.bullet(doc, b)
    H.body(doc, "Het originele brondocument is als PDF beschikbaar gesteld via inbound media; de audit is uitgevoerd op de daaruit geëxtraheerde integrale tekst [1].", size=9.5)

    # -- 8. Bronnen -----------------------------------------------------
    H.h1(doc, "8. Bronnen")
    for n, bron in enumerate([
        "003.056 Chromium VI Exposure Control Procedure, Rev A (02-03-2026) / Rev 1 (15-04-2026), KOO B.V. — integrale tekst (12 p.), research/003.056_Chromium_VI_Exposure_Control_Procedure_Rev_A.pdf",
        "Arbobesluit, Hoofdstuk 4, Afdeling 3 (kankerverwekkende en mutagene stoffen): art. 4.2 lid 1 en 4, 4.2a, 4.10a, 4.10c, 4.13, 4.15, 4.21",
        "Arbo-Informatieblad 77 — Chroom-6 (SZW/SDU): grenswaarde 1 µg/m³ per 1-3-2017, Gezondheidsraad-beredenering, NEN-EN 689-toetskader, art. 4.15-lijst, 40-jaar bewaring, dossiers Defensie/ProRail/Tilburg",
        "REACH Verordening (EG) Nr. 1907/2006, Bijlage XIV — autorisatielijst chroomtrioxide en zuurchromaten",
        "Besluit activiteiten leefomgeving (Bal); Wet milieubeheer / Landelijk Afvalbeheerplan — gevaarlijk afvalregime",
        "Beheersregime Chroom-6 — Rijkswaterstaat, Defensie, ProRail, Rijksvastgoedbedrijf",
        "SER-wettelijke grenswaardenlijst — chroom VI: 1 µg/m³ (8-uurs TGG)",
        "NEN-EN 689 (meetstrategie werkplekblootstelling) en NEN-EN 482 (algemene eisen meetprocedures)",
        "Richtlijn (EU) 2017/2398 tot wijziging van richtlijn 2004/37/EG (kankerverwekkende en mutagene stoffen op het werk)",
        "Arbo-Informatiebladen 67 (kankerverwekkende stoffen), 31 (gezondheidsrisico's gevaarlijke stoffen) en 18 (persoonlijke beschermingsmiddelen)",
        "JvG Consultancy — Herzieningsvoorstel 003.056 CrVI v3.0 (H1–H15)",
        "JvG Consultancy — Vergelijkingsrapport Asbest 003.059 · Kwik 003.057 · ChroomVI 003.056 (24-08-2026)",
        "JvG Consultancy — ChroomVI Tekstueel Verbeteradvies v3.0 (T-01 t/m T-24)",
    ], start=1):
        H.body(doc, bron, bold_lead=f"[{n}]  ", size=9)

    # -- Metadata -------------------------------------------------------
    cp = doc.core_properties
    cp.title = f"{DOC_TITLE} — v{VERSION}"
    cp.subject = "Document-based compliance audit 003.056 Chromium VI Exposure Control Procedure (Koole/Wintershall)"
    cp.author = AUTHOR
    cp.last_modified_by = "JvG Consultancy"
    cp.category = "Compliance Audit"
    cp.comments = f"Versie {VERSION} — {DATUM} — artikelsgewijze toetsing B1-B18, KPI-overzicht, gap-analyse v2.0 naar v3.0"
    cp.keywords = "ChroomVI, Cr(VI), Arbobesluit, REACH, compliance audit, 003.056, Koole, Wintershall"
    cp.revision = 3
    cp.created = datetime.now(timezone.utc)

    os.makedirs(os.path.dirname(OUT), exist_ok=True)
    doc.save(OUT)
    print(f"OK — geschreven: {OUT} ({os.path.getsize(OUT)} bytes)")


if __name__ == "__main__":
    build()
