from pathlib import Path
from datetime import date
from PIL import Image, ImageDraw, ImageFont
from docx import Document
from docx.shared import Inches, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.oxml import OxmlElement
from docx.oxml.ns import qn

ROOT = Path(__file__).resolve().parent
SCREENS = ROOT / 'screens'
SCREENS.mkdir(parents=True, exist_ok=True)
PURPLE = '#7366FF'
PURPLE_DARK = '#5F52E8'
NAVY = '#18233A'
INK = '#30394B'
MUTED = '#718096'
PALE = '#F4F5FA'
LINE = '#E4E7EF'
TEAL = '#18A999'

FONT_REG = r'C:\Windows\Fonts\segoeui.ttf'
FONT_BOLD = r'C:\Windows\Fonts\segoeuib.ttf'

def f(size, bold=False):
    path = FONT_BOLD if bold else FONT_REG
    try:
        return ImageFont.truetype(path, size)
    except OSError:
        return ImageFont.load_default()

def rr(draw, box, fill, radius=14, outline=None, width=1):
    draw.rounded_rectangle(box, radius, fill=fill, outline=outline, width=width)

def text(draw, xy, value, size=18, color=INK, bold=False):
    draw.text(xy, value, font=f(size, bold), fill=color)

def base_screen(title, section='Workspace'):
    im = Image.new('RGB', (1600, 980), '#F5F6FA')
    d = ImageDraw.Draw(im)
    d.rectangle((0, 0, 300, 980), fill='#FFFFFF')
    d.rectangle((0, 0, 300, 1), fill=LINE)
    rr(d, (28, 26, 72, 70), PURPLE, 13)
    text(d, (42, 31), 'E', 27, '#FFFFFF', True)
    text(d, (85, 27), 'Elections', 20, NAVY, True)
    text(d, (85, 51), 'Monitoring System', 15, MUTED)
    d.line((25, 91, 275, 91), fill=LINE, width=2)
    text(d, (29, 112), 'MONITOR', 12, '#9AA3B2', True)
    links = [('▦', 'Election dashboard'), ('✓', 'Review results'), ('▤', 'Reports')]
    for i,(icon,label) in enumerate(links):
        y=146+i*52
        if (title == 'Election overview' and i == 0) or (title == 'Verified results reports' and i == 2):
            rr(d, (18,y-8,281,y+35), '#F1EFFF', 11)
            color=PURPLE_DARK
        else: color='#677286'
        text(d,(34,y),icon,19,color,True); text(d,(68,y+2),label,16,color,(color==PURPLE_DARK))
    text(d, (29, 322), 'MANAGE', 12, '#9AA3B2', True)
    for i,label in enumerate(['Campaigns & teams','User accounts']):
        text(d,(34,356+i*48),'◦',20,'#677286',True); text(d,(68,358+i*48),label,16,'#677286')
    d.line((25,890,275,890),fill=LINE,width=2)
    rr(d,(29,913,67,951),'#E9E5FF',20)
    text(d,(40,920),'AD',14,PURPLE_DARK,True)
    text(d,(78,915),'Demo Administrator',14,NAVY,True)
    text(d,(78,936),'System administrator',12,MUTED)
    text(d,(332,26),'ELECTIONS MONITORING SYSTEM  /  '+section.upper(),12,MUTED,True)
    text(d,(332,54),title,28,NAVY,True)
    text(d,(1450,37),'AD',15,PURPLE_DARK,True)
    d.ellipse((1437,24,1477,64),fill='#E9E5FF')
    return im,d

def make_overview():
    im,d=base_screen('Election overview','Central operations')
    text(d,(332,103),'2027 General Election  ·  Planning',15,'#4B6680',True)
    rr(d,(330,142,1545,311),'#172B4D',22)
    text(d,(368,166),'ELECTION FOUNDATION',12,'#BFCBDB',True)
    text(d,(368,194),'Start with trusted master data.',29,'#FFFFFF',True)
    text(d,(368,238),'Build the election and geography before activating teams and receiving results.',16,'#D5DDEA')
    for i,(n,label) in enumerate([('47','Counties'),('290','Constituencies'),('1,450','Wards'),('—','Stations')]):
        x=332+i*306
        rr(d,(x,339,x+286,460),'#FFFFFF',17,LINE)
        text(d,(x+20,356),label,14,MUTED)
        text(d,(x+20,387),n,32,NAVY,True)
        text(d,(x+20,430),'Master data readiness',12,'#8993A3')
    rr(d,(332,487,1545,915),'#FFFFFF',17,LINE)
    text(d,(362,510),'County master list',19,NAVY,True)
    text(d,(362,540),'Manage official geography codes and parent relationships.',13,MUTED)
    rr(d,(1310,510,1512,552),PURPLE,11)
    text(d,(1333,520),'+  Add county',14,'#FFFFFF',True)
    d.rectangle((352,583,1525,627),fill='#F5F6FA')
    for x,label in [(373,'CODE'),(570,'COUNTY'),(1032,'CONSTITUENCIES'),(1325,'CREATED')]: text(d,(x,597),label,11,MUTED,True)
    rows=[('001','Mombasa','6','08 Oct 2026'),('002','Kwale','4','08 Oct 2026'),('003','Kilifi','7','08 Oct 2026'),('004','Tana River','3','08 Oct 2026'),('005','Lamu','2','08 Oct 2026')]
    for i,row in enumerate(rows):
        y=646+i*48
        d.line((352,y+37,1525,y+37),fill='#EDF0F5',width=1)
        text(d,(373,y),row[0],13,'#778397')
        text(d,(570,y),row[1],14,INK,True)
        text(d,(1032,y),row[2],14,'#657187')
        text(d,(1325,y),row[3],13,'#657187')
    im.save(SCREENS/'overview-preview.png')

