gwoe-antragspruefer/app/templates/v3/pdf/antrag_pdf.html
Dotty Dotter 55423e92e3 feat: PDF mit Inter+JetBrains Mono (Cloud-Design-Anlehnung), Matrix kompakter
User: 'Kannst du die Schriftarten eher nachempfinden? Und die Matrix
ist so sehr raumgreifend ... das könnte deutlich in der Höhe gestaucht
sein.'

Fonts:
- Google-Fonts-Link für Inter + JetBrains Mono ergänzt im PDF-Template.
- font-family-Stack: 'Inter', dann Avenir/Helvetica als Fallback (falls
  WeasyPrint die CDN-Fonts nicht laden kann).
- Mono-Stack: 'JetBrains Mono' bevorzugt, dann Source Code Pro.

Matrix in der Höhe gestaucht (war ca. 320pt hoch, jetzt ~165pt):
- Zellen-Höhe 60pt → 28pt (Symbole bleiben mit 11pt-Schrift gut lesbar)
- Zellen-Breite 60pt → 52pt (knapper, Cells leicht breiter als hoch)
- Zeilen-Label-Spalte 90pt → 76pt
- Header-Reihe 24pt → 18pt
- Gap 2pt → 1.5pt
- Legend kleiner: 8pt → 7.5pt, swatches 12pt → 9pt

Resultat: Matrix-Block fast halbiert, Schwerpunkte-erklärt-Sektion
darunter rückt einen Seitenwechsel weniger in den Hintergrund.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:45:32 +02:00

