#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""District 110AN Voorjaarsconventie 2027 - Projectplan + PVA v1.0 (.docx).
Lions DOCX-standaard conform MASTER_STYLEGUIDE 2.3 / MasterSOP 18.
Alle feiten uitsluitend uit meta-dossier_v1.json (airgapped)."""
from docx import Document
from docx.shared import Pt, Cm, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml

LIONS_LOGO = "/root/projects/jg/2026-jvg-training-master/assets/branding/clients/lions-logo.png"
OUT = "/root/projects/jg/2026-voorjaarsconventie-110an/deliverables/docx/Projectplan_PVA_v1.0.docx"

PRIMARY = RGBColor(0x00, 0x33, 0x66)
ACCENT_HEX = "FFB81C"
GOLD_TXT = RGBColor(0x88, 0x6E, 0x0A)
TEXT_DARK = RGBColor(0x1F, 0x29, 0x37)
TEXT_SEC = RGBColor(0x37, 0x41, 0x51)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
CARD = "F8F9FA"

def shade(cell, hex_color):
    cell._tc.get_or_add_tcPr().append(
        parse_xml('<w:shd %s w:fill="%s"/>' % (nsdecls('w'), hex_color)))

def page_field(p):
    r = p.add_run()
    r._r.append(parse_xml('<w:fldChar %s w:fldCharType="begin"/>' % nsdecls('w')))
    r2 = p.add_run()
    r2._r.append(parse_xml('<w:instrText %s xml:space="preserve"> PAGE </w:instrText>' % nsdecls('w')))
    r3 = p.add_run()
    r3._r.append(parse_xml('<w:fldChar %s w:fldCharType="end"/>' % nsdecls('w')))

def para(doc, text, size=11, color=TEXT_DARK, bold=False, italic=False,
         after=6, before=0, align=None):
    p = doc.add_paragraph()
    r = p.add_run(text)
    r.font.size = Pt(size); r.font.color.rgb = color
    r.font.bold = bold; r.font.italic = italic; r.font.name = 'Calibri'
    p.paragraph_format.space_after = Pt(after)
    p.paragraph_format.space_before = Pt(before)
    if align: p.alignment = align
    return p

def bullet(doc, text, bold_prefix=None):
    p = doc.add_paragraph(style='List Bullet')
    if bold_prefix:
        rb = p.add_run(bold_prefix); rb.font.bold = True
        rb.font.size = Pt(11); rb.font.name = 'Calibri'; rb.font.color.rgb = TEXT_DARK
    r = p.add_run(text)
    r.font.size = Pt(11); r.font.name = 'Calibri'; r.font.color.rgb = TEXT_SEC
    p.paragraph_format.space_after = Pt(4)
    return p

def h1(doc, num, text):
    p = doc.add_paragraph()
    r = p.add_run(("%s. %s" % (num, text)) if num else text)
    r.font.size = Pt(16); r.font.bold = True; r.font.name = 'Calibri'
    r.font.color.rgb = PRIMARY
    p.paragraph_format.space_before = Pt(18); p.paragraph_format.space_after = Pt(2)
    pPr = p._p.get_or_add_pPr()
    pPr.append(parse_xml(
        '<w:pBdr %s><w:bottom w:val="single" w:sz="12" w:space="2" w:color="%s"/></w:pBdr>'
        % (nsdecls('w'), ACCENT_HEX)))
    return p

def h2(doc, text):
    p = doc.add_paragraph()
    r = p.add_run(text)
    r.font.size = Pt(13); r.font.bold = True; r.font.name = 'Calibri'
    r.font.color.rgb = PRIMARY
    p.paragraph_format.space_before = Pt(12); p.paragraph_format.space_after = Pt(4)
    return p

def table(doc, headers, rows, widths=None, font=10.5):
    t = doc.add_table(rows=1, cols=len(headers))
    t.style = 'Table Grid'; t.alignment = WD_TABLE_ALIGNMENT.CENTER
    for i, htxt in enumerate(headers):
        c = t.rows[0].cells[i]; c.text = ""
        r = c.paragraphs[0].add_run(htxt)
        r.font.bold = True; r.font.size = Pt(font); r.font.color.rgb = WHITE
        r.font.name = 'Calibri'
        shade(c, "003366")
    for ri, row in enumerate(rows):
        cells = t.add_row().cells
        for ci, val in enumerate(row):
            cells[ci].text = ""
            r = cells[ci].paragraphs[0].add_run(str(val))
            r.font.size = Pt(font); r.font.name = 'Calibri'
            r.font.color.rgb = TEXT_DARK
            if ri % 2 == 0: shade(cells[ci], CARD)
    if widths:
        for ci, w in enumerate(widths):
            for row in t.rows: row.cells[ci].width = Cm(w)
    return t

def newdoc():
    doc = Document()
    for s in doc.sections:
        s.left_margin = Cm(2.2); s.right_margin = Cm(2.2)
        s.top_margin = Cm(2.0); s.bottom_margin = Cm(2.0)
    f = doc.sections[0].footer.paragraphs[0]
    f.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = f.add_run("Lions District 110AN - Voorjaarsconventie 2027 (Projectplan & PVA)  |  pagina ")
    r.font.size = Pt(8); r.font.color.rgb = TEXT_SEC; r.font.name = 'Calibri'
    page_field(f)
    hdp = doc.sections[0].header.paragraphs[0]
    hdp.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    rh = hdp.add_run("Projectplan & PVA v1.0 - Voorjaarsconventie District 110AN, april 2027")
    rh.font.size = Pt(8); rh.font.color.rgb = TEXT_SEC; rh.font.name = 'Calibri'; rh.font.italic = True
    return doc

doc = newdoc()

# ============================ COVER ============================
for _ in range(4): doc.add_paragraph()
para(doc, "LIONS DISTRICT 110 AN NEDERLAND", 14, PRIMARY, bold=True,
     align=WD_ALIGN_PARAGRAPH.CENTER, after=2)
try:
    doc.add_picture(LIONS_LOGO, width=Inches(2.2))
    doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.CENTER
except Exception:
    pass
para(doc, "PROJECTPLAN & PROJECTVERANTWOORDELIJKHEIDSAFSPRAKEN (PVA)", 12, GOLD_TXT, bold=True,
     align=WD_ALIGN_PARAGRAPH.CENTER, after=4, before=10)
para(doc, "Voorjaarsconventie 2027", 26, PRIMARY, bold=True, align=WD_ALIGN_PARAGRAPH.CENTER, after=6)
para(doc, "Meerdaagse districtsconventie en algemene ledenvergadering van District 110AN",
     13, TEXT_SEC, align=WD_ALIGN_PARAGRAPH.CENTER, after=18)
para(doc, "Lions-jaar 2026-2027  |  Thema: \u201cSamen verder bouwen!\u201d", 12,
     TEXT_SEC, bold=True, align=WD_ALIGN_PARAGRAPH.CENTER, after=2)
para(doc, "April 2027 (definitieve datum nader vast te stellen)  |  Locatie en hostclub nader vast te stellen",
     11, TEXT_SEC, align=WD_ALIGN_PARAGRAPH.CENTER, after=24)
para(doc, "Opgesteld door JvG Consultancy namens het Districtskabinet 110AN", 10,
     TEXT_SEC, italic=True, align=WD_ALIGN_PARAGRAPH.CENTER)
doc.add_page_break()

# ============================ DOCUMENTBEHEER ============================
h1(doc, None, "Documentbeheer")
table(doc, ["Kenmerk", "Waarde"], [
    ("Document", "Projectplan & PVA - Voorjaarsconventie District 110AN 2027"),
    ("Versie", "1.0 (Concept)"),
    ("Datum", "26 augustus 2026"),
    ("Opsteller", "JvG Consultancy (agent OR_02 Project Manager), namens het Districtskabinet 110AN"),
    ("Eigenaar", "Districtsgouverneur 2026-2027, District 110AN"),
    ("Status", "Concept - ter vaststelling door Dagelijks Bestuur / Districtskabinet"),
    ("Bronnen", "Meta-dossier v1 (11 bronbestanden, geconsolideerd conform bronnenprotocol)"),
    ("Herzieningsritme", "Na elke beslismijlpaal (QC-gate); definitieve versie na vaststelling datum, locatie en hostclub"),
], widths=[4.5, 11.5])
para(doc, "Dit projectplan combineert de vaste negen-hoofdstukkenstructuur voor "
     "Lions-projectaanpakken met een volledige PVA-matrix (RACI). Alle feitelijke "
     "uitspraken zijn voorzien van bronverwijzingen [n]; de bronnenlijst staat in "
     "Bijlage A. Feiten zijn uitsluitend ontleend aan het geconsolideerde meta-dossier "
     "van dit project.", 9.5, TEXT_SEC, italic=True, before=6)
doc.add_page_break()

# ============================ INHOUDSOPGAVE ============================
h1(doc, None, "Inhoudsopgave")
for num, titel in [
    ("1", "Inleiding en Achtergrond"),
    ("2", "Doelstelling en Scope"),
    ("3", "Organisatie, Rollen en PVA-Matrix (RACI)"),
    ("4", "Fasering en Tijdlijn - Kritieke Pad"),
    ("5", "Planning en Mijlpalen"),
    ("6", "Begroting en Budgetraming"),
    ("7", "Risicoregister"),
    ("8", "Communicatieplan"),
    ("9", "Kwaliteitsborging en Acceptatiecriteria"),
    ("10", "Governance-Compliance: Stemrechten, Quorum en Conventieregels"),
    ("11", "Goedkeuring"),
    ("A", "Bijlage A - Bronnenlijst"),
    ("B", "Bijlage B - Controleset Feiten (consistentiecheck)"),
]:
    para(doc, "%s  %s" % (num, titel), 11.5, TEXT_DARK if "." not in num else TEXT_SEC,
         bold="." not in num, after=3)
doc.add_page_break()

# ============================ 1 INLEIDING ============================
h1(doc, 1, "Inleiding en Achtergrond")
para(doc, "Lions District 110AN - in eigen woorden \u201chet kleinste maar meest "
     "vooruitstrevende district van MD 110\u201d [7] - organiseert jaarlijks een "
     "voorjaarsconventie. Deze conventie is de algemene ledenvergadering van het "
     "district en heeft een formeel karakter vanwege de stemmingen die er plaatsvinden; "
     "tegelijkertijd is het h\u00e9t ontmoetingsmoment tussen kabinetsleden en clubleden [1].")
para(doc, "Sinds fiscal year 2022-2023 wordt de voorjaarsconventie van District 110AN "
     "georganiseerd door een club uit het eigen district en heeft zij een meerdaags "
     "karakter. Na LC Texel, LC Den Helder en LC Haarlem Over den Duin viel de eer in "
     "FY 2025-2026 toe aan LC Bussum Godelinde [6]. Voor de editie van april 2027 "
     "(Lions-jaar 2026-2027) dienen de organiserende club (hostclub) en locatie nog "
     "vastgesteld te worden; dat is het eerste beslismijlpaal van dit projectplan.")
para(doc, "De districtsconventies moeten minimaal zes weken v\u00f3\u00f3r de nationale "
     "conventies worden gehouden, zodat punten die aan de orde komen nog tijdig "
     "(minimaal 30 dagen tevoren) kunnen worden ingebracht voor de nationale conventie [1]. "
     "De nationale voorjaarsconventie staat in de districtskalender van 110AN gepland op "
     "30 mei 2027 [11]; een districtsconventie in april 2027 voldoet aan deze "
     "termijneis. Voor het Lions-jaar 2026-2027 is in het werkplan de \u201cmeerdaagse "
     "voorjaarsconventie\u201d als vaste activiteit opgenomen [7].")
para(doc, "Dit document bundelt het projectplan en de Projectverantwoordelijkheidsafspraken "
     "(PVA). De PVA is uitgewerkt als RACI-matrix (hoofdstuk 3) en vormt samen met het "
     "risicoregister (hoofdstuk 7) en de kwaliteitsborging (hoofdstuk 9) het "
     "besturingskader van het project.", before=2)

# ============================ 2 DOELSTELLING & SCOPE ============================
h1(doc, 2, "Doelstelling en Scope")
h2(doc, "2.1 Projectdoelstellingen")
bullet(doc, "het statutair correct organiseren van de algemene ledenvergadering van "
       "District 110AN in april 2027, inclusief alle vereiste stemmingen en benoemingen: "
       "voortgangsverhaal van de zittende gouverneur, benoeming van de inkomende 2e VDG, "
       "1e VDG en DG, en presentatie van het werkplan en de begroting van het komende "
       "Lions-jaar [1][7].",
       "Governance - ")
bullet(doc, "een aantrekkelijke, meerdaagse conventie bieden die de "
       "ledenbetrokkenheid vergroot: een aansprekende locatie (eventueel met rondleiding), "
       "een aansprekende spreker of actueel onderwerp, goede catering, aankleding, "
       "muzikale ondersteuning en met name een open, persoonlijke en warme ontvangst [1].",
       "Ledenbetrokkenheid - ")
bullet(doc, "een financieel verantwoorde editie binnen de kaders van de "
       "districtsbegroting (referentiepost \u201c1 Districtsconventie\u201d: \u20ac 3.500) [10], "
       "waar passend aangevuld met eigen bijdragen van deelnemers [1].",
       "Financieel - ")
bullet(doc, "het borgen van lessen en afspraken in een jaaroverdracht, zodat de editie "
       "2028 voortbouwt op beproefde werkwijzen [6].",
       "Continuiteit - ")
h2(doc, "2.2 Scope")
table(doc, ["Aspect", "Binnen scope", "Buiten scope"], [
    ("Evenement", "Meerdaagse voorjaarsconventie District 110AN, april 2027 (algemene ledenvergadering met formeel vergaderdeel) [1][6][7]",
     "Nationale Voorjaarsconventie op 30 mei 2027 (organisatie volgens GR-roulatie in FY 2026-2027 bij district 110AZ) [2][11]"),
    ("Deelnemers", "Lions-leden, bestuurders en partners/gasten uit District 110AN [1][6]",
     "Internationale gastenprogramma's van de nationale voorjaarsconventie [1]"),
    ("Programma", "Formeel vergaderdeel + informeel programma (sprekers, activiteiten, ontmoeting) [1]",
     "Losse clubactiviteiten buiten het conventieprogramma"),
    ("Vergaderstukken", "Agenda, notulen, werkplan, begroting en kandidaatstellingen - conform de 30-dagenregel [1][3]",
     "Financiele verantwoording + decharge en kascommissieverslag (behorend bij de najaarsbijeenkomst) [1][5]"),
    ("Financien", "Districtsbijdrage conform begrotingspost + deelnemersbijdragen [1][10]",
     "Inhoudelijke vaststelling van de begroting FY 2027-2028 (DG/DP-traject) [7]"),
], widths=[2.8, 7.0, 6.2], font=9.5)
h2(doc, "2.3 Succescriteria")
bullet(doc, "Alle stemmingen zijn geldig uitgevoerd; het quorum (voor de meeste "
       "artikelen meer dan 50% van de stemgerechtigden) is gehaald [3].")
bullet(doc, "Agenda en vergaderstukken zijn uiterlijk 30 dagen v\u00f3\u00f3r de conventie "
       "voor de leden beschikbaar (statutaire verplichting) [1].")
bullet(doc, "De deelnemersopkomst is hoger dan bij de editie 2025-2026, gemeten via de "
       "registratie en roll-call van de districtssecretaris [1].")
bullet(doc, "Positieve afsluiting: de leden gaan met een positief gevoel naar huis [1].")
bullet(doc, "Financiele eindafrekening binnen het vastgestelde budgetkader [10].")
bullet(doc, "Conclusieverslag en jaaroverdracht zijn binnen vier weken na de conventie "
       "opgeleverd [5][6].")

# ============================ 3 ORGANISATIE, ROLLEN & PVA ============================
h1(doc, 3, "Organisatie, Rollen en PVA-Matrix (RACI)")
h2(doc, "3.1 Organisatiestructuur")
para(doc, "De voorjaarsconventie wordt georganiseerd onder verantwoordelijkheid van het "
     "Dagelijks Bestuur (DG, 1e VDG, 2e VDG, districtssecretaris en "
     "districtspenningmeester) van District 110AN, in samenwerking met de hostclub en "
     "het daaruit voortkomende organiserend comit\u00e9. De districtssecretaris is de "
     "centrale regelaar: verantwoordelijk voor het verzamelen van alle stukken, de "
     "uitnodiging via de vergadermodule in het CMS, de registratie van "
     "(stemgerechtigde) deelnemers, de roll-call en de badges [1]. Binnen het kabinet "
     "2026-2027 is een commissaris \u201cVoorjaarsconventie\u201d benoemd [7]; in FY 2025-2026 "
     "was dat Pieter Bas Boertje (LC Bussum Godelinde) [6]. De portefeuille "
     "voorjaarsconventie ligt volgens de DB-afspraken gezamenlijk bij DG, VDG en DP [6].")
table(doc, ["Rol", "Functie/beschrijving", "Bron"], [
    ("Districtsgouverneur (DG) 2026-2027", "Voorzitter DB/kabinet; eindverantwoordelijke; stelt begroting op aan de hand van werkplan en speerpunten, afgestemd met de districtspenningmeester; keuze hostclub en locatie-afspraken", "[1][3][7]"),
    ("1e Vice-Districtsgouverneur (VDG)", "Co-portefeuillehouder voorjaarsconventie; toezicht zonevoorzitters (zonevoorzitters vallen onder de 1e VDG)", "[6]"),
    ("2e Vice-Districtsgouverneur (2e VDG)", "Co-organisatie; ondersteunt programma en activiteiten", "[6][7]"),
    ("Districtssecretaris", "Centrale regelaar: stukken, uitnodiging (CMS-vergadermodule), registratie stemgerechtigden, roll-call, badges, stembiljetten; lid stembureau", "[1][3]"),
    ("Districtspenningmeester (DP)", "Co-portefeuillehouder; budgetbewaking en financiele verantwoording; kascontrolecommissie valt onder de DP", "[1][6]"),
    ("Directe Voorzitter Districtsgouverneur (IPDG)", "Gevestigde functie in het kabinet; o.a. LCIF-coordinatie en stichting Lions Helpen", "[6][7]"),
    ("Commissaris Voorjaarsconventie (kabinet 2026-2027)", "Verbindende schakel tussen kabinet en hostclub; naam in werkplan 2026-2027 (draft) nog in te vullen", "[7]"),
    ("Hostclub + organiserend comite", "Organisatie locatie, programma, catering, aankleding, logistiek en activiteiten voor deelnemers en partners", "[1][6]"),
    ("Zonevoorzitters (7 zones: 1A, 1B, 2A, 2B, 2C, 3A, 3B)", "Linking pins tussen clubs en district; mobiliseren clubs voor opkomst en aanwijzing stemgerechtigden", "[6]"),
    ("Kascontrolecommissie", "Door de districtsconventie benoemd; geen kabinetsleden; onderzoekt jaarstukken en rapporteert schriftelijk aan de conventie", "[3][6]"),
    ("Lionsclubs in District 110AN (+/- 50 clubs)", "Deelnemers; wijzen tijdig hun stemgerechtigde afgevaardigde(n) aan", "[3][4]"),
    ("Lions Media (voorheen CCM)", "Alle media-uitingen: website, LION magazine, social media; promotie conventie", "[6]"),
], widths=[4.2, 9.4, 2.4], font=9.5)
h2(doc, "3.2 PVA-Matrix (RACI)")
para(doc, "R = Responsible (uitvoerend verantwoordelijk) | A = Accountable "
     "(eindverantwoordelijk) | C = Consulted (gehoord) | I = Informed (ge\u00efnformeerd). "
     "DB = Dagelijks Bestuur.", 9.5, TEXT_SEC, italic=True, after=8)
table(doc, ["Activiteit", "DG", "VDG/2VDG", "Secr.", "DP", "Hostclub", "Zvz", "Clubs", "Lions Media"], [
    ("1. Definitieve datum vaststellen (geen vakantie; nooit verplaatsen na communicatie) [1]", "A", "C", "R", "I", "C", "I", "I", "I"),
    ("2. Hostclub aanwijzen en organiserend comite instellen [1][6]", "A/R", "C", "C", "C", "R", "I", "I", "I"),
    ("3. Locatie selecteren, bezoeken en afstemmen met uitbater [1]", "A", "C", "C", "C", "R", "I", "I", "I"),
    ("4. Programma, sprekers en activiteiten samenstellen [1]", "A", "C", "C", "C", "R", "C", "I", "C"),
    ("5. Agenda + vergaderstukken opstellen en 30 dagen vooraf publiceren (site, uitnodiging, ownCloud) [1][3]", "A", "C", "R", "C", "C", "I", "I", "I"),
    ("6. Kandidaatstellingen verzamelen (uiterlijk 30 dagen vooraf; enkele kandidaat per functie) [3]", "A", "R", "C", "I", "-", "I", "C", "-"),
    ("7. Uitnodiging versturen via CMS-vergadermodule + aanmelding beheren (48-uursregel) [1][3]", "A", "I", "R", "I", "C", "C", "R", "C"),
    ("8. Clubs wijzen stemgerechtigde afgevaardigde aan (24-uursregel) [3]", "I", "I", "C", "-", "-", "R", "A/R", "-"),
    ("9. Communicatie & PR (website, social media, LION, zonebijeenkomsten) [1][6]", "A", "C", "C", "I", "C", "R", "I", "R"),
    ("10. Budget vaststellen, bewaken en financiele stukken beschikbaar stellen [1][10]", "A", "C", "I", "R", "C", "I", "I", "-"),
    ("11. Registratie, badges, stemkaarten en stembiljetten; stembureau [1][3]", "A", "C", "R", "I", "C", "-", "I", "-"),
    ("12. Catering, aankleding, techniek en routing op de dag [1]", "A", "C", "C", "C", "R", "-", "I", "I"),
    ("13. Vergaderleiding, roll-call en stemmingen [1][3]", "A/R", "C", "R", "I", "I", "-", "I", "-"),
    ("14. Verslag, financiele afrekening en evaluatie na afloop [1][5]", "A", "I", "R", "R", "C", "I", "I", "C"),
    ("15. Jaaroverdracht voor editie 2028 [6]", "C", "I", "C", "I", "R", "I", "I", "I"),
], widths=[5.6, 1.1, 1.3, 1.1, 1.0, 1.4, 1.0, 1.2, 1.5], font=8.5)
para(doc, "Toelichting: de algemene ledenvergadering (de conventie zelf) blijft te allen "
     "tijde het besluitvormende orgaan; de DG is voorzitter van vergadering en kabinet [6][7]. "
     "De kascontrolecommissie is bij dit project betrokken als gevraagd en ongevraagd "
     "adviesorgaan voor de jaarstukken en wordt door de conventie benoemd [3][6].",
     9.5, TEXT_SEC, italic=True, before=6)

# ============================ 4 FASERING & TIJDLIJN ============================
h1(doc, 4, "Fasering en Tijdlijn - Kritieke Pad")
para(doc, "Het project doorloopt zes fasen, gerekend vanaf de projectstart (september "
     "2026) tot en met de afronding (mei 2027). Referentiepunt is de eerder vastgestelde "
     "kalender van het district: in de werkkalender 2026-2027 is reeds een voorlopige "
     "reservering \u201cVoorjaarsconventie??\u201d opgenomen voor 19-20 maart 2027 [11]; de "
     "projectopdracht gaat uit van april 2027 [B]. Vaststelling van de definitieve datum "
     "in Fase 0 lost deze openstaande keuze op. De fasering sluit aan op de praktijk van "
     "eerdere edities: de editie 2025-2026 stond in april gepland bij LC Bussum "
     "Godelinde [6].")
table(doc, ["Fase", "Periode (indicatief)", "Kernactiviteiten", "Output/resultaat"], [
    ("Fase 0 - Initiatie", "september 2026",
     "Definitieve datum vaststellen; hostclub aanwijzen; organiserend comite instellen; projectplan & PVA vaststellen door DB/kabinet",
     "Vastgesteld projectplan + PVA; datum + hostclub bekend"),
    ("Fase 1 - Voorontwerp", "oktober-november 2026",
     "Longlist locaties; locatiebezoeken; afstemming programma-ideeen met kabinet en zonevoorzitters; begrotingskader opstellen",
     "Locatie-advies; concept-programma; budgetkader"),
    ("Fase 2 - Ontwerp & besluitvorming", "december 2026",
     "Locatie keuze + afspraken met uitbater (routing, parkeren, geluid, catering, afgesloten zaal); sprekers benaderen; communicatieplan gereed",
     "Definitieve locatie + programma-outline; QC-gate 2 (DB-goedkeuring)"),
    ("Fase 3 - Realisatie", "januari-maart 2027",
     "Uitnodiging via CMS-vergadermodule (T-8 weken); aanmeldingen registreren; vergaderstukken en kandidaatstellingen verzamelen; publicatie stukken uiterlijk T-30 dagen; PR-offensief via website, social media, LION en zonevoorzitters",
     "Uitnodiging verstuurd; stukken compleet + gepubliceerd (T-30); inschrijvingen lopen"),
    ("Fase 4 - Uitvoering", "april 2027",
     "Conventiedagen: opbouw en techniektest (DB 1-2 uur vooraf aanwezig); registratie vanaf 1 uur voor opening; opening, roll-call, formeel vergaderdeel met stemmingen en benoemingen; informeel programma; afsluiting",
     "Succesvolle conventie; geldige stemmingen; verslagbasis"),
    ("Fase 5 - Afronding & overdracht", "april-mei 2027",
     "Conclusieverslag (referentie: verslag vorige editie binnen ca. 2 weken) [5]; financiele afrekening met DP; evaluatie met hostclub en kabinet; jaaroverdracht editie 2028",
     "Verslag; eindafrekening; evaluatierapport; jaaroverdracht"),
], widths=[3.0, 3.0, 6.4, 3.6], font=9)
h2(doc, "4.1 Kritieke pad")
para(doc, "Het kritieke pad loopt via vijf knelpunten. Elke vertraging op deze keten "
     "raakt de conventiedatum direct:")
bullet(doc, "Datum- en hostclubvaststelling (Fase 0) -> alle locatie-activiteiten hangen hieraan; "
       "een eenmaal vastgestelde en gecommuniceerde datum wordt nooit verplaatst [1].")
bullet(doc, "Locatie & contract (Fase 2) -> catering, routing, geluid en zaal zijn "
       "bepalend voor programma en budget [1].")
bullet(doc, "Vergaderstukken & kandidaatstellingen (T-30 dagen) -> statutaire "
       "termijn; schriftelijke kandidaatstelling door de districtsconventie vindt plaats "
       "uiterlijk dertig dagen voor aanvang [1][3].")
bullet(doc, "Aanmeldingen & aanwijzing stemgerechtigden (T-48 uur / T-24 uur) -> "
       "aanmelding uiterlijk 48 uur voor aanvang om als stemgerechtigde geregistreerd te "
       "kunnen worden; club meldt uiterlijk 24 uur voor aanvang wie stemgerechtigd is, "
       "anders is de club niet stemgerechtigd [3].")
bullet(doc, "De conventiedagen zelf (april 2027) -> inclusief 6-wekenrule t.o.v. de "
       "nationale conventie van 30 mei 2027 [1][11].")

# ============================ 5 PLANNING & MIJLPALEN ============================
h1(doc, 5, "Planning en Mijlpalen")
table(doc, ["Mijlpaal", "Moment", "Eigenaar (R)", "Beslispunt/criterium"], [
    ("M0 - Projectstart: projectplan & PVA vastgesteld", "september 2026", "DG / DB", "Kabinet accordeert plan; hostclub en datum definitief"),
    ("M1 - Locatie definitief + afspraken met uitbater", "uiterlijk 31-12-2026", "Hostclub / Commissaris VC", "Zaalgarantie, routing, parkeren, catering, geluid en afgesloten ruimte contractueel geregeld [1]"),
    ("M2 - Programma & sprekers definitief", "januari 2027", "Hostclub / Commissaris VC", "Inhoudelijk goedgekeurd door DG; sprekers tijdsloten en verwachtingen afgestemd (no surprises) [1]"),
    ("M3 - Uitnodiging verstuurd via CMS", "uiterlijk 6 weken vooraf", "Districtssecretaris", "Alle leden bereikt; aanmeldlink actief [1][3]"),
    ("M4 - Agenda + vergaderstukken gepubliceerd", "uiterlijk 30 dagen vooraf (statutair)", "Districtssecretaris", "Stukken beschikbaar via site, uitnodiging en ownCloud [1]"),
    ("M5 - Kandidaatstellingen compleet", "uiterlijk 30 dagen vooraf", "DB (VDG)", "Schriftelijke kandidaatstellingen rondgezonden [3]"),
    ("M6 - Sluiting aanmeldingen stemgerechtigden", "T-48 uur", "Districtssecretaris", "Registratielijst definitief [3]"),
    ("M7 - Aanwijzing stemgerechtigden door clubs", "T-24 uur", "Clubs / zonevoorzitters", "Alle deelnemende clubs hebben afgevaardigde gemeld [3]"),
    ("M8 - Conventie", "april 2027", "DG", "Quorum gehaald; stemmingen en benoemingen geldig [3]"),
    ("M9 - Verslag opgeleverd", "binnen 2-4 weken na conventie", "Districtssecretaris", "Conclusieverslag conform format eerdere editie [5]"),
    ("M10 - Eindafrekening & evaluatie", "mei 2027", "DP / Hostclub", "Binnen budgetkader; evaluatie en jaaroverdracht vastgesteld [10][6]"),
], widths=[4.6, 3.0, 3.0, 5.4], font=9)

# ============================ 6 BEGROTING ============================
h1(doc, 6, "Begroting en Budgetraming")
h2(doc, "6.1 Referentiekader uit de districtsbegroting")
para(doc, "De Districtbegroting 110AN 2025-2026 kent 1.200 leden (contributie "
     "\u20ac 20 per lid), begrote inkomsten van \u20ac 24.500 en uitgaven van \u20ac 29.500 "
     "(saldo \u20ac 5.000 negatief; in het verlengde van het beleid om gemiddeld "
     "\u20ac 5.000 per jaar te investeren vanuit het eigen vermogen) [6][10]. "
     "De begrotingspost voor dit project is \u201cDistrict - 1 Districtsconventie: "
     "\u20ac 3.500\u201d [10]. Ter vergelijking: de Ledenontmoetings- en inspiratieavond is "
     "begroot op \u20ac 3.000 en de MJF/PDG-stamppotavond op \u20ac 2.500 [10].")
h2(doc, "6.2 Budgetraming Voorjaarsconventie 2027 (indicatief)")
para(doc, "De onderstaande raming is een plafondkader op basis van de "
     "referentiebegrotingspost en de meerdaagse opzet. De definitieve begroting wordt in "
     "Fase 2 vastgesteld, nadat locatie en programma bekend zijn. Voor het "
     "activiteitenprogramma wordt altijd een eigen bijdrage van deelnemers gevraagd [1]. "
     "Alle bedragen zijn indicatief en uitsluitend gebaseerd op de referentiepost uit de "
     "districtsbegroting [10]; zij vormen geen feitelijk offerte-gebonden raming.",
     10, TEXT_SEC, italic=True)
table(doc, ["Post", "Raming", "Dekking", "Toelichting"], [
    ("Locatie, zalen en opbouw", "\u20ac 2.500", "Hostclub / deelnemersbijdrage", "Meerdaagse opzet; afgesloten vergaderruimte verplicht [1]"),
    ("Catering (dagdelen, diner/lunch onder voorbehoud)", "\u20ac 2.500", "Deelnemersbijdrage + district", "Goede catering bevordert opkomst [1]; gratis/koop-belijning vooraf duidelijk maken [1]"),
    ("Techniek, geluid en aankleding", "\u20ac 1.000", "District", "Geluidsinstallatie inventariseren en testen; vlaggen en standaarden [1]"),
    ("Sprekers en programma", "\u20ac 750", "District / hostclub", "Aansprekende spreker of actueel onderwerp [1]"),
    ("Communicatie en drukwerk", "\u20ac 500", "District", "Uitnodiging via CMS; actieve promotie website/social/LION [1][6]"),
    ("Badges, registratie en stembiljetten", "\u20ac 250", "District", "Naam + clubnaam op badges; stemkaarten en stembiljetten [1][3]"),
    ("Onvoorzien (ca. 10%)", "\u20ac 700", "District", "Buffer conform prudent begroten"),
    ("Totaal brutokostprijs (indicatief)", "\u20ac 8.200", "-", "-"),
    ("Waarvan districtsbijdrage conform referentiepost", "\u20ac 3.500", "Districtbegroting", "Post \u201c1 Districtsconventie\u201d [10]"),
    ("Waarvan te dekken uit deelnemersbijdragen/hostclub", "\u20ac 4.700 (indicatief)", "Deelnemers / hostclub", "Eigen bijdrage activiteitenprogramma gebruikelijk [1]"),
], widths=[5.0, 2.6, 3.6, 4.8], font=9)
para(doc, "Financiele beheersing: de DP bewaakt het budget en stemt begroting en "
     "mutaties af met het DB; de inkomstenkant (aanmeldingen/deelnemersbijdragen) wordt "
     "wekelijks gemonitord vanaf Fase 3 [6][10]. Bij dreigende onderdekking geldt "
     "ingeschaald programma als mitigatie (zie risico R6).", 10, TEXT_SEC, before=4)

# ============================ 7 RISICOREGISTER ============================
h1(doc, 7, "Risicoregister")
para(doc, "Het risicoregister bevat twaalf risico's, gescoord op impact (H/M/L) en "
     "kans (H/M/L) met maatregelen en eigenaren conform de PVA-matrix. Praktijkreferentie: "
     "bij de districtsconventie/Leden Ontmoetingsavond van 17 oktober 2024 waren 66 "
     "aanwezigen uit 20 clubs, samen 42 stemmen [5].", 10, TEXT_SEC, italic=True)
table(doc, ["#", "Risico", "I", "K", "Maatregelen", "Eigenaar"], [
    ("R1", "Quorum wordt niet gehaald (>50% van de stemgerechtigden voor de meeste artikelen vereist) [3]", "H", "M",
     "Vroege en herhaalde oproepen via zonevoorzitters; 48-uurs- en 24-uursregels expliciet communiceren; roll-call vooraf monitoren; praktijkreferentie 42 stemmen [5] als streefbenchmark", "DG + Secr."),
    ("R2", "Hostclub valt weg of heeft onvoldoende capaciteit", "H", "M",
     "Vroege aanwijzing in Fase 0; back-upoptie uit de historische ronde (Texel, Den Helder, Haarlem Over den Duin, Bussum Godelinde) [6]; organiserend comite met duidelijke rolverdeling", "DG"),
    ("R3", "Vergaderstukken niet tijdig gepubliceerd (30-dagenregel is statutaire verplichting) [1][3]", "H", "M",
     "Mijlpaal M4 met harde deadline; districtssecretaris als centrale regelaar; wekelijkse status in Fase 3", "Secr."),
    ("R4", "Stemgerechtigden niet (tijdig) aangemeld of aangewezen: club verliest stemrecht (48-uur-/24-uursregels) [3]", "M", "M",
     "Regels opnemen in elke communicatie; zonevoorzitters activeren clubs; herinnering T-72 uur", "Secr. + Zvz"),
    ("R5", "Kandidaatstellingen niet uiterlijk 30 dagen vooraf rond [3]", "H", "L",
     "Vroege kandidaatprocedure via DB; enkele kandidaat per functie; rondzending conform reglement", "DB/VDG"),
    ("R6", "Budget wordt overschreden of deelnemersbijdragen vallen tegen (lage opkomst)", "M", "M",
     "Budgetplafond vastgesteld in Fase 2; weekmonitor aanmeldingen; ingeschaald programma als fallback; buffer 10%", "DP"),
    ("R7", "Locatie of techniek tekortschietend (geluid, routing, geen afgesloten ruimte, keukenlawaai) [1]", "M", "M",
     "Locatiebezoek vooraf; geluid dag vooraf testen; routing afstemmen met cateraar; extra banners bij andere gezelschappen", "Hostclub"),
    ("R8", "Lage opkomst ondanks uitnodiging (herhalingsmail alleen is niet voldoende) [1]", "M", "M",
     "Multikanaalspromotie: website, social media, zonevoorzitters, LION magazine; aantrekkelijk programma als trekker [1][6]", "Lions Media + Zvz"),
    ("R9", "Datumconflict met vakantie of andere (districts)activiteiten [1]", "M", "M",
     "Datumcheck in Fase 0 tegen districts- en nationale kalender [11]; datum na communicatie nooit verplaatsen", "DG"),
    ("R10", "Programma loopt uit of sprekers verrassen (geen tijdafstemming)", "M", "M",
     "Tijdsloten en inhoud vooraf afspreken met sprekers (no surprises) [1]; apart agendapunt voor tellen stembiljetten [1][3]", "Hostclub"),
    ("R11", "Uitval DB-lid of sleutelrol op de dag zelf", "M", "L",
     "Volledige DB-aanwezigheid verplicht; bij verhindering bij opening melden met reden [1]; plaatsvervangers aanwijzen voor stembureau en techniek", "DG"),
    ("R12", "Wensen leden onvoldoende meegenomen: hybride deelname, OV-bereikbaarheid, vegetarisch aanbod, minder papier [1]", "L", "M",
     "Vrapunt opnemen in aanmelding; locatiekeuze wegen op OV; digitaal eerst-beleid bij stukken", "Hostclub + Secr."),
], widths=[0.9, 5.2, 0.8, 0.8, 6.0, 2.2], font=8.5)

# ============================ 8 COMMUNICATIEPLAN ============================
h1(doc, 8, "Communicatieplan")
para(doc, "Uitgangspunt: het uitsluitend uitnodigen via een inspirerende "
     "(herhalings)mail naar alle leden is niet voldoende; ook op de website en social "
     "media en via de zonevoorzitters moet het moment actief worden aangeprezen [1].")
h2(doc, "8.1 Interne communicatie (district 110AN)")
table(doc, ["Moment", "Kanaal", "Boodschap", "Zender"], [
    ("Aankondiging (Fase 0/1)", "Kabinets- en zonebijeenkomsten, website", "Datum + hostclub + thema's van het jaar [6][7]", "DG / zonevoorzitters"),
    ("T-10 weken", "Website + social media", "Save-the-date + eerste programma-outline", "Lions Media"),
    ("T-8 weken", "Uitnodiging via CMS-vergadermodule [1]", "Officiele uitnodiging incl. aanmeldlink en 48-uursregel [3]", "Districtssecretaris"),
    ("T-6 weken", "LION magazine / districtskanalen [6]", "Aandachtsartikel conventie", "Lions Media"),
    ("T-4 weken", "Zonebijeenkomsten + directe clubbrief", "Activeringsoproep: meld je aan; wijs je stemgerechtigde aan", "Zonevoorzitters"),
    ("T-30 dagen (verplicht)", "Site + uitnodiging + ownCloud [1]", "Agenda en vergaderstukken gepubliceerd (statutair)", "Districtssecretaris"),
    ("T-72 uur", "E-mailherinnering", "Laatste kans aanmelden (48-uursregel) + aanwijzing afgevaardigde (24-uursregel) [3]", "Districtssecretaris"),
    ("Dag zelf", "Entree locatie", "Registratie, badges naam + clubnaam, roll-call [1][3]", "Districtssecretaris + hostclub"),
    ("Na afloop", "Website + social media + verslag", "Terugblik met fotografie [1]; conclusieverslag binnen 2-4 weken [5]", "Lions Media + secretaris"),
], widths=[2.8, 3.6, 6.0, 3.6], font=9)
h2(doc, "8.2 Externe communicatie")
para(doc, "Externe zichtbaarheid is ondergeschikt aan het interne karakter van de "
     "districtsconventie. Afgesproken wordt wie wat plaatst voor de LION, op de website "
     "en op social media [1]. De hostclub stemt lokale PR af met het DB. Externe "
     "presentaties vinden buiten de formele vergadering plaats [1].")

# ============================ 9 KWALITEITSBORGING ============================
h1(doc, 9, "Kwaliteitsborging en Acceptatiecriteria")
h2(doc, "9.1 QC-gates")
table(doc, ["Gate", "Moment", "Toetsingscriteria", "Beslist door"], [
    ("QG1 - Start", "september 2026", "Projectplan & PVA vastgesteld; datum en hostclub definitief; datumcheck vakantie/kalender gedaan [1][11]", "DB / kabinet"),
    ("QG2 - Ontwerp", "31-12-2026", "Locatie + programma + budgetkader goedgekeurd; afspraken met uitbater schriftelijk [1]", "DG + DP"),
    ("QG3 - Stukken", "T-30 dagen", "Agenda en alle vergaderstukken compleet en gepubliceerd [1][3]; kandidaatstellingen rond [3]", "DG + secretaris"),
    ("QG4 - Techniek & generalisme", "Dag vooraf", "Locatiebezoek afgerond; geluid getest; routing met cateraar afgestemd; presentaties ingeleverd (uiterlijk 1 week vooraf) en op laptop aanwezig [1]", "Hostclub + secretaris"),
    ("QG5 - Uitvoering", "Conventiedag", "DB 1-2 uur vooraf aanwezig; registratie vanaf 1 uur voor opening [1][3]; roll-call en quorum vastgesteld [3]; vaste agendapunten: welkom, roll-call, rondvraag [1]", "DG"),
    ("QG6 - Afsluiting", "binnen 4 weken na", "Verslag, eindafrekening en evaluatie opgeleverd; jaaroverdracht vastgesteld [5][6]", "DB"),
], widths=[2.4, 2.6, 8.0, 3.0], font=9)
h2(doc, "9.2 Acceptatiecriteria (definitieve oplevering)")
bullet(doc, "Quorum gehaald en alle stemmingen geldig uitgevoerd [3].")
bullet(doc, "Vergaderstukken waren uiterlijk 30 dagen vooraf beschikbaar (site, "
       "uitnodiging, ownCloud) [1].")
bullet(doc, "Benoemingen inkomende 2e VDG, 1e VDG en DG zijn conform reglement "
       "totstandgekomen (kandidaatstelling uiterlijk 30 dagen vooraf; enkele kandidaat "
       "per functie; max. 5 minuten toelichting per kandidaat) [1][3].")
bullet(doc, "Herdenking van overledenen en presentatie werkplan/begroting inkomend "
       "bestuur zijn op de agenda opgenomen [1].")
bullet(doc, "Eindafrekening binnen het budgetkader; deelnemersbijdragen "
       "verantwoord [10].")
bullet(doc, "Conclusieverslag binnen 2-4 weken opgeleverd [5]; evaluatie en "
       "jaaroverdracht vastgesteld [6].")
h2(doc, "9.3 Lessen uit de vorige editie")
para(doc, "Het verslag van de districtsconventie/Leden Ontmoetingsavond van 17 "
     "oktober 2024 (Stadscaffee Laurens, Kerkade): 66 aanwezigen uit 20 clubs met "
     "samen 42 stemmen; stembureau bemand door Joost Paijmans (LC Lisse De Oude "
     "Duinen) en Esmee Bon-Duijn (districtssecretaris); ingezonden stukken: "
     "notulen mei 2024, begrotingsverslag, kascommissieverslag en wijnactieplan; "
     "vaste agendapunten: opening door de voorzitter, vaststelling agenda, notulen, "
     "ingekomen stukken, rondvraag en sluiting. Deze opzet wordt als format voor de "
     "verslaglegging van 2027 gehanteerd [5].")

# ============================ 10 GOVERNANCE COMPLIANCE ============================
h1(doc, 10, "Governance-Compliance: Stemrechten, Quorum en Conventieregels")
h2(doc, "10.1 Stemrechten en afgevaardigden")
bullet(doc, "Elke club heeft stemrecht naar rato van het ledenaantal: 1 stem per "
       "10 leden, met een minimum van 1 en een maximum van 5 stemmen per club [B]. "
       "Actieve, buitengewone, vrijgestelde en levenslange leden tellen mee voor de "
       "vaststelling van het aantal stemmen per club conform artikel 11 van de statuten; "
       "erelieden en geassocieerde leden tellen niet mee [3].")
bullet(doc, "Buitengewone, geassocieerde en geaffilieerde leden kunnen de club niet "
       "vertegenwoordigen als afgevaardigde naar districtsconventies [3].")
bullet(doc, "Wie zich uiterlijk 48 uur voor aanvang van de conventie niet heeft "
       "aangemeld, kan niet als stemgerechtigde worden geregistreerd [3].")
bullet(doc, "Clubs melden uiterlijk 24 uur voor aanvang wie namens de aangemelde "
       "leden stemgerechtigd is; bij geen of te late melding is de club niet "
       "stemgerechtigd [3].")
bullet(doc, "Lions die niet als stemgerechtigde zijn aangewezen, hebben toegang tot "
       "de conventie, doch zonder spreekrecht [3].")
h2(doc, "10.2 Quorum en stemmingen")
bullet(doc, "Voor de meeste artikelen geldt een quorum van meer dan 50% van de "
       "stemgerechtigden [B].")
bullet(doc, "De voorzitter van het stembureau verifieert of bij een schriftelijke "
       "stemming aan de quorumbepalingen is voldaan [3].")
bullet(doc, "Stemmen over personen geschiedt schriftelijk met vooraf ingevulde "
       "stembiljetten; er is een apart agendapunt v\u00f3\u00f3r het bekendmaken van een "
       "uitslag, zodat er tijdig geteld kan worden [1][3].")
bullet(doc, "De conventie kan over zaken bij acclamatie beslissen, tenzij een "
       "stemgerechtigde bezwaar maakt; dan wordt onmiddellijk over de zaak gestemd [3]. "
       "Bij twijfel over de uitslag wordt onmiddellijk opnieuw gestemd [3].")
h2(doc, "10.3 Verkiezingen en benoemingen")
bullet(doc, "Kandidaten voor verkiezingen dienen gekandideerd te zijn door de "
       "districtsconventie van hun eigen district; per functie kan slechts een enkele "
       "kandidaat worden gesteld; schriftelijke kandidaatstelling uiterlijk 30 dagen "
       "v\u00f3\u00f3r de conventie, waarna rondzending volgt [3].")
bullet(doc, "Kandidaten krijgen bij het agendapunt maximaal vijf minuten om hun "
       "kandidatuur toe te lichten [3].")
bullet(doc, "Op de voorjaarsconventie worden de inkomende 2e VDG, 1e VDG en DG "
       "benoemd; de inkomende DG presenteert de plannen voor het volgende Lions-jaar "
       "(start 1 juli) inclusief begroting; verder agendapunten: herdenking van "
       "overledenen en voortgangsverhaal van het werkplan [1].")
bullet(doc, "De kascontrolecommissie wordt door de districtsconventie benoemd uit "
       "leden van clubs in het district; kabinetsleden zijn uitgesloten; de commissie "
       "onderzoekt de jaarstukken en rapporteert schriftelijk aan de conventie [6].")
h2(doc, "10.4 Termijnen en LCI/MD110-regels")
table(doc, ["Regel", "Vereiste", "Bron"], [
    ("Districtsconventie v\u00f3\u00f3r nationale conventie", "Minimaal 6 weken; punten minimaal 30 dagen tevoren inbrengbaar", "[1]"),
    ("Publicatie agenda en stukken", "1 maand (30 dagen) vooraf; via site, uitnodiging en ownCloud - statutaire verplichting", "[1]"),
    ("Registratie stemgerechtigden", "Vanaf ten minste 1 uur v\u00f3\u00f3r de opening tot de opening; stemkaart + stembiljet per geagendeerde stemming", "[3]"),
    ("Aanmelding deelnemers", "Uiterlijk 48 uur voor aanvang (voor stemrecht)", "[3]"),
    ("Aanwijzing stemgerechtigde door club", "Binnen 24 uur na aanmelding, uiterlijk 24 uur voor aanvang", "[3]"),
    ("Schriftelijke kandidaatstelling", "Uiterlijk 30 dagen v\u00f3\u00f3r aanvang; rondzending aan geadresseerden", "[3]"),
    ("Datum niet in vakantie; niet verplaatsen", "Check bij vaststelling; een vastgestelde en gecommuniceerde datum wordt nooit verplaatst", "[1]"),
    ("Aanwezigheid dagelijks bestuur", "Volledige DB aanwezig; bij verhindering bij de opening vermelden met reden", "[1]"),
    ("Roulatie nationale voorjaarsconventie", "Vastgesteld door de Gouverneursraad op 6 mei 2022: 2025-2026 = 110AN, 2026-2027 = 110AZ (controle: districtsconventie 110AN is zelfstandig en valt hier niet onder)", "[2]"),
    ("Vlaggenceremonie", "Voorbehouden aan de nationale voorjaarsconventie; niet vereist voor de districtsconventie", "[1]"),
], widths=[4.4, 8.6, 2.4], font=9)

# ============================ 11 GOEDKEURING ============================
h1(doc, 11, "Goedkeuring")
para(doc, "Dit projectplan & PVA wordt ter vaststelling aangeboden aan het Dagelijks "
     "Bestuur en het Districtskabinet 2026-2027 van Lions District 110AN. Na vaststelling "
     "fungeert dit document als het besturingskader voor de organisatie van de "
     "Voorjaarsconventie 2027. Herziening vindt plaats na elke QC-gate of bij "
     "ingrijpende besluiten (locatie, hostclub, budget).")
table(doc, ["Rol", "Naam", "Datum", "Handtekening"], [
    ("Districtsgouverneur 2026-2027", "", "", ""),
    ("1e Vice-Districtsgouverneur", "", "", ""),
    ("2e Vice-Districtsgouverneur", "", "", ""),
    ("Districtssecretaris", "", "", ""),
    ("Districtspenningmeester", "", "", ""),
    ("Voorzitter hostclub / organiserend comite", "", "", ""),
], widths=[5.6, 4.4, 2.6, 3.4], font=10)
doc.add_page_break()

# ============================ BIJLAGE A - BRONNEN ============================
h1(doc, None, "Bijlage A - Bronnenlijst")
para(doc, "Alle feiten in dit document zijn ontleend aan het geconsolideerde "
     "meta-dossier (v1) van dit project. Genummerde bronnen:", 10, TEXT_SEC, italic=True)
bronnen = [
    "Handboek MD 110 Lions Conventies, vs 06, 29-07-2024 (Handboek Lions Conventies) - organisatorische en statutaire to do's districts-/nationale conventies.",
    "Conventie Roulatieschema (vastgesteld Gouverneursraad 6 mei 2022) - roulatie landelijke voorjaarsconventies: 2023-2024 110CO, 2024-2025 110BN, 2025-2026 110AN, 2026-2027 110AZ.",
    "Huishoudelijk Reglement MD 110 Nederland, mei 2021 - Sectie 1 Conventiereglement (aanmelding, registratie, stemmingen, kandidaatstellingen) en lidmaatschaps-/stemrechtbepalingen.",
    "Club Health Assessment District 110AN t/m september 2025 (LCI-rapport) - club- en ledenaantallen district.",
    "Verslag Districtsconventie / Leden Ontmoetingsavond 17 oktober 2024, 110AN (Stadscaffee Laurens, Kerkade) - opzet, opkomst (66 aanwezigen, 20 clubs, 42 stemmen), stembureau, agenda.",
    "Werkplan en begroting AN 2025-2026 (16-03-2025) - DB-afspraken (DG/VDG/DP-portefeuille voorjaarsconventie), commissaris Voorjaarsconventie (Pieter Bas Boertje, LC Bussum Godelinde), meerdaagse voorjaarsconventie sinds FY 2022-2023 (Texel, Den Helder, Haarlem Over den Duin, Bussum Godelinde), zonevoorzitters, Lions Media, kascontrolecommissie, investeringsbeleid eigen vermogen, jaarkalender.",
    "Werkplan en begroting AN 2026-2027, draft v1 - thema 'Samen verder bouwen!', kabinet 2026-2027 (incl. commissaris Voorjaarsconventie [nog in te vullen]), meerdaagse voorjaarsconventie als vaste activiteit.",
    "Lions 110AN Werkplan 2024-2025 v1.0 - commissiestructuur en context.",
    "2024-2025 MD110 Werkplan Gouverneursraad - MD-coordinatie en nationale programma's.",
    "Districtbegroting 110 AN 2025-2026 - 1.200 leden / contributie EUR 20; inkomsten EUR 24.500; uitgaven EUR 29.500; post '1 Districtsconventie' EUR 3.500.",
    "agenda-110AN-2026-2027.json (Kalender_110AN_2026-2027_final_jg_werkbestand.xlsx) - voorlopige reservering 'Voorjaarsconventie??' 19-20 maart 2027; Publicatie stukken Nationale voorjaarsconventie 29-4-2027; Nationale Voorjaarsconventie 30-5-2027.",
]
for i, b in enumerate(bronnen, 1):
    para(doc, "[%d] %s" % (i, b), 9.5, TEXT_DARK, after=4)
para(doc, "[B] Controleset Feiten (bronnenoverzicht/meta-dossier, TierVerify) - "
     "bindende feitenset voor alle deliverables van dit project; zie Bijlage B.",
     9.5, TEXT_DARK, after=4, before=6)

# ============================ BIJLAGE B - CONTROLESET ============================
h1(doc, None, "Bijlage B - Controleset Feiten (consistentiecheck)")
para(doc, "De controleset uit het bronnenoverzicht is op dit document toegepast. "
     "Toepassing per feit:", 10, TEXT_SEC, italic=True)
table(doc, ["Feit (controleset)", "Waarde", "Toepassing in dit document"], [
    ("District", "110AN", "Doorgevoerd - gehele document"),
    ("Conventietype", "Voorjaarsconventie (District Convention)", "Hoofdstuk 1-2; formele ALV [1][3]"),
    ("Datum", "April 2027 (exacte datum t.b.v. roulatie)", "Aangehouden als projectkader; openstaande keuze 19-20 maart 2027 uit werkkalender [11] expliciet benoemd in hoofdstuk 4; vaststelling is M0"),
    ("Locatie", "Te bepalen op basis van roulatie", "Vaststelling via M1; hostclub-aanwijzing conform districtspraktijk [6]"),
    ("Governance", "Huishoudelijk Reglement MD 110 mei 2021", "Hoofdstuk 10 volledig gebaseerd op HR"),
    ("Stemrechten", "Per club 1 stem per 10 leden (min 1, max 5)", "Hoofdstuk 10.1 [B][3]"),
    ("Quorum", "Meeste artikelen: >50% stemgerechtigden", "Hoofdstuk 10.2 [B][3]"),
    ("Budget referentie", "Districtbegroting 25-26: post districtsconventie EUR 3.500", "Hoofdstuk 6 [10]"),
    ("Voorzitter", "District Governor 2026-2027", "PVA-matrix: DG = Accountable"),
    ("Secretaris", "Cabinet Secretary 2026-2027", "PVA-matrix: secretaris = centrale regelaar [1]"),
], widths=[4.2, 5.2, 6.6], font=9)
para(doc, "Disclaimers: namen van functionarissen 2026-2027 zijn in het werkplan-draft "
     "deels nog placeholders [7] en zijn daarom in dit document niet hard gemaakt. "
     "Alle bedragen in hoofdstuk 6 zijn indicatieve plafondkaders op basis van de "
     "referentiebegroting [10], geen offertes.", 9, TEXT_SEC, italic=True, before=6)

# ============================ OPSLAAN ============================
import os
os.makedirs(os.path.dirname(OUT), exist_ok=True)
doc.save(OUT)
print("OPGESLAGEN:", OUT)
print("Bestandsgrootte:", os.path.getsize(OUT), "bytes")