def make_dashboard():
    im,d=base_screen('Election dashboards','Monitoring')
    rr(d,(332,112,1545,165),'#FFFFFF',14,LINE)
    for i,(a,b) in enumerate([('Central election','Selected'),('Campaign data',''),('Agent assignments','')]):
        x=353+i*205
        rr(d,(x,125,x+190,152),PURPLE if i==0 else '#F4F5FA',8)
        text(d,(x+12,130),a,12,'#FFFFFF' if i==0 else '#69758A',True)
    vals=[('Stations in election','24','Geographic master data'),('Stations reporting','18','Selected position'),('Verified results','15','Ready for aggregation'),('Awaiting review','3','2 pending · 1 flagged')]
    for i,(label,num,foot) in enumerate(vals):
        x=332+i*306
        rr(d,(x,187,x+286,309),'#FFFFFF',16,LINE)
        text(d,(x+20,206),label,14,MUTED)
        text(d,(x+20,234),num,32,NAVY,True)
        text(d,(x+20,278),foot,12,'#8993A3')
    rr(d,(332,336,1545,915),'#FFFFFF',17,LINE)
    text(d,(360,358),'Central tally · Member of National Assembly',19,NAVY,True)
    text(d,(360,388),'Verified submissions only contribute to candidate totals.',13,MUTED)
    candidates=[('Amina Sample','SRP','5,480',0.82),('Brian Sample','SUP','4,920',0.74),('Carol Sample','Independent','1,120',0.29),('Other candidate','—','640',0.12)]
    for i,(name,party,votes,ratio) in enumerate(candidates):
        y=452+i*103
        text(d,(365,y),name,16,NAVY,True); text(d,(365,y+27),party,12,MUTED)
        rr(d,(810,y+7,1320,y+28),'#EDF0F6',11)
        rr(d,(810,y+7,810+int(510*ratio),y+28),PURPLE,11)
        text(d,(1374,y+5),votes,17,NAVY,True)
        d.line((360,y+69,1516,y+69),fill='#EDF0F5',width=1)
    text(d,(365,872),'Filters: election  ·  contest position  ·  campaign or agent scope',13,'#667286')
    im.save(SCREENS/'dashboard-preview.png')

def make_submission():
    im=Image.new('RGB',(1400,940),'#F5F6FA'); d=ImageDraw.Draw(im)
    rr(d,(72,54,1328,886),'#FFFFFF',24,LINE,2)
    rr(d,(72,54,1328,160),'#18233A',24)
    d.rectangle((72,130,1328,160),fill='#18233A')
    text(d,(112,80),'FIELD REPORTING  /  AGENT WORKSPACE',13,'#CAD4E1',True)
    text(d,(112,108),'Submit polling station result',28,'#FFFFFF',True)
    rr(d,(1110,90,1277,132),'#243959',19)
    text(d,(1132,103),'Assigned station',12,'#FFFFFF',True)
    text(d,(112,190),'1  Select assignment',18,NAVY,True)
    text(d,(112,220),'Your account can submit only for active stations assigned to you.',13,MUTED)
    rr(d,(112,257,1288,315),'#FFFFFF',10,LINE,2)
    text(d,(135,276),'Sample East Primary · Stream 1   |   Sample Constituency, Demo County',15,INK,True)
    text(d,(112,353),'2  Enter results from the official form',18,NAVY,True)
    fields=[('Registered voters','420'),('Total votes cast','286'),('Valid votes','279'),('Rejected votes','4'),('Spoilt votes','2'),('Disputed votes','1')]
    for i,(label,value) in enumerate(fields):
        col=i%3; row=i//3; x=112+col*396; y=397+row*124
        text(d,(x,y),label,13,'#536174',True)
        rr(d,(x,y+25,x+365,y+75),'#FFFFFF',9,LINE,2)
        text(d,(x+16,y+39),value,16,INK)
    text(d,(112,658),'Candidate votes',18,NAVY,True)
    for i,(candidate,value) in enumerate([('Amina Sample','180'),('Brian Sample','75'),('Carol Sample','24')]):
        y=700+i*49
        text(d,(112,y),candidate,14,INK)
        rr(d,(925,y-6,1287,y+30),'#FFFFFF',8,LINE,1)
        text(d,(944,y+1),value,14,INK)
    text(d,(112,849),'Attach source form image or PDF for review; uploads remain private.',12,MUTED)
    rr(d,(1045,826,1288,870),PURPLE,11)
    text(d,(1105,839),'Save submission',14,'#FFFFFF',True)
    im.save(SCREENS/'submission-preview.png')