761 lines
24 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{# ─────────────────────────────────────────────────────────────────────
v3/pdf/antrag_pdf.html — GWÖ-Antragsprüfung als druckbares PDF.
Wiederverwendet die v3-Layout-Logik (Single Column, Sektions-Reihen-
folge), kappt aber alle Online-Elemente:
raus: Merken, Vote-treffend, Share-Block, News-Box, Kommentare,
Modals, Reanalyze, Historie, Cluster
drin: Header, Titel, Themen, Zusammenfassung+Kernpunkte, Bewertung,
Stärken/Schwächen, Matrix mit Achsen-Labels, NEU: Erklärung
der Schwerpunkt-Felder, Programm-Treue, Verbesserungen,
Abstimmungsergebnis.
Wird von report.py via Jinja gerendert; WeasyPrint produziert A4 PDF.
───────────────────────────────────────────────────────────────────── #}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>GWÖ-Antragsprüfung — {{ assessment.title }}</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
@page {
size: A4;
margin: 18mm 16mm 22mm 16mm;
@bottom-left {
content: "{{ assessment.drucksache }}";
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
color: #888;
}
@bottom-center {
content: "gwoe.toppyr.de · GWÖ-Antragsprüfer · automatische Gemeinwohl-Bilanzierung";
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
color: #888;
}
@bottom-right {
content: "Seite " counter(page) " von " counter(pages);
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
color: #888;
}
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { font-family: 'Inter', 'Avenir Next', 'Avenir', 'Helvetica Neue', Arial, sans-serif; }
body {
color: #1f1f1f;
font-size: 10pt;
line-height: 1.5;
}
/* ── Header ── */
.pdf-header {
border-bottom: 2px solid #009DA5;
padding-bottom: 8pt;
margin-bottom: 12pt;
}
.pdf-kicker {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8.5pt;
text-transform: uppercase;
letter-spacing: 0.07em;
color: #009DA5;
margin-bottom: 4pt;
}
.pdf-title {
font-size: 18pt;
font-weight: 700;
line-height: 1.18;
color: #1f1f1f;
margin: 0 0 6pt;
}
.pdf-meta {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 9pt;
color: #555;
line-height: 1.5;
}
.pdf-meta .pill {
display: inline-block;
padding: 1pt 6pt;
background: rgba(136, 158, 51, 0.18);
color: #44570a;
border-radius: 2pt;
font-weight: 700;
margin-left: 4pt;
}
.pdf-themen {
margin-top: 6pt;
}
.pdf-theme-chip {
display: inline-block;
padding: 1pt 6pt;
border: 0.5pt solid #cccfb8;
border-radius: 2pt;
background: #fff;
color: #444;
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8.5pt;
margin-right: 4pt;
}
/* ── Sektionen ── */
.pdf-section {
margin: 14pt 0 0;
page-break-inside: avoid;
}
.pdf-h2 {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 9pt;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: #009DA5;
margin: 0 0 6pt;
}
p { margin: 0 0 6pt; }
/* ── Zusammenfassung + Kernpunkte ── */
.pdf-prose {
font-size: 10pt;
line-height: 1.55;
}
.pdf-kernpunkte {
margin-top: 8pt;
padding-top: 6pt;
border-top: 0.5pt dashed #ccc;
}
.pdf-kernpunkte-label {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #009DA5;
margin-bottom: 4pt;
font-weight: 700;
}
.pdf-kernpunkte ul {
margin: 0;
padding-left: 14pt;
}
.pdf-kernpunkte li {
margin-bottom: 2pt;
}
/* ── Bewertungs-Block ── */
.pdf-bewertung {
background:
{% if score_color == '#1a7f37' %}#e8eed1
{% elif score_color == '#bf6c10' %}#f4e6cf
{% else %}#f1dcda{% endif %};
border-left: 4pt solid {{ score_color }};
border-radius: 2pt;
padding: 12pt 14pt;
display: flex;
align-items: center;
gap: 16pt;
}
.pdf-score-num {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 36pt;
font-weight: 700;
line-height: 1;
color: {{ score_color }};
flex-shrink: 0;
}
.pdf-score-num .slash { font-size: 16pt; opacity: 0.5; }
.pdf-score-side { flex: 1; }
.pdf-score-label {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #555;
margin-bottom: 3pt;
}
.pdf-verdict {
font-size: 14pt;
font-weight: 700;
color: {{ score_color }};
line-height: 1.15;
}
.pdf-bewertung-body {
font-size: 9.5pt;
line-height: 1.55;
color: #333;
margin-top: 8pt;
}
/* ── Stärken / Schwächen ── */
.pdf-werte-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10pt;
}
.pdf-werte-card {
border-left: 3pt solid;
padding: 8pt 10pt;
border-radius: 2pt;
}
.pdf-werte-card.staerke { border-left-color: #889E33; background: rgba(136, 158, 51, 0.08); }
.pdf-werte-card.schwaeche { border-left-color: #009DA5; background: rgba(0, 157, 165, 0.08); }
.pdf-werte-card h4 {
font-size: 9pt;
margin-bottom: 4pt;
color: #1f1f1f;
font-weight: 700;
}
.pdf-werte-card p {
font-size: 9.5pt;
margin: 0;
line-height: 1.5;
}
.pdf-werte-card ul {
margin: 0;
padding-left: 14pt;
font-size: 9.5pt;
}
/* ── Matrix mit Achsen ── */
.pdf-matrix-block {
page-break-inside: avoid;
}
.pdf-matrix-grid {
/* kompakt: Zellen 28pt hoch (war 60pt), Höhe gesamt halbiert ohne
dass die Symbole unleserlich werden. Cells leicht breiter (52pt)
als hoch — die Symbole bleiben mittig. */
display: grid;
grid-template-columns: 76pt repeat(5, 52pt);
grid-template-rows: 18pt repeat(5, 28pt);
gap: 1.5pt;
}
.pdf-matrix-grid .col-label {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 6.5pt;
font-weight: 700;
color: #009DA5;
text-transform: uppercase;
letter-spacing: 0.03em;
display: flex;
align-items: end;
justify-content: center;
padding-bottom: 2pt;
text-align: center;
line-height: 1.05;
}
.pdf-matrix-grid .row-label {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 7pt;
font-weight: 700;
color: #009DA5;
text-transform: uppercase;
letter-spacing: 0.02em;
display: flex;
align-items: center;
padding-right: 4pt;
line-height: 1.1;
}
.pdf-matrix-grid .cell {
border-radius: 1pt;
display: flex;
align-items: center;
justify-content: center;
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 11pt;
font-weight: 700;
}
.cell.r-pp { background: #889E33; color: #fff; }
.cell.r-p { background: #cddaa1; color: #44570a; }
.cell.r-0 { background: #b8b8b2; color: #4a4a44; }
.cell.r-n { background: #efc9c3; color: #931515; }
.cell.r-nn { background: #9A2A2A; color: #fff; }
.pdf-matrix-legend {
margin-top: 5pt;
display: flex;
flex-wrap: wrap;
gap: 3pt 12pt;
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 7.5pt;
color: #444;
}
.pdf-matrix-legend .legend-item {
display: inline-flex;
align-items: center;
gap: 3pt;
}
.pdf-matrix-legend .swatch {
width: 9pt; height: 9pt; border-radius: 1pt; flex-shrink: 0;
}
/* ── Schwerpunkt-Felder-Erklaerung (Ersatz fuer Klick im PDF) ── */
.pdf-schwerpunkt-list {
margin-top: 8pt;
}
.pdf-schwerpunkt-item {
border-left: 2pt solid;
padding: 6pt 10pt;
margin-bottom: 5pt;
background: #fafaf7;
border-radius: 1pt;
}
.pdf-schwerpunkt-item.pos { border-left-color: #889E33; }
.pdf-schwerpunkt-item.neg { border-left-color: #9A2A2A; }
.pdf-schwerpunkt-item.neu { border-left-color: #b8b8b2; }
.pdf-schwerpunkt-head {
display: flex;
align-items: baseline;
gap: 8pt;
margin-bottom: 3pt;
}
.pdf-schwerpunkt-code {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
font-weight: 700;
padding: 1pt 5pt;
border-radius: 2pt;
color: #fff;
background: #009DA5;
}
.pdf-schwerpunkt-rating {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
font-weight: 700;
color: #555;
}
.pdf-schwerpunkt-label {
font-size: 9.5pt;
font-weight: 700;
color: #1f1f1f;
}
.pdf-schwerpunkt-aspect {
font-size: 9pt;
line-height: 1.5;
color: #333;
}
/* ── Programm-Treue ── */
.pdf-fraktion {
border: 0.5pt solid #ddd;
border-radius: 2pt;
padding: 8pt 10pt;
margin-bottom: 8pt;
page-break-inside: avoid;
background: #fafaf7;
}
.pdf-fraktion-head {
display: flex;
align-items: baseline;
gap: 6pt;
flex-wrap: wrap;
margin-bottom: 5pt;
}
.pdf-fraktion-name {
font-size: 11pt;
font-weight: 700;
}
.pdf-pill {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 7pt;
text-transform: uppercase;
padding: 1pt 5pt;
border-radius: 1pt;
}
.pdf-pill.antrag { background: rgba(247, 148, 29, 0.18); color: #bf6c10; }
.pdf-pill.reg { background: rgba(0, 157, 165, 0.18); color: #1e6a90; }
.pdf-prog {
margin-top: 5pt;
padding-top: 5pt;
border-top: 0.5pt dashed #ccc;
}
.pdf-prog:first-of-type { border-top: none; padding-top: 0; }
.pdf-prog-row {
display: flex;
align-items: baseline;
gap: 8pt;
margin-bottom: 3pt;
}
.pdf-prog-label {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8.5pt;
font-weight: 700;
text-transform: uppercase;
color: #0d6f76;
letter-spacing: 0.04em;
}
.pdf-prog-spacer { flex: 1; }
.pdf-prog-score {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8.5pt;
font-weight: 700;
padding: 1pt 5pt;
border-radius: 2pt;
}
.pdf-prog-score.good { background: #cddaa1; color: #44570a; }
.pdf-prog-score.mid { background: #fff3cd; color: #7d5a00; }
.pdf-prog-score.low { background: #efc9c3; color: #a00000; }
.pdf-prog-text {
font-size: 9.5pt;
line-height: 1.5;
color: #333;
margin-bottom: 3pt;
}
.pdf-prog-zitat {
border-left: 2pt solid #009DA5;
background: #f3f8f8;
padding: 4pt 8pt;
margin: 3pt 0;
font-size: 9pt;
line-height: 1.4;
color: #333;
}
.pdf-prog-zitat .src {
display: block;
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
color: #555;
margin-top: 2pt;
}
.pdf-no-zitate {
font-size: 8.5pt;
line-height: 1.5;
color: #777;
font-style: italic;
background: #f5f5f5;
border-left: 2pt dashed #ccc;
padding: 4pt 8pt;
border-radius: 0 2pt 2pt 0;
}
/* ── Verbesserungsvorschlaege (Redline) ── */
.pdf-verbesserung {
margin-bottom: 10pt;
padding: 6pt 0;
page-break-inside: avoid;
}
.pdf-verbesserung-num {
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8pt;
color: #555;
margin-bottom: 4pt;
}
.pdf-redline {
font-size: 9.5pt;
line-height: 1.5;
background: #fafaf7;
padding: 6pt 8pt;
border-radius: 2pt;
}
.pdf-redline .ins { background: #cddaa1; padding: 0 2pt; }
.pdf-redline .del { background: #efc9c3; text-decoration: line-through; padding: 0 2pt; }
.pdf-redline-original { font-size: 9pt; color: #555; margin-bottom: 4pt; }
.pdf-verbesserung-begr {
font-size: 8.5pt;
color: #555;
line-height: 1.5;
margin-top: 4pt;
font-style: italic;
}
/* ── Abstimmungsergebnis ── */
.pdf-vote-card {
border: 0.5pt solid #ddd;
border-radius: 2pt;
padding: 6pt 8pt;
margin-bottom: 5pt;
background: #fafaf7;
page-break-inside: avoid;
}
.pdf-vote-head {
font-size: 10pt;
font-weight: 700;
margin-bottom: 4pt;
}
.pdf-vote-pills {
display: flex;
flex-wrap: wrap;
gap: 6pt 12pt;
font-family: 'JetBrains Mono', 'Source Code Pro', monospace;
font-size: 8.5pt;
}
.pdf-vote-side { font-weight: 700; }
.pdf-vote-pill {
display: inline-block;
padding: 1pt 5pt;
border-radius: 1pt;
margin-right: 2pt;
}
.pdf-vote-pill.ja { background: rgba(45, 164, 78, 0.15); color: #1a7f37; }
.pdf-vote-pill.nein { background: rgba(207, 34, 46, 0.15); color: #a40e26; }
.pdf-vote-pill.enth { background: rgba(110, 119, 129, 0.15); color: #57606a; }
.pdf-konsistenz {
font-size: 9pt;
padding: 5pt 8pt;
margin-bottom: 5pt;
border-left: 3pt solid;
border-radius: 2pt;
}
.pdf-konsistenz.conflict {
background: rgba(207, 34, 46, 0.07);
border-left-color: #cf222e;
}
.pdf-konsistenz.match {
background: rgba(45, 164, 78, 0.07);
border-left-color: #2da44e;
}
</style>
</head>
<body>
<!-- ─── Header + Title ─────────────────────────────────────────────── -->
<div class="pdf-header">
<div class="pdf-kicker">GWÖ-Antragsprüfung
{% if parlament_name %} · {{ parlament_name }}{% endif %}
</div>
<h1 class="pdf-title">{{ assessment.title }}</h1>
<div class="pdf-meta">
Drucksache <strong>{{ assessment.drucksache }}</strong>
{% if assessment.datum %} · eingebracht {{ assessment.datum }}{% endif %}
{% if assessment.fraktionen %}
— Antragsteller:
{% for f in assessment.fraktionen %}<span class="pill">{{ f }}</span>{% endfor %}
{% endif %}
</div>
{% if assessment.themen %}
<div class="pdf-themen">
{% for t in assessment.themen %}<span class="pdf-theme-chip">{{ t }}</span>{% endfor %}
</div>
{% endif %}
</div>
<!-- ─── Zusammenfassung + Kernpunkte ────────────────────────────────── -->
{% if assessment.antrag_zusammenfassung or assessment.antrag_kernpunkte %}
<div class="pdf-section">
<h2 class="pdf-h2">Zusammenfassung</h2>
{% if assessment.antrag_zusammenfassung %}
<p class="pdf-prose">{{ assessment.antrag_zusammenfassung }}</p>
{% endif %}
{% if assessment.antrag_kernpunkte %}
<div class="pdf-kernpunkte">
<div class="pdf-kernpunkte-label">Kernforderungen</div>
<ul>
{% for kp in assessment.antrag_kernpunkte %}<li>{{ kp }}</li>{% endfor %}
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- ─── Bewertungs-Block ────────────────────────────────────────────── -->
<div class="pdf-section">
<h2 class="pdf-h2">Bewertung</h2>
<div class="pdf-bewertung">
<div class="pdf-score-num">{{ "%.1f"|format(assessment.gwoe_score) }}<span class="slash">/10</span></div>
<div class="pdf-score-side">
<div class="pdf-score-label">Gemeinwohl-Score</div>
<div class="pdf-verdict">{{ assessment.empfehlung.value }}</div>
</div>
</div>
{% if assessment.gwoe_begruendung %}
<p class="pdf-bewertung-body">{{ assessment.gwoe_begruendung }}</p>
{% endif %}
</div>
<!-- ─── Stärken / Schwächen ────────────────────────────────────────── -->
{% if assessment.staerken or assessment.schwaechen %}
<div class="pdf-section">
<h2 class="pdf-h2">Stärken &amp; Schwächen</h2>
<div class="pdf-werte-grid">
{% if assessment.staerken %}
<div class="pdf-werte-card staerke">
<h4>Stärken</h4>
<ul>{% for s in assessment.staerken %}<li>{{ s }}</li>{% endfor %}</ul>
</div>
{% endif %}
{% if assessment.schwaechen %}
<div class="pdf-werte-card schwaeche">
<h4>Schwächen</h4>
<ul>{% for s in assessment.schwaechen %}<li>{{ s }}</li>{% endfor %}</ul>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- ─── Matrix 5×5 mit Achsen-Labels ───────────────────────────────── -->
<div class="pdf-section pdf-matrix-block">
<h2 class="pdf-h2">GWÖ-Matrix 5×5</h2>
<div class="pdf-matrix-grid">
<div></div>
<div class="col-label">Würde</div>
<div class="col-label">Solida­rität</div>
<div class="col-label">Nach­haltig­keit</div>
<div class="col-label">Gerech­tigkeit</div>
<div class="col-label">Trans­parenz</div>
{% set rows = [
('A', 'Lieferant:­innen'),
('B', 'Finanzen'),
('C', 'Verwal­tung'),
('D', 'Bürger:­innen'),
('E', 'Gesell­schaft & Natur'),
] %}
{% for r, r_label in rows %}
<div class="row-label">{{ r }} · {{ r_label }}</div>
{% for c in ['1','2','3','4','5'] %}
{% set cell = matrix_lookup.get(r ~ c, {}) %}
{% set rt = cell.get('rating', 0) %}
<div class="cell {% if rt >= 4 %}r-pp{% elif rt >= 1 %}r-p{% elif rt == 0 %}r-0{% elif rt <= -4 %}r-nn{% else %}r-n{% endif %}">{% if rt >= 4 %}++{% elif rt >= 1 %}+{% elif rt == 0 %}·{% elif rt <= -4 %}{% else %}{% endif %}</div>
{% endfor %}
{% endfor %}
</div>
<div class="pdf-matrix-legend">
<span class="legend-item"><span class="swatch" style="background:#889E33;"></span>++ stark fördernd</span>
<span class="legend-item"><span class="swatch" style="background:#cddaa1;"></span>+ fördernd</span>
<span class="legend-item"><span class="swatch" style="background:#b8b8b2;"></span>○ neutral</span>
<span class="legend-item"><span class="swatch" style="background:#efc9c3;"></span> widersprechend</span>
<span class="legend-item"><span class="swatch" style="background:#9A2A2A;"></span> stark widersprechend</span>
</div>
</div>
<!-- ─── Schwerpunkt-Felder erklärt (Ersatz für Matrix-Klick im PDF) ── -->
{% if matrix_top or matrix_bottom %}
<div class="pdf-section">
<h2 class="pdf-h2">Schwerpunkte erklärt</h2>
<p style="font-size:9pt;color:#555;margin-bottom:6pt;">
Die wichtigsten positiv und negativ wirkenden Bewertungsfelder mit der jeweiligen Begründung.
</p>
<div class="pdf-schwerpunkt-list">
{% for cell in matrix_top %}
<div class="pdf-schwerpunkt-item pos">
<div class="pdf-schwerpunkt-head">
<span class="pdf-schwerpunkt-code">{{ cell.field }}</span>
<span class="pdf-schwerpunkt-label">{{ cell.label }}</span>
<span class="pdf-schwerpunkt-rating">Bewertung: {% if cell.rating > 0 %}+{% endif %}{{ cell.rating }}</span>
</div>
{% if cell.aspect %}<div class="pdf-schwerpunkt-aspect">{{ cell.aspect }}</div>{% endif %}
</div>
{% endfor %}
{% for cell in matrix_bottom %}
<div class="pdf-schwerpunkt-item neg">
<div class="pdf-schwerpunkt-head">
<span class="pdf-schwerpunkt-code">{{ cell.field }}</span>
<span class="pdf-schwerpunkt-label">{{ cell.label }}</span>
<span class="pdf-schwerpunkt-rating">Bewertung: {{ cell.rating }}</span>
</div>
{% if cell.aspect %}<div class="pdf-schwerpunkt-aspect">{{ cell.aspect }}</div>{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- ─── Programm-Treue pro Fraktion ─────────────────────────────────── -->
{% if assessment.wahlprogramm_scores %}
<div class="pdf-section">
<h2 class="pdf-h2">Programm-Treue pro Fraktion</h2>
{% if assessment.fehlende_programme %}
<p style="font-size:9pt;color:#555;margin-bottom:6pt;">
<strong>Hinweis:</strong> Für folgende Parteien lag kein Wahl-/Parteiprogramm vor:
{{ assessment.fehlende_programme | join(", ") }}.
</p>
{% endif %}
{% for fs in assessment.wahlprogramm_scores %}
<div class="pdf-fraktion">
<div class="pdf-fraktion-head">
<span class="pdf-fraktion-name">{{ fs.fraktion }}</span>
{% if fs.ist_antragsteller %}<span class="pdf-pill antrag">Antragsteller:in</span>{% endif %}
{% if fs.ist_regierung %}<span class="pdf-pill reg">Regierungsfraktion</span>{% endif %}
</div>
{% for prog_key, prog_label in [("wahlprogramm","Wahlprogramm"), ("parteiprogramm","Parteiprogramm")] %}
{% set p = fs[prog_key] %}
{% set p_score = p.score | float %}
<div class="pdf-prog">
<div class="pdf-prog-row">
<span class="pdf-prog-label">{{ prog_label }}</span>
<span class="pdf-prog-spacer"></span>
<span class="pdf-prog-score {% if p_score >= 7 %}good{% elif p_score >= 4 %}mid{% else %}low{% endif %}">{{ "%.0f"|format(p_score) }}/10</span>
</div>
{% if p.begruendung %}<p class="pdf-prog-text">{{ p.begruendung }}</p>{% endif %}
{% if p.zitate %}
{% for z in p.zitate %}
<div class="pdf-prog-zitat">
„{{ z.text }}"
<span class="src">{{ z.quelle }}</span>
</div>
{% endfor %}
{% else %}
<div class="pdf-no-zitate">
Keine wörtlich passenden Stellen im {{ prog_label }} gefunden — Bewertung beruht auf inhaltlicher Auslegung.
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
</div>
{% endif %}
<!-- ─── Verbesserungsvorschläge (Redline-Diff) ─────────────────────── -->
{% if assessment.verbesserungen %}
<div class="pdf-section">
<h2 class="pdf-h2">Verbesserungsvorschläge</h2>
{% for v in assessment.verbesserungen %}
<div class="pdf-verbesserung">
{% if assessment.verbesserungen | length > 1 %}
<div class="pdf-verbesserung-num">Vorschlag {{ loop.index }} von {{ assessment.verbesserungen | length }}</div>
{% endif %}
{% if v.original %}
<div class="pdf-redline-original"><em>Original:</em> {{ v.original }}</div>
{% endif %}
{% if v.vorschlag %}
<div class="pdf-redline">{{ v.vorschlag | safe }}</div>
{% endif %}
{% if v.begruendung %}
<div class="pdf-verbesserung-begr">Begründung: {{ v.begruendung }}</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
<!-- ─── Abstimmungsergebnis (falls vorhanden) ──────────────────────── -->
{% if plenum_votes %}
<div class="pdf-section">
<h2 class="pdf-h2">Abstimmungsergebnis</h2>
{% if konsistenz_state %}
<div class="pdf-konsistenz {{ konsistenz_state }}">
<strong>{% if konsistenz_state == 'conflict' %}Mehrheit gegen GWÖ-Empfehlung{% else %}Mehrheit deckt sich mit GWÖ-Empfehlung{% endif %}</strong>
— Empfohlen: <em>{{ assessment.empfehlung.value }}</em>; Beschluss: <em>{{ konsistenz_decisive | capitalize }}</em>.
</div>
{% endif %}
{% for v in plenum_votes %}
<div class="pdf-vote-card">
<div class="pdf-vote-head">{{ v.ergebnis | capitalize }}{% if v.einstimmig %} · einstimmig{% endif %}{% if v.quelle_protokoll %} · {{ v.quelle_protokoll }}{% endif %}</div>
<div class="pdf-vote-pills">
{% if v.fraktionen_ja %}
<div><span class="pdf-vote-side" style="color:#1a7f37;">Ja:</span>
{% for f in v.fraktionen_ja %}<span class="pdf-vote-pill ja">{{ f }}</span>{% endfor %}
</div>
{% endif %}
{% if v.fraktionen_nein %}
<div><span class="pdf-vote-side" style="color:#a40e26;">Nein:</span>
{% for f in v.fraktionen_nein %}<span class="pdf-vote-pill nein">{{ f }}</span>{% endfor %}
</div>
{% endif %}
{% if v.fraktionen_enthaltung %}
<div><span class="pdf-vote-side" style="color:#57606a;">Enth.:</span>
{% for f in v.fraktionen_enthaltung %}<span class="pdf-vote-pill enth">{{ f }}</span>{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
</body>
</html>