def make_review():
    im,d=base_screen('Review & verification','Quality control')
    text(d,(332,106),'Inspect evidence, resolve discrepancies, and record a decision.',14,MUTED)
    rr(d,(332,145,1545,236),'#FFFFFF',16,LINE)
    for i,(label,num,color) in enumerate([('Pending','5','#D18A12'),('Flagged','2','#CA4F61'),('Verified','15','#209178'),('Rejected','1','#788397')]):
        x=360+i*290
        text(d,(x,161),label,13,MUTED); text(d,(x,185),num,24,color,True)
    rr(d,(332,260,680,915),'#FFFFFF',16,LINE)
    text(d,(356,282),'Review queue',18,NAVY,True)
    text(d,(356,311),'Filter by status, station, or date.',12,MUTED)
    for i,(station,status) in enumerate([('Sample East Primary','Pending'),('Sample West Primary','Flagged'),('Demo Centre 03','Pending'),('Demo Centre 04','Verified'),('Demo Centre 05','Flagged')]):
        y=354+i*100
        if i==1: rr(d,(346,y-12,665,y+70),'#F4F1FF',10)
        text(d,(360,y),station,14,NAVY,True)
        text(d,(360,y+27),'Submitted 08 Oct · Agent 02',11,MUTED)
        color={'Pending':'#9A6A15','Flagged':'#BB4256','Verified':'#167C68'}[status]
        text(d,(360,y+49),status.upper(),10,color,True)
        d.line((356,y+78,657,y+78),fill=LINE,width=1)
    rr(d,(705,260,1545,915),'#FFFFFF',16,LINE)
    text(d,(735,282),'Submission details',18,NAVY,True)
    text(d,(735,313),'Sample West Primary · Member of National Assembly',13,MUTED)
    rr(d,(735,355,1515,433),'#F6F7FB',10)
    text(d,(756,371),'Automated discrepancy check',13,NAVY,True)
    text(d,(756,398),'Turnout is within registered voter limits; verify one tally mismatch.',12,'#7E5B13')
    text(d,(735,461),'Evidence record & OCR assistance',16,NAVY,True)
    rr(d,(735,496,1515,601),'#F6F7FB',10)
    text(d,(756,514),'station-form-sample.jpg  ·  SHA-256 fingerprint recorded',12,INK)
    text(d,(756,543),'Local OCR suggestions are shown for human comparison only.',12,MUTED)
    text(d,(735,632),'Review notes',13,NAVY,True)
    rr(d,(735,660,1515,736),'#FFFFFF',9,LINE,2)
    text(d,(755,679),'Record the source form and checks completed…',12,'#929CAC')
    for i,(label,color,fill) in enumerate([('Flag','#B17914','#FFF7E7'),('Reject','#B8485B','#FFF0F1'),('Verify & lock','#FFFFFF',PURPLE)]):
        x=1035+i*164
        rr(d,(x,779,x+150,823),fill,10)
        text(d,(x+19,792),label,13,color,True)
    text(d,(735,858),'Decision, reviewer, timestamp, and notes are retained in the review history.',11,MUTED)
    im.save(SCREENS/'review-preview.png')

def make_reports():
    im,d=base_screen('Verified results reports','Analysis')
    rr(d,(332,112,1545,315),'#FFFFFF',16,LINE)
    text(d,(359,132),'Report filters',17,NAVY,True)
    text(d,(359,158),'Choose election, contest, reporting level, and optional geography.',12,MUTED)
    filters=[('Election','2027 General Election'),('Position','Member of National Assembly'),('Report by','County'),('Candidate','All candidates'),('County','All counties'),('Constituency','All constituencies'),('Ward','All wards'),('Station','All stations')]
    for i,(label,val) in enumerate(filters):
        col=i%4; row=i//4; x=359+col*290; y=193+row*58
        text(d,(x,y),label,10,MUTED,True)
        rr(d,(x,y+17,x+264,y+50),'#FFFFFF',6,LINE)
        text(d,(x+10,y+25),val,11,INK)
    vals=[('Polling stations','24'),('Verified reports','18'),('Registered voters','12,480'),('Votes cast','8,672')]
    for i,(label,num) in enumerate(vals):
        x=332+i*306
        rr(d,(x,340,x+286,448),'#FFFFFF',14,LINE)
        text(d,(x+18,357),label,12,MUTED)
        text(d,(x+18,384),num,28,NAVY,True)
        text(d,(x+18,421),'Verified submissions only',10,'#929CAC')
    rr(d,(332,470,1545,915),'#FFFFFF',16,LINE)
    text(d,(359,490),'Report details',17,NAVY,True)
    for i,(x,label) in enumerate([(359,'AREA'),(558,'CANDIDATE'),(889,'PARTY'),(1164,'VOTES'),(1365,'STATIONS')]): text(d,(x,531),label,10,MUTED,True)
    d.rectangle((350,518,1526,553),fill='#F5F6FA')
    rows=[('Demo County','Amina Sample','SRP','3,210','12'),('Demo County','Brian Sample','SUP','2,980','12'),('Demo County','Carol Sample','Independent','765','11'),('Sample Coast','Amina Sample','SRP','1,190','4'),('Sample Coast','Brian Sample','SUP','1,030','4'),('Sample Coast','Carol Sample','Independent','210','4')]
    for i,row in enumerate(rows):
        y=572+i*49
        for x,val in zip([359,558,889,1164,1365],row): text(d,(x,y),val,12,INK if x==558 else '#657187',x==558)
        d.line((350,y+32,1526,y+32),fill='#EDF0F5',width=1)
    for i,label in enumerate(['CSV','EXCEL','PDF']):
        x=1210+i*98
        rr(d,(x,862,x+88,896),PURPLE if i==0 else '#F4F5FA',8)
        text(d,(x+18,871),'↓ '+label,11,'#FFFFFF' if i==0 else '#59667A',True)
    im.save(SCREENS/'reports-preview.png')

def make_accounts():
    im,d=base_screen('User accounts','Access management')
    rr(d,(332,113,756,915),'#FFFFFF',16,LINE)
    text(d,(360,137),'Invite a user',18,NAVY,True)
    text(d,(360,168),'Create an account and send a password setup link.',12,MUTED)
    for i,(label,value) in enumerate([('Full name','Sam Sample'),('Email address','sam@example.org'),('System role','Observer')]):
        y=221+i*98
        text(d,(360,y),label,12,'#556176',True)
        rr(d,(360,y+24,728,y+68),'#FFFFFF',8,LINE,2)
        text(d,(377,y+37),value,13,INK)
    rr(d,(360,534,728,580),PURPLE,10)
    text(d,(427,548),'Create account & send email',12,'#FFFFFF',True)
    rr(d,(360,619,728,835),'#F8F7FF',12)
    text(d,(381,642),'Role boundaries',14,NAVY,True)
    for i,line in enumerate(['Agents submit results only for assigned stations.','Campaign users see permitted campaign records.','Only authorized reviewers can change a result decision.']):
        text(d,(382,682+i*45),'• '+line,11,'#566276')
    rr(d,(780,113,1545,915),'#FFFFFF',16,LINE)
    text(d,(810,137),'Accounts',18,NAVY,True)
    rr(d,(1190,128,1512,168),'#FFFFFF',8,LINE,2)
    text(d,(1207,141),'Search name, email, ID or phone',11,'#8993A3')
    rows=[('Demo Administrator','admin@example.org','Administrator','+254 700 123 400','View ID copy'),('Faith Monitor','faith@example.org','Election officer','+254 711 222 330','No ID copy'),('Sam Sample','sam@example.org','Observer','Not provided','No ID copy'),('Field Agent 01','agent@example.org','Agent','+254 733 445 566','View ID copy'),('Review Analyst','analyst@example.org','Analyst','+254 722 100 200','No ID copy')]
    for i,row in enumerate(rows):
        y=210+i*127
        text(d,(810,y),row[0],14,NAVY,True)
        text(d,(810,y+25),row[1]+'  ·  '+row[2],11,MUTED)
        text(d,(810,y+50),'Phone: '+row[3],11,'#667286')
        text(d,(810,y+73),'KYC document: '+row[4],10,PURPLE_DARK,True)
        rr(d,(1330,y+31,1508,y+68),'#FFFFFF',8,LINE)
        text(d,(1348,y+43),'Send setup link',10,'#566276',True)
        d.line((808,y+104,1517,y+104),fill='#EDF0F5',width=1)
    im.save(SCREENS/'accounts-preview.png')

for fn in [make_overview, make_dashboard, make_submission, make_review, make_reports, make_accounts]:
    fn()

def shade(cell, color):
    tcPr = cell._tc.get_or_add_tcPr()
    shd = OxmlElement('w:shd'); shd.set(qn('w:fill'), color); tcPr.append(shd)

def set_cell_margins(cell, top=90, start=110, bottom=90, end=110):
    tc = cell._tc; tcPr = tc.get_or_add_tcPr(); mar = OxmlElement('w:tcMar')
    for m,v in [('top',top),('start',start),('bottom',bottom),('end',end)]:
        node=OxmlElement('w:'+m); node.set(qn('w:w'),str(v)); node.set(qn('w:type'),'dxa'); mar.append(node)
    tcPr.append(mar)

def set_repeat_table_header(row):
    trPr=row._tr.get_or_add_trPr(); tblHeader=OxmlElement('w:tblHeader'); tblHeader.set(qn('w:val'),'true'); trPr.append(tblHeader)

def set_run(run, size=10.5, color='30394B', bold=False, font='Aptos'):
    run.font.name=font; run.font.size=Pt(size); run.font.color.rgb=RGBColor.from_string(color); run.bold=bold
    rPr=run._element.get_or_add_rPr(); rFonts=rPr.rFonts
    if rFonts is not None:
        rFonts.set(qn('w:ascii'),font); rFonts.set(qn('w:hAnsi'),font)

def set_doc_base(doc, short_title):
    sec=doc.sections[0]; sec.top_margin=Inches(.72); sec.bottom_margin=Inches(.68); sec.left_margin=Inches(.8); sec.right_margin=Inches(.8)
    sec.header_distance=Inches(.35); sec.footer_distance=Inches(.35)
    styles=doc.styles
    normal=styles['Normal']; normal.font.name='Aptos'; normal.font.size=Pt(10.5); normal.font.color.rgb=RGBColor.from_string('30394B')
    normal.paragraph_format.space_after=Pt(6); normal.paragraph_format.line_spacing=1.12
    for name,size,color in [('Title',32,'151B2A'),('Subtitle',14,'667286'),('Heading 1',21,'282050'),('Heading 2',14,'5143B8'),('Heading 3',11,'30394B')]:
        st=styles[name]; st.font.name='Aptos Display' if name in ['Title','Heading 1'] else 'Aptos'; st.font.size=Pt(size); st.font.color.rgb=RGBColor.from_string(color); st.font.bold=name!='Subtitle'; st.paragraph_format.space_before=Pt(12 if name!='Title' else 0); st.paragraph_format.space_after=Pt(6); st.paragraph_format.keep_with_next=True
    title_ppr=styles['Title']._element.get_or_add_pPr()
    title_border=title_ppr.find(qn('w:pBdr'))
    if title_border is not None: title_ppr.remove(title_border)
    header=sec.header.paragraphs[0]; header.alignment=WD_ALIGN_PARAGRAPH.RIGHT
    r=header.add_run('ELECTIONS MONITORING SYSTEM  /  '+short_title.upper()); set_run(r,8,'7366FF',True)
    footer=sec.footer.paragraphs[0]; footer.alignment=WD_ALIGN_PARAGRAPH.CENTER
    r=footer.add_run('Elections Monitoring System  ·  Stakeholder discussion copy  ·  '); set_run(r,8,'8791A1')
    fld=OxmlElement('w:fldSimple'); fld.set(qn('w:instr'),'PAGE'); footer._p.append(fld)
    doc.core_properties.title=short_title
    doc.core_properties.subject='Election monitoring platform proposal and stakeholder presentation'
    doc.core_properties.author='Elections Monitoring System'

def p(doc, text_value='', style=None, bold_lead=None, color=None, size=None, italic=False):
    para=doc.add_paragraph(style=style)
    if bold_lead and text_value.startswith(bold_lead):
        a=para.add_run(bold_lead); set_run(a,10.5,color or '30394B',True)
        b=para.add_run(text_value[len(bold_lead):]); set_run(b,10.5,color or '30394B')
    else:
        r=para.add_run(text_value); set_run(r,size or 10.5,color or '30394B',False); r.italic=italic
    return para

def bullet(doc, text_value, level=0):
    para=doc.add_paragraph(style='List Bullet' if level==0 else 'List Bullet 2')
    para.paragraph_format.space_after=Pt(3)
    r=para.add_run(text_value); set_run(r,10.2,'30394B')
    return para

def numbered(doc, text_value):
    para=doc.add_paragraph(style='List Number'); para.paragraph_format.space_after=Pt(4)
    r=para.add_run(text_value); set_run(r,10.2,'30394B'); return para

def callout(doc, title, body, fill='F1EFFF', accent='7366FF'):
    table=doc.add_table(rows=1,cols=1); table.alignment=WD_TABLE_ALIGNMENT.CENTER; table.autofit=False; table.columns[0].width=Inches(6.8)
    c=table.cell(0,0); shade(c,fill); set_cell_margins(c,130,190,130,190)
    para=c.paragraphs[0]; para.paragraph_format.space_after=Pt(3); r=para.add_run(title); set_run(r,10,accent,True)
    para=c.add_paragraph(); para.paragraph_format.space_after=Pt(0); r=para.add_run(body); set_run(r,9.5,'354154')
    doc.add_paragraph().paragraph_format.space_after=Pt(0)

def image(doc, filename, width=6.8, caption='Illustrative interface preview · sample names and figures'):
    para=doc.add_paragraph(); para.alignment=WD_ALIGN_PARAGRAPH.CENTER; para.paragraph_format.space_after=Pt(2)
    para.add_run().add_picture(str(SCREENS/filename),width=Inches(width))
    cp=doc.add_paragraph(); cp.alignment=WD_ALIGN_PARAGRAPH.CENTER; cp.paragraph_format.space_after=Pt(7)
    r=cp.add_run(caption); set_run(r,8,'8791A1')

def cover(doc, subtitle, strapline, art='overview-preview.png'):
    p(doc,'ELECTIONS MONITORING SYSTEM',size=10,color='7366FF')
    para=doc.add_paragraph(style='Title'); para.paragraph_format.space_before=Pt(28); para.paragraph_format.space_after=Pt(8)
    title_ppr=para._p.get_or_add_pPr(); title_border=title_ppr.find(qn('w:pBdr'))
    if title_border is not None: title_ppr.remove(title_border)
    r=para.add_run(doc.core_properties.title); set_run(r,31,'151B2A',True,'Aptos Display')
    para=doc.add_paragraph(style='Subtitle'); para.paragraph_format.space_after=Pt(15)
    r=para.add_run(subtitle); set_run(r,15,'667286')
    para=doc.add_paragraph(); para.paragraph_format.space_after=Pt(10)
    r=para.add_run(strapline); set_run(r,12,'5143B8',True)
    p(doc,'Prepared October 2026  ·  Kenya  ·  East Africa Time (UTC+3)',size=9,color='8791A1')
    doc.add_paragraph()
    image(doc,art,6.8,'Illustrative interface preview based on the current system workflow')
    callout(doc,'A practical election operations workspace','A role-based web system for election preparation, field result capture, review, and verified reporting. This document describes the current application and a proposed path to operational deployment.')
    doc.add_page_break()

def add_heading(doc, title, level=1):
    doc.add_heading(title,level=level)

def add_table(doc, headers, rows, widths=None):
    table=doc.add_table(rows=1,cols=len(headers)); table.alignment=WD_TABLE_ALIGNMENT.CENTER; table.autofit=False
    if widths:
        for col,w in zip(table.columns,widths): col.width=Inches(w)
    for i,h in enumerate(headers):
        c=table.rows[0].cells[i]; shade(c,'282050'); set_cell_margins(c,100,100,100,100); c.vertical_alignment=WD_CELL_VERTICAL_ALIGNMENT.CENTER
        r=c.paragraphs[0].add_run(h); set_run(r,8.5,'FFFFFF',True)
    set_repeat_table_header(table.rows[0])
    for ridx,row in enumerate(rows):
        cells=table.add_row().cells
        for i,value in enumerate(row):
            if ridx%2==1: shade(cells[i],'F6F7FA')
            set_cell_margins(cells[i],85,100,85,100); cells[i].vertical_alignment=WD_CELL_VERTICAL_ALIGNMENT.CENTER
            r=cells[i].paragraphs[0].add_run(str(value)); set_run(r,8.5,'30394B',i==0)
    doc.add_paragraph().paragraph_format.space_after=Pt(0)
    return table

def build_proposal():
    doc=Document(); set_doc_base(doc,'Stakeholder Proposal'); cover(doc,'Proposal for candidates and election stakeholders','Election field reporting and results oversight', 'dashboard-preview.png')
    add_heading(doc,'Executive summary')
    p(doc,'We propose using the Elections Monitoring System as a shared operational workspace for preparing an election, coordinating authorized field teams, recording polling-station results, reviewing submissions, and producing reports from verified records. It is designed to help candidates, campaign teams, and election stakeholders see what has been submitted, what needs attention, and what has passed review.')
    p(doc,'The current application is a browser-based Laravel and Livewire system. Its implemented workflows include election geography setup, candidate and campaign configuration, agent assignments, result submission with supporting evidence, review and locking, and filtered CSV, Excel, and PDF reports. Access is separated by system and campaign roles. A responsive interface supports desktop and mobile browsers.')
    callout(doc,'Decision requested','Agree a pilot election, confirm the operating roles and review rules, nominate data owners, and authorize a controlled user acceptance exercise before any live election use.')
    add_heading(doc,'The operating challenge')
    p(doc,'Election teams work across many locations and roles. When field reports, supporting forms, review notes, and summary figures are handled in disconnected tools, it becomes harder to find missing submissions, investigate differences, and explain where a number came from. The proposed system brings these tasks into one permission-aware workflow while preserving the distinction between submitted data and reviewed results.')
    add_heading(doc,'What stakeholders gain')
    for x in ['A common view of election setup, polling-station coverage, and result review progress.','Faster collection of station-level figures from assigned agents with an attached source form.','A recorded review process with discrepancy checks, reviewer notes, and locked verified records.','Reports that can be filtered by election, contest, candidate, and geography, then downloaded in common formats.','Role-based accounts so users see the workspace and actions relevant to their assignment.']: bullet(doc,x)
    doc.add_page_break()

    add_heading(doc,'How the system supports election operations')
    p(doc,'The workflow starts with structured master data and moves through field collection to review and reporting. The same election, contest, and geographic records connect the stages, reducing duplicate entry and making each report traceable to its underlying submissions.')
    add_table(doc,['Stage','System workflow','Operational result'],[
        ('1 · Prepare','Set up election, positions, parties, candidates, counties, constituencies, wards, centres, and polling stations.','A consistent reference structure for teams and reports.'),
        ('2 · Assign','Invite users, grant system or campaign roles, and assign agents to polling stations.','Users receive access aligned to their responsibility.'),
        ('3 · Collect','Agents enter station figures and attach the source result form.','A timestamped submission with an evidence record.'),
        ('4 · Review','Reviewers inspect totals and evidence, run checks, add notes, and verify, flag, or reject.','A documented decision; verified records are locked.'),
        ('5 · Report','Filter verified results by contest and geography; export CSV, Excel, or PDF.','Consistent summaries for authorized stakeholders.'),
    ],[1.05,3.65,2.1])
    add_heading(doc,'Election preparation and geography',2)
    p(doc,'Administrators maintain the geographic chain from county to constituency, ward, registration centre, and polling station. Station records can include streams and registered-voter counts. Positions and candidates can be scoped to the relevant geographic level. These relationships provide the basis for agent assignment and hierarchical reports.')
    add_heading(doc,'Campaign and team coordination',2)
    p(doc,'System administrators configure candidates, parties, campaign workspaces, team members, and field assignments. Users can be provisioned through invitations and complete password setup from a time-limited email link. A candidate campaign can coordinate its own permitted team and assigned agents without opening other campaign data.')
    doc.add_page_break()

    add_heading(doc,'Field reporting and evidence')
    p(doc,'An agent selects an active assignment and enters the station figures, including registered voters, votes cast, valid, rejected, spoilt, disputed, and candidate totals. Validation checks basic completeness and limits. A submission can include a photograph or PDF of the source form. Evidence is stored privately and includes a file fingerprint to support integrity checks.')
    p(doc,'The current web interface is responsive for phone-sized screens. The Flutter application is not part of this release, and offline mobile capture is not included. Connectivity, device readiness, and field support should therefore be tested during the pilot.')
    image(doc,'submission-preview.png',5.4,'Illustrative preview · assigned station result entry and evidence upload')
    doc.add_page_break()
    add_heading(doc,'Review, discrepancy checks, and locking')
    p(doc,'Authorized reviewers can inspect a queue of submissions, compare submitted totals with attached evidence, and run automated checks for arithmetic, turnout bounds, contest membership, and evidence fingerprints. Review decisions include notes and are retained with reviewer and time information. Verified records are locked and treated as read-only, so they are distinguishable from pending, flagged, or rejected submissions.')
    p(doc,'Local OCR assistance can extract text from supported evidence files and suggest possible vote values. OCR is an aid for human comparison; it does not change vote counts or approve a result automatically. The local OCR engine and its operating configuration must be confirmed in the deployment environment.')
    image(doc,'review-preview.png',5.5,'Illustrative preview · result queue, evidence, checks, and review decisions')
    doc.add_page_break()

    add_heading(doc,'Reporting and stakeholder visibility')
    p(doc,'The reporting area provides filters for election, position, reporting level, candidate, and geographic area. Users can view figures at national, county, constituency, ward, polling-station, or candidate level. Current reports aggregate verified results only and offer CSV, Excel, and PDF download options. This makes it easier to compare coverage, review station reporting progress, and prepare stakeholder updates from a consistent data set.')
    image(doc,'reports-preview.png',5.6,'Illustrative preview · hierarchical report filters, verified metrics, and downloads')
    doc.add_page_break()
    add_heading(doc,'Role-based access model')
    add_table(doc,['User group','Typical access','Important boundary'],[
        ('Super administrator','System-wide setup, accounts, campaigns, and oversight.','Reserved for a small trusted group.'),
        ('Administrator / central administrator','System setup and account operations within granted permissions.','Elevated system roles remain controlled.'),
        ('Election officer','Central dashboards, review, and reports.','Review actions follow assigned permissions.'),
        ('Campaign administrator / manager / analyst','Permitted campaign dashboards and review visibility.','Campaign data is scoped to membership.'),
        ('Polling agent','Assigned station workspace and result submission.','Submission requires an active station assignment.'),
        ('Observer','Read access where explicitly granted.','No result submission authority by default.'),
    ],[1.85,2.65,2.3])
    doc.add_page_break()

    add_heading(doc,'Data protection and operational safeguards')
    p(doc,'The application separates users by role, rate-limits sign-in attempts, supports password setup and reset by email, and records important review and invitation events. Users can complete profile and KYC details, upload an avatar, and submit an ID or passport copy. Identity documents and result evidence are stored privately and served through authenticated, permission-checked routes.')
    p(doc,'These controls support responsible operations but do not replace the organization’s security, privacy, and legal review. Before collecting real identity documents, stakeholders should confirm a lawful purpose, user notice, access list, retention period, deletion process, and incident response procedure. Production deployment should add encrypted backups, encrypted transport, least-privilege infrastructure access, monitoring, and a tested recovery plan.')
    add_heading(doc,'Implementation and readiness plan')
    add_table(doc,['Workstream','Proposed activity','Exit evidence'],[
        ('1 · Pilot definition','Select a pilot election, geography, contest, test stations, users, and review rules.','Approved pilot scope and named owners.'),
        ('2 · Data and configuration','Load and validate official election and geography data; configure teams and access.','Signed-off reference data and access matrix.'),
        ('3 · Operational validation','Run agent, reviewer, report, and recovery scenarios with sample records.','User acceptance findings and resolved blockers.'),
        ('4 · Production preparation','Configure hosting, SSL, mail, queues, local OCR, backups, retention, monitoring, and support.','Deployment checklist and recovery test.'),
        ('5 · Controlled rollout','Train role groups, activate a limited pilot, monitor coverage and issue handling.','Pilot review and go/no-go decision.'),
    ],[1.3,3.7,1.8])
    add_heading(doc,'Readiness items to confirm',2)
    for x in ['Official election, contest, candidate, county, constituency, ward, centre, and station data.','Role owners, account approval process, agent assignment rules, and review escalation path.','Mail service for invitations and password reset; local OCR engine installation and language configuration.','Hosting location, SSL, database capacity, secure storage, backup schedule, monitoring, and on-call support.','Identity-document purpose, access policy, retention duration, privacy notice, and deletion rules.','User acceptance scenarios, training materials, field connectivity plan, and incident communications.']: bullet(doc,x)
    doc.add_page_break()

    add_heading(doc,'Stakeholder participation and next steps')
    p(doc,'A successful pilot depends on stakeholders agreeing how the platform fits the election operation. We recommend a working session with candidate or campaign representatives, election administrators, field coordinators, reviewers, and data-protection or legal advisers.')
    add_heading(doc,'Decisions for the working session',2)
    for x in ['Which election, contest, and geographic area should be used for the first pilot?','Which sources define the official polling-station list and candidate contest scope?','Who may invite users, assign agents, review results, resolve discrepancies, and lock verified records?','What evidence is required for each submission, and what discrepancy thresholds trigger a flag?','Which summary reports are required, who may download them, and at what reporting cadence?','How long should identity documents, source forms, review notes, and exports be retained?','What technical contact will own hosting, backups, local OCR, email delivery, and support?']: numbered(doc,x)
    add_heading(doc,'Proposed pilot success measures',2)
    for label,question in [
        ('Coverage.','Can coordinators identify assigned stations and see which have reported?'),
        ('Data quality.','Do validation and review catch missing, inconsistent, or out-of-range figures?'),
        ('Traceability.','Can a reviewer move from a report row to the verified station submission and evidence?'),
        ('Usability.','Can agents and reviewers complete their tasks on the devices and connections available?'),
        ('Access.','Do users see only the areas and actions that match their role and campaign membership?'),
        ('Recovery.','Can administrators restore the service and records from a tested backup?'),
    ]:
        p(doc,label+' '+question,bold_lead=label)
    callout(doc,'Recommended next step','Convene a pilot design workshop, then approve the sample election dataset, role matrix, review rules, privacy controls, and acceptance scenarios before live data is introduced.')
    doc.save(ROOT/'Elections_Monitoring_System_Proposal.docx')

def build_presentation():
    doc=Document(); set_doc_base(doc,'Stakeholder Presentation Guide'); cover(doc,'Stakeholder presentation and demonstration guide','A concise walkthrough for candidate and election stakeholder meetings','review-preview.png')
    p(doc,'Suggested delivery: 12 to 15 minutes, followed by discussion. Use the talk track as a guide, then demonstrate the live application with a prepared test account and sample data. The interface previews in this guide use fictional sample names and figures.')
    add_heading(doc,'Meeting objectives',2)
    p(doc,'By the end of the discussion, stakeholders should understand how the system links station preparation, agent reporting, human review, and verified reporting. The meeting should also identify what remains to be configured and tested before a pilot.')
    add_heading(doc,'Suggested run of show',2)
    for time,section in [
        ('2 minutes.','Current coordination needs and intended outcome.'),
        ('7 minutes.','Walkthrough of setup, agent submission, review, and reports.'),
        ('3 minutes.','Access boundaries, KYC privacy, and readiness conditions.'),
        ('3 minutes.','Agree pilot scope, owners, and next actions.'),
    ]:
        p(doc,time+' '+section,bold_lead=time)
    callout(doc,'Before the meeting','Use a test account and fictional records. Confirm the demo environment, report exports, mail setup, and local OCR status. Do not display real identity documents or live election data on a projector.')
    add_heading(doc,'Suggested opening',2)
    p(doc,'“Today we will walk through how election setup, assigned field reporting, human review, and verified reports fit into one workflow. We will also identify the operational and privacy decisions that must be agreed before a pilot.”',italic=True)
    add_heading(doc,'Demo preparation checklist',2)
    for x in ['Sign in with a prepared test account for each role you plan to show.','Use fictional submissions and sample evidence only.','Confirm report downloads and browser display on the presentation device.','Keep a printed or offline copy of the agenda and pilot questions.']: bullet(doc,x)
    slides=[
      ('01','The operational need','Election Operations span many polling stations, field teams, and reviewers. Stakeholders need a shared view of what has been submitted, what requires attention, and which results have completed review.','Open by asking how the team currently receives, compares, and summarizes station results. Frame the system as an operational workflow and evidence record, not as an election authority or a substitute for official results processes.','dashboard-preview.png','Opening and context'),
      ('02','One workflow from setup to report','The platform connects election geography, contests, candidates, campaigns, agent assignments, field submissions, review, and verified reporting.','Walk through the sequence: establish trusted reference data; give users scoped access; assign agents to stations; capture figures and source forms; review and lock records; publish authorized summaries.','overview-preview.png','Show election setup'),
      ('03','Field teams submit from assigned stations','Agents enter station-level figures and attach the source form. Active assignments constrain which stations they can submit for. The responsive web experience works in a mobile browser.','Demonstrate the agent view with a test account. Enter sample totals and show the validation cues. Explain that Flutter and offline capture are future work and not part of the current release.','submission-preview.png','Show the form on a phone-sized browser'),
      ('04','Review keeps submitted and verified data distinct','Reviewers inspect evidence, run discrepancy checks, record notes, and decide whether to verify, flag, or reject. Verified records are locked for aggregation.','Open one sample submission. Point to its evidence, arithmetic checks, status, review notes, and history. Explain that local OCR suggests text for human comparison and never changes vote counts on its own.','review-preview.png','Show queue and detail'),
      ('05','Reports answer geographic questions','Authorized reports can be filtered by election, contest, candidate, and geography. Current exports include CSV, Excel, and PDF and aggregate verified records.','Change the report level from national to county or polling station. Show the metrics and result table, then identify who may access and share each export.','reports-preview.png','Show filters and exports'),
      ('06','Access follows assigned responsibility','System and campaign roles determine what a user can see or do. Agents submit only against active station assignments; system administrators manage configuration; authorized reviewers record decisions.','Show account provisioning and the navigation available to the selected role. Describe how invitation links and password setup avoid shared default credentials. Confirm the proposed access matrix with stakeholders.','accounts-preview.png','Show accounts and role separation'),
      ('07','Pilot before election Operations','A controlled pilot validates official data, role boundaries, field usability, review rules, reports, infrastructure, backups, and support.','Ask stakeholders to nominate a pilot contest and area, data owners, field representatives, reviewers, and technical leads. Agree how success and go/no-go decisions will be measured.','dashboard-preview.png','Close with decisions and actions'),
    ]
    for num,title,claim,talk,img,cue in slides:
        doc.add_page_break()
        p(doc,'PRESENTATION SECTION  '+num,size=9,color='7366FF')
        add_heading(doc,title,1)
        callout(doc,'Key message',claim)
        image(doc,img,6.8,'Illustrative interface preview · fictional sample data')
        add_heading(doc,'Presenter notes',2)
        p(doc,talk)
        add_heading(doc,'Demo cue',3)
        p(doc,cue,size=9.5,color='667286')
    doc.add_page_break()
    add_heading(doc,'Discussion and decision record')
    p(doc,'Use the questions below to turn the demonstration into a practical pilot plan. Record the stakeholder answer, named owner, and target date during the meeting.')
    for question in [
      'Pilot election, contest, and geography',
      'Official source for election and polling-station data',
      'Role owners and agent assignment approach',
      'Evidence and discrepancy review rules',
      'Required reports and authorized recipients',
      'Hosting, email, local OCR, backup, and support owners',
      'KYC document privacy and retention policy',
      'Pilot acceptance measures and go/no-go date',
    ]:
        para=doc.add_paragraph(); para.paragraph_format.space_after=Pt(4)
        r=para.add_run(question+'    Owner: ____________________    Date: __________'); set_run(r,9.3,'30394B')
    add_heading(doc,'Questions stakeholders may ask',2)
    for question,response in [
      ('Does the system declare official results?','No. It organizes submitted information, review decisions, and verified-record reports. Official processes remain authoritative.'),
      ('Does OCR decide vote totals?','No. Local OCR can suggest text for a human to compare with the source form; it does not modify or approve totals.'),
      ('Can agents change any station result?','No. Submission is limited to active assignments; review and locking are separately permissioned.'),
      ('Can users submit while offline?','Offline capture is not in the current web release. Connectivity requirements should be tested in the pilot.'),
      ('Is this ready for production today?','The application provides the described workflows. Production use depends on completing hosting, security, privacy, data, recovery, and acceptance readiness checks.'),
    ]:
        add_heading(doc,question,3)
        p(doc,response,size=9.5)
    callout(doc,'Close','Agree the pilot scope, owners, and readiness checklist. Do not introduce live identity documents or election data until access, privacy, and operational controls are approved.')
    doc.save(ROOT/'Elections_Monitoring_System_Stakeholder_Presentation_Guide.docx')

build_proposal()
build_presentation()
print('Created proposal and presentation guide with six illustrative interface previews.')
