feat(v3): single-column Restructure — neue Reihenfolge, CD-konforme Radien

User-Spec: 1a (single column überall), CD-Rundungen subtil 2-4 px,
keine 8px-Pills, keine Kreise. v3 wird nach Reife der neue Default.

Reihenfolge:
1. Metadaten/Titel
2. Zusammenfassung
3. Bewertung — Score xl + Empfehlung daneben + verdict_body voll breit
4. Merken + Bewertung treffend
5. Matrix 5×5 (volle Profi-Variante mit Klick-Modal)
6. Programm-Treue — pro Partei: WP-Zeile + PP-Zeile (default closed),
   Klick auf die Zeile expandiert Begründung + Belege. Score-Chip
   rechtsbündig.
7. Verbesserungsvorschläge — volle Breite, kein max-width
8. Abstimmungsergebnis — Plenum + namentliche Abstimmung
9. Rest — PDF/Teilen/Re-analyze/Historie + Stärken/Schwächen +
   Konsistenz-Hinweis + Marker-Legende + News-Box + Kommentare

v2-Templates unangetastet (Sub-Blocks vom letzten Commit beibehalten,
nur main-Block wird in v3 komplett überschrieben). v2-JS-Handler
bleiben funktionsfähig — gleiche DOM-IDs (v2-merkliste-btn, v2-vote-up,
v2-comments-list, v2-matrix-field-modal, …) im neuen Layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-07 10:51:33 +02:00
parent 895187ac36
commit 4de2df9cea
2 changed files with 1010 additions and 510 deletions

View File

@ -1,23 +1,615 @@
/* /*
* v3.css Bürgerinnen-Modus-Erweiterung über v2.css * v3.css Bürger:innen-Modus, single-column.
* *
* v3 = Sandbox für Issues #184 (CD-Konformität) und #185 (Bürgerinnen- * CD-Linie: ECOnGOOD (Manual Juni 2024). Subtile Rundungen 2-4 px,
* Perspektive). Lädt nach v2.css und überschreibt selektiv. v2-Endpoints * keine Kreise (außer dort, wo es semantisch unvermeidbar ist), keine
* bleiben unverändert. * 8px-Pill-Optik. Farben durchgängig aus tokens.css. Inline-Styles in
* * den Templates auf Mindestmaß; alle Formatierungen über Klassen.
* Konvention:
* - Klassen mit Präfix `v3-` sind v3-spezifisch.
* - Inline-Styles in v3-Templates möglichst vermeiden (Lint-Hook folgt in #184).
*/ */
/* ── Topbar: Beta-Pill + Modus-Toggle ──────────────────────────────── */ /* ── Page-Container: max-width zentriert, single column ────────────── */
.v3-page {
max-width: 880px;
margin: 0 auto;
padding: 0 4px;
}
.v3-back {
font-family: var(--font-mono);
font-size: 11px;
margin: 0 0 16px;
}
/* ── Sections: vertikale Reihung ───────────────────────────────────── */
.v3-section {
margin: 0 0 24px;
padding: 0 0 20px;
border-bottom: 1px solid var(--hairline);
}
.v3-section:last-of-type {
border-bottom: none;
}
.v3-h3 {
font-family: var(--font-sans);
font-weight: 900;
font-size: 13px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--ecg-dark);
margin: 0 0 10px;
}
.v3-prose {
font-size: 14.5px;
line-height: 1.55;
color: var(--ecg-dark);
margin: 0;
}
/* ── 1. Metadaten / Titel ──────────────────────────────────────────── */
.v3-meta {
border-bottom: 1px solid var(--hairline);
padding-bottom: 14px;
margin-bottom: 18px;
}
.v3-antrag-id {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--ecg-dark);
opacity: 0.75;
margin-bottom: 6px;
}
.v3-title {
font-family: var(--font-sans);
font-weight: 900;
font-size: 30px;
line-height: 1.18;
color: var(--ecg-dark);
margin: 0 0 8px;
}
.v3-byline {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ecg-dark);
opacity: 0.7;
}
/* ── 3. Bewertung: Score xl + Empfehlung daneben + Body voll breit ─── */
.v3-bewertung-head {
display: flex;
align-items: baseline;
gap: 18px;
flex-wrap: wrap;
margin-bottom: 14px;
}
.v3-score-num {
font-family: var(--font-sans);
font-weight: 900;
font-size: 56px;
line-height: 1;
color: var(--ecg-dark);
}
.v3-score-slash {
font-size: 30px;
font-weight: 700;
color: var(--ecg-dark);
opacity: 0.4;
margin-left: 2px;
}
.v3-empfehlung {
font-family: var(--font-sans);
font-weight: 900;
font-size: 22px;
line-height: 1.15;
color: var(--ecg-dark);
letter-spacing: -0.01em;
flex: 1 1 auto;
}
.v3-empfehlung.good { color: var(--score-chip-green-fg); }
.v3-empfehlung.mid { color: var(--ecg-dark); }
.v3-empfehlung.low { color: var(--score-chip-red-fg); }
.v3-bewertung-body {
font-size: 14.5px;
line-height: 1.6;
color: var(--ecg-dark);
margin: 0;
width: 100%;
}
/* ── 4. User-Aktionen-Zeile (Merken + Vote-treffend) ───────────────── */
.v3-userrow {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.v3-userrow-vote {
display: flex;
align-items: center;
gap: 10px;
}
.v3-userrow-label {
font-family: var(--font-mono);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--ecg-dark);
opacity: 0.6;
}
.v3-vote-buttons {
display: flex;
gap: 6px;
}
.v3-action-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 12px;
border: 1px solid var(--hairline);
border-radius: 3px;
background: var(--paper);
color: var(--ecg-dark);
font-family: var(--font-mono);
font-size: 11px;
cursor: pointer;
text-decoration: none;
}
.v3-action-btn:hover { border-color: var(--ecg-blue); color: var(--ecg-blue); }
.v3-action-btn.primary {
background: var(--ecg-blue);
color: #fff;
border-color: var(--ecg-blue);
font-weight: 700;
}
.v3-action-btn.primary:hover { background: var(--ecg-dark); border-color: var(--ecg-dark); color: #fff; }
.v3-action-btn.v3-action-muted { opacity: 0.8; }
/* ── 6. Programm-Treue ─────────────────────────────────────────────── */
.v3-fraktionen {
display: flex;
flex-direction: column;
gap: 12px;
}
.v3-fraktion {
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: 4px;
padding: 12px 14px;
}
.v3-fraktion-head {
display: flex;
align-items: baseline;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 6px;
}
.v3-fraktion-name {
font-family: var(--font-sans);
font-weight: 900;
font-size: 14px;
letter-spacing: 0.02em;
color: var(--ecg-dark);
}
.v3-pill {
font-family: var(--font-mono);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 1px 6px;
border-radius: 2px;
}
.v3-pill-antrag { color: #bf6c10; background: rgba(247, 148, 29, 0.15); }
.v3-pill-reg { color: #1e6a90; background: rgba(0, 157, 165, 0.15); }
.v3-prog {
border-top: 1px solid var(--hairline);
}
.v3-prog:first-of-type { border-top: 1px dashed var(--hairline); margin-top: 4px; }
.v3-prog > summary {
list-style: none;
cursor: pointer;
user-select: none;
outline-offset: 2px;
}
.v3-prog > summary::-webkit-details-marker { display: none; }
.v3-prog-row {
display: flex;
align-items: baseline;
gap: 8px;
padding: 10px 0 8px;
}
.v3-prog-label {
font-family: var(--font-mono);
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--ecg-dark);
font-weight: 600;
transition: color 0.12s ease;
}
.v3-prog-label::before {
content: "▸";
display: inline-block;
margin-right: 6px;
font-size: 10px;
opacity: 0.55;
transition: transform 0.15s ease;
}
.v3-prog[open] > summary .v3-prog-label::before {
transform: rotate(90deg);
}
.v3-prog > summary:hover .v3-prog-label { color: var(--ecg-blue); }
.v3-prog-spacer { flex: 1 1 auto; }
.v3-prog-score {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 700;
padding: 2px 8px;
border-radius: 3px;
}
.v3-prog-score.good { background: var(--score-chip-green-bg); color: var(--score-chip-green-fg); }
.v3-prog-score.mid { background: var(--score-chip-mid-bg); color: var(--score-chip-mid-fg); }
.v3-prog-score.low { background: var(--score-chip-red-bg); color: var(--score-chip-red-fg); }
.v3-prog-body {
padding: 0 0 12px 16px;
}
.v3-prog-text {
font-size: 13.5px;
line-height: 1.55;
color: var(--ecg-dark);
margin: 0 0 8px;
}
.v3-prog-zitate { margin-top: 6px; }
/* ── 7. Verbesserungsvorschläge: volle Breite, kein max-width ──────── */
.v3-verbesserungen p,
.v3-verbesserungen .v3-verbesserung-begr {
max-width: none;
}
.v3-verbesserung {
margin-bottom: 18px;
}
.v3-verbesserung:last-child { margin-bottom: 0; }
.v3-verbesserung-num {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ecg-dark);
opacity: 0.65;
margin-bottom: 4px;
}
.v3-verbesserung-begr {
font-family: var(--font-mono);
font-size: 12px;
color: var(--ecg-dark);
opacity: 0.78;
line-height: 1.55;
margin: 6px 0 0;
}
/* ── 8. Abstimmungsergebnis ────────────────────────────────────────── */
.v3-vote-card {
border: 1px solid var(--hairline);
border-radius: 4px;
padding: 12px 14px;
background: var(--paper);
margin-bottom: 10px;
}
.v3-vote-card:last-child { margin-bottom: 4px; }
.v3-vote-card-head {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 6px;
}
.v3-vote-ergebnis {
font-family: var(--font-sans);
font-weight: 700;
font-size: 14px;
}
.v3-vote-quelle {
font-family: var(--font-mono);
font-size: 10px;
color: var(--ecg-blue);
text-decoration: none;
border-bottom: 1px solid rgba(0, 157, 165, 0.35);
}
.v3-vote-quelle:hover { background: rgba(0, 157, 165, 0.08); }
.v3-vote-bar {
display: flex;
height: 10px;
border-radius: 3px;
overflow: hidden;
border: 1px solid var(--hairline);
margin: 6px 0 2px;
}
.v3-vote-bar-ja { background: #2da44e; }
.v3-vote-bar-enth { background: #6e7781; }
.v3-vote-bar-nein { background: #cf222e; }
.v3-vote-bar-caption {
font-family: var(--font-mono);
font-size: 9px;
color: var(--ecg-dark);
opacity: 0.55;
margin-bottom: 8px;
}
.v3-vote-pills {
display: flex;
flex-wrap: wrap;
gap: 12px;
font-family: var(--font-mono);
font-size: 11px;
}
.v3-vote-side { font-weight: 700; }
.v3-vote-side.ja { color: #2da44e; }
.v3-vote-side.nein { color: #cf222e; }
.v3-vote-side.enth { color: #6e7781; }
.v3-vote-pill {
display: inline-block;
padding: 1px 6px;
border-radius: 2px;
margin-right: 3px;
}
.v3-vote-pill.ja { background: color-mix(in srgb, #2da44e 15%, transparent); color: #1a7f37; }
.v3-vote-pill.nein { background: color-mix(in srgb, #cf222e 15%, transparent); color: #a40e26; }
.v3-vote-pill.enth { background: color-mix(in srgb, #6e7781 15%, transparent); color: #57606a; }
/* ⚠/!-Marker — kein 8px-Pill mehr, einfache Badge-Form ohne Rundung */
.v3-marker {
display: inline-block;
margin-left: 4px;
padding: 0 4px;
border-radius: 2px;
cursor: help;
font-weight: 700;
border: 1px solid transparent;
}
.v3-marker:hover, .v3-marker:focus-visible {
outline: none;
border-color: currentColor;
}
.v3-marker.heuchelei { color: #a40e26; background: rgba(207, 34, 46, 0.10); }
.v3-marker.opp { color: #bf8700; background: rgba(191, 135, 0, 0.10); font-style: italic; }
.v3-vote-source {
font-family: var(--font-mono);
font-size: 10px;
opacity: 0.5;
margin-top: 4px;
}
.v3-namentlich {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ecg-dark);
opacity: 0.78;
display: flex;
flex-direction: column;
gap: 4px;
}
/* ── 9. Rest-Block ─────────────────────────────────────────────────── */
.v3-rest {
margin-top: 28px;
padding-top: 20px;
border-top: 1px solid var(--hairline);
display: flex;
flex-direction: column;
gap: 22px;
}
.v3-rest-block {
font-size: 13px;
}
.v3-rest-block h3 {
margin-bottom: 10px;
}
.v3-rest-aktions {
display: flex;
gap: 16px;
flex-wrap: wrap;
font-family: var(--font-mono);
font-size: 11px;
}
.v3-rest-aktions a {
color: var(--ecg-blue);
border-bottom: 1px solid rgba(0, 157, 165, 0.35);
}
.v3-share-buttons {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.v3-loading {
font-family: var(--font-mono);
font-size: 12px;
color: var(--ecg-dark);
opacity: 0.5;
}
.v3-konsistenz {
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
line-height: 1.5;
border-left: 3px solid;
}
.v3-konsistenz.conflict {
background: color-mix(in srgb, #cf222e 8%, transparent);
border-left-color: #cf222e;
}
.v3-konsistenz.match {
background: color-mix(in srgb, #2da44e 8%, transparent);
border-left-color: #2da44e;
}
.v3-marker-legend {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ecg-dark);
opacity: 0.85;
display: flex;
flex-direction: column;
gap: 6px;
}
.v3-news-box {
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: 4px;
padding: 14px 16px;
}
.v3-news-meta {
font-size: 11px;
font-family: var(--font-mono);
opacity: 0.65;
margin: 0 0 10px;
}
.v3-comments .v3-comment-label {
font-family: var(--font-mono);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--ecg-dark);
opacity: 0.6;
margin-bottom: 6px;
}
.v3-comment-textarea {
width: 100%;
box-sizing: border-box;
padding: 8px 10px;
border: 1px solid var(--hairline);
border-radius: 3px;
font-family: var(--font-sans);
font-size: 13px;
background: var(--surface);
color: var(--ecg-dark);
resize: vertical;
margin-bottom: 8px;
outline: none;
}
.v3-comment-controls {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.v3-comment-vis {
padding: 5px 8px;
border: 1px solid var(--hairline);
border-radius: 3px;
font-family: var(--font-mono);
font-size: 11px;
background: var(--surface);
color: var(--ecg-dark);
}
/* ── Modals (Matrix-Feld-Info) ─────────────────────────────────────── */
.v3-modal {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 9100;
align-items: center;
justify-content: center;
}
.v3-modal-card {
background: var(--ecg-card-bg);
border: 1px solid var(--ecg-border);
border-radius: 4px;
padding: 24px 28px;
min-width: 300px;
max-width: 540px;
font-family: var(--font-sans);
font-size: 14px;
color: var(--ecg-dark);
line-height: 1.55;
box-shadow: 0 8px 32px rgba(0,0,0,0.18);
}
.v3-modal-head {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 12px;
margin-bottom: 6px;
}
.v3-modal-title {
font-family: var(--font-sans);
font-size: 16px;
font-weight: 900;
color: var(--ecg-teal);
letter-spacing: 0.03em;
}
.v3-modal-rating {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 700;
padding: 2px 8px;
border-radius: 2px;
white-space: nowrap;
}
.v3-modal-close {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
color: var(--ecg-dark);
opacity: 0.55;
padding: 0;
line-height: 1;
margin-left: auto;
}
.v3-modal-section {
margin: 14px 0 0;
}
.v3-modal-section--bordered {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid var(--hairline);
}
.v3-modal-sublabel {
font-family: var(--font-mono);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--ecg-dark);
opacity: 0.6;
margin-bottom: 4px;
}
.v3-modal-itemlabel {
font-weight: 700;
font-size: 14px;
margin-bottom: 4px;
}
.v3-modal-itemtext {
font-size: 13.5px;
line-height: 1.55;
margin: 0;
}
/* ── Topbar Beta-Badge + Modus-Toggle (von v3-base) ────────────────── */
.v3-beta-badge { .v3-beta-badge {
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 9px; font-size: 9px;
letter-spacing: 0.12em; letter-spacing: 0.12em;
text-transform: uppercase; text-transform: uppercase;
padding: 2px 7px; padding: 1px 7px;
border-radius: 9px; border-radius: 2px;
background: var(--ecg-green); background: var(--ecg-green);
color: #fff; color: #fff;
font-weight: 700; font-weight: 700;
@ -25,7 +617,6 @@
.v3-modus-toggle { .v3-modus-toggle {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 6px;
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 10px; font-size: 10px;
text-transform: uppercase; text-transform: uppercase;
@ -38,257 +629,11 @@
} }
.v3-modus-toggle:hover { opacity: 1; color: var(--ecg-blue); } .v3-modus-toggle:hover { opacity: 1; color: var(--ecg-blue); }
/* ── 1. Drucksache-ID: ruhiger Untertitel ──────────────────────────── */ /* ── Responsive: kleinere Schrift & engerer Padding ab schmaler ───── */
.v3-antrag-id { @media (max-width: 640px) {
/* erbt v2-antrag-id; nichts zu ändern auf der Ebene */ .v3-page { padding: 0 12px; }
} .v3-title { font-size: 24px; }
.v3-score-num { font-size: 44px; }
/* ── 2. Score-Hero: Wort-Etikett vor Zahl ──────────────────────────── */ .v3-empfehlung { font-size: 18px; }
.v3-score-hero { .v3-userrow { flex-direction: column; align-items: flex-start; }
background: var(--surface);
border: 1px solid var(--hairline);
border-left: 6px solid var(--ecg-blue);
border-radius: 6px;
padding: 18px 20px;
margin: 8px 0 18px;
}
.v3-score-hero.good { border-left-color: var(--ecg-green); }
.v3-score-hero.mid { border-left-color: var(--ecg-blue); }
.v3-score-hero.low { border-left-color: var(--redline-contra, #cf222e); }
.v3-score-label {
font-family: var(--font-sans);
font-size: 22px;
font-weight: 900;
line-height: 1.1;
color: var(--ecg-dark);
margin-bottom: 4px;
}
.v3-score-hero.good .v3-score-label { color: #1a7f37; }
.v3-score-hero.low .v3-score-label { color: #a40e26; }
.v3-score-body {
font-size: 14px;
line-height: 1.5;
color: var(--ecg-dark);
margin: 6px 0 10px;
}
.v3-score-meta {
font-family: var(--font-mono);
font-size: 11px;
color: var(--ecg-dark);
opacity: 0.75;
border-top: 1px dashed var(--hairline);
padding-top: 8px;
}
.v3-score-meta strong { font-weight: 700; }
/* ── 3. 5-Werte-Bars (Matrix-Vereinfachung) ────────────────────────── */
.v3-werte-list {
display: flex;
flex-direction: column;
gap: 6px;
margin: 10px 0 16px;
}
.v3-wert-row {
display: grid;
grid-template-columns: minmax(140px, 1fr) 2.5fr auto;
align-items: center;
gap: 10px;
font-size: 13px;
}
.v3-wert-label {
color: var(--ecg-dark);
font-weight: 600;
line-height: 1.3;
}
.v3-wert-bar {
position: relative;
height: 10px;
}
.v3-wert-track {
position: absolute;
inset: 0;
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: 5px;
overflow: hidden;
}
.v3-wert-mid {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 1px;
background: var(--hairline);
}
.v3-wert-fill {
position: absolute;
top: 0;
bottom: 0;
border-radius: 3px;
min-width: 2px;
}
.v3-wert-fill.pos { background: var(--ecg-green); }
.v3-wert-fill.neu { background: var(--ecg-dark); opacity: 0.3; }
.v3-wert-fill.neg { background: var(--redline-contra, #cf222e); }
.v3-wert-num {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 700;
color: var(--ecg-dark);
min-width: 36px;
text-align: right;
}
.v3-matrix-details {
margin-top: 10px;
padding: 8px 12px;
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: 5px;
}
.v3-matrix-details > summary {
cursor: pointer;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.03em;
color: var(--ecg-dark);
user-select: none;
list-style: none;
}
.v3-matrix-details > summary::-webkit-details-marker { display: none; }
.v3-matrix-details > summary::before {
content: "▸ ";
display: inline-block;
margin-right: 4px;
transition: transform 0.15s ease;
}
.v3-matrix-details[open] > summary::before {
transform: rotate(90deg);
}
/* ── 4. Glossar-Tooltips ───────────────────────────────────────────── */
.v3-glossar {
display: inline;
border-bottom: 1px dotted var(--ecg-blue);
cursor: help;
color: var(--ecg-blue);
}
.v3-glossar:hover,
.v3-glossar:focus-visible {
outline: none;
background: rgba(0, 157, 165, 0.12);
border-bottom-style: solid;
}
.v3-glossar-modal {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 9100;
align-items: center;
justify-content: center;
}
.v3-glossar-card {
background: var(--ecg-card-bg, #fff);
border: 1px solid var(--ecg-border, #ddd);
border-radius: 8px;
padding: 22px 26px;
min-width: 280px;
max-width: 480px;
font-family: var(--font-sans);
color: var(--ecg-dark);
box-shadow: 0 8px 32px rgba(0,0,0,0.18);
}
.v3-glossar-head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 10px;
}
.v3-glossar-head strong {
font-family: var(--font-display, inherit);
font-size: 16px;
font-weight: 900;
color: var(--ecg-teal, #009da5);
letter-spacing: 0.03em;
}
.v3-glossar-close {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
color: var(--ecg-dark);
opacity: 0.55;
padding: 0;
line-height: 1;
}
.v3-glossar-card p {
margin: 0;
font-size: 14px;
line-height: 1.55;
}
/* ── 5. Collapsible-Sections (Verbesserungen, Kommentare default zu) ─ */
.v3-collapsible {
margin-top: 24px;
padding: 8px 0;
border-top: 1px solid var(--hairline);
}
.v3-collapsible > summary {
cursor: pointer;
list-style: none;
user-select: none;
display: flex;
align-items: baseline;
gap: 12px;
padding: 4px 0;
}
.v3-collapsible > summary::-webkit-details-marker { display: none; }
.v3-collapsible > summary > h3 {
margin: 0;
display: inline-flex;
align-items: baseline;
gap: 8px;
}
.v3-collapsible > summary > h3::before {
content: "▸";
display: inline-block;
font-family: var(--font-mono);
font-size: 12px;
color: var(--ecg-dark);
opacity: 0.55;
transition: transform 0.15s ease;
}
.v3-collapsible[open] > summary > h3::before {
transform: rotate(90deg);
}
.v3-collapsible-hint {
font-family: var(--font-mono);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--ecg-dark);
opacity: 0.5;
}
.v3-comments {
margin-top: 40px;
padding-top: 20px;
}
/* ── 6. Aktions-Links ──────────────────────────────────────────────── */
.v3-aktions {
margin-top: 24px;
font-family: var(--font-mono);
font-size: 11px;
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.v3-aktions a {
color: var(--ecg-blue);
border-bottom: 1px solid rgba(0, 157, 165, 0.35);
} }

View File

@ -1,302 +1,457 @@
{# ───────────────────────────────────────────────────────────────────── {# ─────────────────────────────────────────────────────────────────────
v3/screens/antrag_detail.html — Bürger:innen-Modus v3/screens/antrag_detail.html — Bürger:innen-Modus, single column
Override-Strategie: extendet v2-Screen, ersetzt nur die Sub-Blocks, Override-Strategie: extendet v2/screens/antrag_detail.html und
die in v3 vereinfacht werden. Block-Liste: ersetzt nur `block main` komplett. body_scripts erbt via super(), so
- antrag_id_section — entschärfte Drucksache-Sprache mit Glossar dass alle v2-JS-Handler (vote, merkliste, share, comments, history,
- score_hero_section — Wort-Etikett groß, Zahl klein darunter matrix-modal) ohne Code-Duplikation weiterlaufen — sie hängen sich an
- matrix_section — 5 Werte default + <details> für 5×5 DOM-IDs (v2-vote-up, v2-merkliste-btn, v2-comments-list, …), die wir
- verbesserungen_section — default kollabiert in der neuen Layout-Reihenfolge wiederverwenden.
- aktions_section — JSON-Export raus, nur PDF + Permalink
- comments_section — default kollabiert Reihenfolge (single column):
1. Metadaten/Titel
2. Zusammenfassung
3. Bewertung (Score xl + Empfehlung daneben + Begründung darunter)
4. Merken / Bewertung treffend
5. Matrix 5×5
6. Programm-Treue (default-closed Klappmechanismus pro Programm)
7. Verbesserungsvorschläge (volle Breite, nicht kollabiert)
8. Abstimmungsergebnis
9. Rest: PDF/Teilen/Neu/Historie + Stärken/Schwächen + Konsistenz +
Marker-Legende + News + Kommentare
───────────────────────────────────────────────────────────────────── #} ───────────────────────────────────────────────────────────────────── #}
{% extends "v2/screens/antrag_detail.html" %} {% extends "v2/screens/antrag_detail.html" %}
{% from "v2/components/score_hero.html" import score_hero %}
{% from "v2/components/matrix_mini.html" import matrix_mini %}
{% from "v2/components/quote_card.html" import quote_card %}
{% from "v2/components/redline.html" import redline %}
{% block head_extra %} {% block head_extra %}
{{ super() }} {{ super() }}
<link rel="stylesheet" href="/static/v3/v3.css?v={{ app_version|default('1') }}"> <link rel="stylesheet" href="/static/v3/v3.css?v={{ app_version|default('1') }}">
{% endblock %} {% endblock %}
{# ─── 1. Drucksache-ID: weniger Behörden-Sprache, Glossar-Hinweise ─── #} {% block main %}
{% block antrag_id_section %} {% if error is defined and error %}
<div class="v2-antrag-id v3-antrag-id"> <div class="v2-kasten" style="border-color:var(--redline-contra);margin-top:32px;">
Antrag im <h3 style="color:var(--redline-contra);">Antrag nicht gefunden</h3>
{% if antrag.bundesland == "BU" %}Bundestag <p>{{ error }}</p>
{% elif antrag.bundesland %}Landtag {{ antrag.bundesland }} <p><a href="/">← Zurück zur Übersicht</a></p>
{% else %}Parlament{% endif %} </div>
{% if antrag.drucksache %} {% elif antrag is defined and antrag %}
· <span class="v3-glossar" data-glossar="drucksache" tabindex="0" role="button"
aria-label="Glossar: Drucksache">Drucksache</span> <div class="v3-page">
{{ antrag.drucksache }}
{% endif %} <p class="v3-back">
<a href="/">← Zurück zur Übersicht</a>
</p>
{# 1 ── Metadaten + Titel ─────────────────────────────────────────── #}
<section class="v3-section v3-meta">
<div class="v3-antrag-id">
{{ antrag.bundesland | default("") }}
{% if antrag.drucksache %} · Drs. {{ antrag.drucksache }}{% endif %}
{% if antrag.typ %} · {{ antrag.typ }}{% endif %}
{% if antrag.datum %} · eingebracht {{ antrag.datum }}{% endif %} {% if antrag.datum %} · eingebracht {{ antrag.datum }}{% endif %}
</div> </div>
{% endblock %} <h1 class="v3-title">{{ antrag.title | default("Antrag") }}</h1>
{% if antrag.parteien or antrag.analysiert %}
<div class="v3-byline">
{% if antrag.parteien %}Eingebracht von {{ antrag.parteien | join(", ") }}{% endif %}
{% if antrag.analysiert %} — Analyse {{ antrag.analysiert }}{% endif %}
{% if antrag.modell %}, {{ antrag.modell }}{% endif %}
{% if antrag.zitate_count %} · {{ antrag.zitate_count }} Zitat{{ "e" if antrag.zitate_count != 1 else "" }} verifiziert{% endif %}
</div>
{% endif %}
</section>
{# ─── 2. Score-Hero: Wort-Etikett groß, Zahl klein ──────────────────── #} {# 2 ── Zusammenfassung ───────────────────────────────────────────── #}
{% block score_hero_section %} {% if antrag.zusammenfassung %}
{% set s = (antrag.score | default(0)) | float %} <section class="v3-section">
{% if s >= 7 %} <h3 class="v3-h3">Zusammenfassung</h3>
{% set v3_label = antrag.verdict_title or "Stark gemeinwohlfördernd" %} <p class="v3-prose">{{ antrag.zusammenfassung }}</p>
{% set v3_class = "good" %} </section>
{% elif s >= 4 %} {% endif %}
{% set v3_label = antrag.verdict_title or "Gemischt" %}
{% set v3_class = "mid" %}
{% else %}
{% set v3_label = antrag.verdict_title or "Widerspricht dem Gemeinwohl" %}
{% set v3_class = "low" %}
{% endif %}
<div class="v3-score-hero {{ v3_class }}" {# 3 ── Bewertung ─────────────────────────────────────────────────── #}
role="region" aria-label="Bewertung: {{ v3_label }} ({{ '%.1f'|format(s) }} von 10)"> <section class="v3-section v3-bewertung">
<div class="v3-score-label">{{ v3_label }}</div> {% set s = (antrag.score | default(0)) | float %}
<div class="v3-bewertung-head">
<div class="v3-score-num">
{{ "%.1f"|format(s) }}<span class="v3-score-slash">/10</span>
</div>
{% if antrag.verdict_title %}
<div class="v3-empfehlung
{% if s >= 7 %}good{% elif s >= 4 %}mid{% else %}low{% endif %}">
{{ antrag.verdict_title }}
</div>
{% endif %}
</div>
{% if antrag.verdict_body %} {% if antrag.verdict_body %}
<div class="v3-score-body">{{ antrag.verdict_body }}</div> <p class="v3-bewertung-body">{{ antrag.verdict_body }}</p>
{% endif %} {% endif %}
<div class="v3-score-meta"> </section>
<span class="v3-glossar" data-glossar="gwoe-score" tabindex="0" role="button"
aria-label="Glossar: GWÖ-Score">Gemeinwohl-Score</span>:
<strong>{{ "%.1f"|format(s) }} von 10</strong>
</div>
</div>
{% endblock %}
{# ─── 3. Matrix: 5 Werte default, volle 5×5 in <details> ─────────────── #} {# 4 ── Merken + Bewertung treffend (User-Aktionen) ──────────────── #}
{% block matrix_section %} <section class="v3-section v3-userrow">
{% if antrag.matrix %} <button id="v2-merkliste-btn" onclick="v2DetailMerklisteToggle()" class="v3-action-btn">
<h3 class="v2-h3">Beitrag zu den 5 Gemeinwohl-Werten</h3> <span id="v2-merkliste-star"></span>
<span id="v2-merkliste-label">Merken</span>
</button>
<div class="v3-userrow-vote">
<span class="v3-userrow-label">Bewertung treffend?</span>
<div id="v2-vote-overall" class="v3-vote-buttons">
<button id="v2-vote-up" onclick="v2DetailCastVote('{{ antrag.drucksache | e }}','up')" class="v3-action-btn">
👍 <span id="v2-vote-up-count">0</span>
</button>
<button id="v2-vote-down" onclick="v2DetailCastVote('{{ antrag.drucksache | e }}','down')" class="v3-action-btn">
👎 <span id="v2-vote-down-count">0</span>
</button>
</div>
</div>
</section>
{# Pro Wert (Spalte 1..5) Durchschnitt aus den 5 Berührungsgruppen-Zellen. {# 5 ── Matrix 5×5 (volle Profi-Variante, mit Klick-Modal) ────────── #}
Skala bleibt -5..+5 wie in der DB. #} {% if antrag.matrix %}
{% set v3_werte = [ <section class="v3-section">
("1", "Menschenwürde"), <h3 class="v3-h3">Matrix 2.0 · 25 Felder</h3>
("2", "Solidarität"),
("3", "Ökologische Nachhaltigkeit"),
("4", "Soziale Gerechtigkeit"),
("5", "Transparenz & Demokratie"),
] %}
<div class="v3-werte-list">
{% for col, label in v3_werte %}
{% set ns = namespace(sum=0, cnt=0) %}
{% for row in ["A","B","C","D","E"] %}
{% set cell = antrag.matrix[row ~ col] | default(none) %}
{% if cell %}
{% set ns.sum = ns.sum + (cell.rating | int) %}
{% set ns.cnt = ns.cnt + 1 %}
{% endif %}
{% endfor %}
{% set avg = (ns.sum / ns.cnt) if ns.cnt else 0 %}
{% set pct = ((avg + 5) / 10 * 100) %}
<div class="v3-wert-row">
<div class="v3-wert-label">{{ label }}</div>
<div class="v3-wert-bar" aria-hidden="true">
<div class="v3-wert-track">
<div class="v3-wert-mid"></div>
<div class="v3-wert-fill {% if avg >= 1 %}pos{% elif avg <= -1 %}neg{% else %}neu{% endif %}"
style="left: {{ 'calc(50% - ' ~ ((-avg / 5 * 50)) ~ '%)' if avg < 0 else '50%' }};
width: {{ ((avg if avg > 0 else -avg) / 5 * 50) }}%;"></div>
</div>
</div>
<div class="v3-wert-num"
title="Durchschnitt der 5 Berührungsgruppen — Skala 5 bis +5">
{% if avg > 0 %}+{% endif %}{{ "%.1f"|format(avg) }}
</div>
</div>
{% endfor %}
</div>
<details class="v3-matrix-details">
<summary>
Volle <span class="v3-glossar" data-glossar="gwoe-matrix" tabindex="0" role="button"
aria-label="Glossar: GWÖ-Matrix">GWÖ-Matrix 2.0</span>
· 25 Felder (5 Werte × 5 Berührungsgruppen)
</summary>
<div style="margin-top:8px;">
{% from "v2/components/matrix_mini.html" import matrix_mini %}
{{ matrix_mini(antrag.matrix) }} {{ matrix_mini(antrag.matrix) }}
</div> </section>
</details> {% endif %}
{% endif %}
{% endblock %}
{# ─── 4. Verbesserungsvorschläge: default kollabiert ──────────────────── #} {# 6 ── Programm-Treue ─────────────────────────────────────────────── #}
{% block verbesserungen_section %} {% if antrag.fraktions_scores %}
{% if antrag.verbesserungen %} <section class="v3-section">
<details class="v3-collapsible"> <h3 class="v3-h3">Programm-Treue pro Fraktion</h3>
<summary> <div class="v3-fraktionen">
<h3 class="v2-h3">Verbesserungsvorschläge</h3> {% for fs in antrag.fraktions_scores %}
<span class="v3-collapsible-hint">{{ antrag.verbesserungen | length }} Vorschlag{{ "" if antrag.verbesserungen | length == 1 else "e" }} · klicken zum Aufklappen</span> <div class="v3-fraktion">
<div class="v3-fraktion-head">
<span class="v3-fraktion-name">{{ fs.fraktion }}</span>
{% if fs.ist_antragsteller %}<span class="v3-pill v3-pill-antrag">Antragsteller:in</span>{% endif %}
{% if fs.ist_regierung %}<span class="v3-pill v3-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 %}
<details class="v3-prog">
<summary class="v3-prog-row">
<span class="v3-prog-label">{{ prog_label }}</span>
<span class="v3-prog-spacer"></span>
<span class="v3-prog-score
{% if p_score >= 7 %}good{% elif p_score >= 4 %}mid{% else %}low{% endif %}">{{ "%.0f"|format(p_score) }}/10</span>
</summary> </summary>
<div style="margin-top:12px;"> <div class="v3-prog-body">
{% for v in antrag.verbesserungen %} {% if p.begruendung %}<p class="v3-prog-text">{{ p.begruendung }}</p>{% endif %}
<div style="margin-bottom:16px;"> {% if p.zitate %}
{% if antrag.verbesserungen | length > 1 %} <div class="v3-prog-zitate">
<div style="font-family:var(--font-mono);font-size:11px;color:var(--ecg-dark);opacity:0.65;margin-bottom:4px;"> {% for z in p.zitate %}
Vorschlag {{ loop.index }} von {{ antrag.verbesserungen | length }} {{ quote_card(z.text, z.source, True, False, z.pdf_href) }}
{% endfor %}
</div> </div>
{% endif %} {% endif %}
{% from "v2/components/redline.html" import redline %} </div>
</details>
{% endfor %}
</div>
{% endfor %}
</div>
</section>
{% endif %}
{# 7 ── Verbesserungsvorschläge — volle Breite, nicht kollabiert ───── #}
{% if antrag.verbesserungen %}
<section class="v3-section v3-verbesserungen">
<h3 class="v3-h3">Verbesserungsvorschläge</h3>
{% for v in antrag.verbesserungen %}
<div class="v3-verbesserung">
{% if antrag.verbesserungen | length > 1 %}
<div class="v3-verbesserung-num">Vorschlag {{ loop.index }} von {{ antrag.verbesserungen | length }}</div>
{% endif %}
{% if v.segments %} {% if v.segments %}
{{ redline(original=v.original | default(""), segments=v.segments) }} {{ redline(original=v.original | default(""), segments=v.segments) }}
{% else %} {% else %}
{{ redline(original=v.original | default(""), vorschlag=v.vorschlag | default("")) }} {{ redline(original=v.original | default(""), vorschlag=v.vorschlag | default("")) }}
{% endif %} {% endif %}
{% if v.begruendung %} {% if v.begruendung %}
<p style="font-size:12px;color:var(--ecg-dark);opacity:0.75;margin:4px 0 0;font-family:var(--font-mono);"> <p class="v3-verbesserung-begr">{{ v.begruendung }}</p>
{{ v.begruendung }}
</p>
{% endif %} {% endif %}
</div> </div>
{% endfor %} {% endfor %}
</section>
{% elif antrag.redline and antrag.redline.segments %}
<section class="v3-section v3-verbesserungen">
<h3 class="v3-h3">Verbesserungsvorschlag</h3>
{{ redline(segments=antrag.redline.segments) }}
</section>
{% endif %}
{# 8 ── Abstimmungsergebnis (Plenum + namentlich) ──────────────────── #}
{% if antrag.plenum_votes %}
<section class="v3-section v3-vote-section">
<h3 class="v3-h3">Abstimmungsergebnis</h3>
{% set ergebnis_color = {
"angenommen":"#2da44e","abgelehnt":"#cf222e","überwiesen":"#0969da",
"zurückgezogen":"#8250df","bestätigt":"#2da44e","sammel":"#0969da",
} %}
{% for v in antrag.plenum_votes %}
<div class="v3-vote-card">
<div class="v3-vote-card-head">
<span class="v3-vote-ergebnis" style="color:{{ ergebnis_color.get(v.ergebnis, '#6e7781') }};">
{{ v.ergebnis | capitalize }}{% if v.einstimmig %} · einstimmig{% endif %}
</span>
{% if v.quelle_url %}
<a href="{{ v.quelle_url }}" target="_blank" rel="noopener" class="v3-vote-quelle"
title="Plenarprotokoll im neuen Tab öffnen">
{{ v.quelle_protokoll }} ↗
</a>
{% else %}
<span class="v3-vote-quelle">{{ v.quelle_protokoll }}</span>
{% endif %}
</div> </div>
</details> {% if v.fraktionen_ja or v.fraktionen_nein or v.fraktionen_enthaltung %}
{% endif %} {% set _n_ja = v.fraktionen_ja | length %}
{% endblock %} {% set _n_nein = v.fraktionen_nein | length %}
{% set _n_enth = v.fraktionen_enthaltung | length %}
{% set _n_total = _n_ja + _n_nein + _n_enth %}
{% if _n_total > 0 %}
<div class="v3-vote-bar"
title="Fraktions-Mehrheit: {{ _n_ja }} Ja · {{ _n_nein }} Nein · {{ _n_enth }} Enth.">
{% if _n_ja %}<div class="v3-vote-bar-ja" style="width:{{ (100 * _n_ja / _n_total) }}%;"></div>{% endif %}
{% if _n_enth %}<div class="v3-vote-bar-enth" style="width:{{ (100 * _n_enth / _n_total) }}%;"></div>{% endif %}
{% if _n_nein %}<div class="v3-vote-bar-nein" style="width:{{ (100 * _n_nein / _n_total) }}%;"></div>{% endif %}
</div>
<div class="v3-vote-bar-caption">
{{ _n_ja }}/{{ _n_total }} Fraktionen Ja · {{ _n_nein }} Nein · {{ _n_enth }} Enth.
</div>
{% endif %}
<div class="v3-vote-pills">
{% if v.fraktionen_ja %}
<div><span class="v3-vote-side ja">Ja:</span>
{% for f in v.fraktionen_ja %}
{% set _opp = opportunismus_score(f, antrag.fraktions_scores) %}
<span class="v3-vote-pill ja">{{ f }}{% if _opp is not none %}<span class="v3-marker opp" tabindex="0" role="button" title="Opportunismus-Marker — Ja trotz schwacher Wahlprogramm-Übereinstimmung (WP-Score {{ '%.0f' | format(_opp) }}/10).">!</span>{% endif %}</span>
{% endfor %}
</div>
{% endif %}
{% if v.fraktionen_nein %}
<div><span class="v3-vote-side nein">Nein:</span>
{% for f in v.fraktionen_nein %}
{% set _wp = heuchelei_score(f, antrag.fraktions_scores) %}
<span class="v3-vote-pill nein">{{ f }}{% if _wp is not none %}<span class="v3-marker heuchelei" tabindex="0" role="button" title="Heuchelei-Marker — Nein trotz hoher Wahlprogramm-Übereinstimmung (WP-Score {{ '%.0f' | format(_wp) }}/10)."></span>{% endif %}</span>
{% endfor %}
</div>
{% endif %}
{% if v.fraktionen_enthaltung %}
<div><span class="v3-vote-side enth">Enth.:</span>
{% for f in v.fraktionen_enthaltung %}<span class="v3-vote-pill enth">{{ f }}</span>{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
</div>
{% endfor %}
<div class="v3-vote-source">Quelle: Plenarprotokoll · automatisch extrahiert</div>
</section>
{% endif %}
{# ─── 5. Aktions-Links: nur PDF + Permalink ──────────────────────────── #} {% if antrag.abstimmungsverhalten %}
{% block aktions_section %} {% set aw = antrag.abstimmungsverhalten %}
<div class="v3-aktions"> <section class="v3-section">
<a href="/api/assessment/pdf?drucksache={{ antrag.drucksache | urlencode }}"> <h3 class="v3-h3">Namentliche Abstimmung</h3>
PDF-Bericht <div class="v3-namentlich">
</a> <div>{{ aw.protokoll | default("") }}{% if aw.datum %} · {{ aw.datum }}{% endif %}</div>
<a href="/antrag/{{ antrag.drucksache }}"> {% if aw.abstimmung %}
Permalink <div>{{ aw.abstimmung.ja | default(0) }} Ja · {{ aw.abstimmung.nein | default(0) }} Nein · {{ aw.abstimmung.enthaltung | default(0) }} Enthaltung</div>
</a> {% endif %}
</div> </div>
{% endblock %} </section>
{% endif %}
{# ─── 6. Kommentare: default kollabiert ──────────────────────────────── #} {# ════════════════ 9 ── REST-Block ════════════════════════════════════ #}
{% block comments_section %} <section class="v3-rest">
<details class="v3-collapsible v3-comments">
<summary> {# 9a Aktions-Links #}
<h3 class="v2-h3">Kommentare</h3> <div class="v3-rest-aktions">
<span class="v3-collapsible-hint">klicken zum Aufklappen · Anmeldung erforderlich</span> <a href="/api/assessment/pdf?drucksache={{ antrag.drucksache | urlencode }}">PDF-Bericht</a>
</summary> <a href="/api/assessment?drucksache={{ antrag.drucksache | urlencode }}">JSON-Export</a>
<div style="margin-top:16px;"> <a href="/antrag/{{ antrag.drucksache }}">Permalink</a>
<div id="v2-comments-list" style="margin-bottom:20px;">
<span style="font-family:var(--font-mono);font-size:12px;color:var(--ecg-dark);opacity:0.5;">Lade…</span>
</div> </div>
{# 9b Teilen #}
<div class="v3-rest-block">
<h3 class="v3-h3">Teilen</h3>
<div class="v3-share-buttons">
<button onclick="v2DetailShareCopy()" class="v3-action-btn">📋 Kopieren</button>
<button onclick="v2DetailShare('threads')" class="v3-action-btn">Threads</button>
<button onclick="v2DetailShareMastodon()" class="v3-action-btn">Mastodon</button>
<button onclick="v2DetailShare('linkedin')" class="v3-action-btn">LinkedIn</button>
<button onclick="v2DetailShareEmail()" class="v3-action-btn">📧 E-Mail</button>
<button onclick="v2DetailShareImage()" class="v3-action-btn">🖼 Bild</button>
</div>
</div>
{# 9c Neu analysieren #}
<div class="v3-rest-block">
<button id="v2-reanalyze-btn" onclick="v2DetailReAnalyze(this)" class="v3-action-btn v3-action-muted">
Neu analysieren
</button>
</div>
{# 9d Bewertungs-Historie #}
<div class="v3-rest-block">
<h3 class="v3-h3">Bewertungs-Historie</h3>
<div id="v2-history-list">
<span class="v3-loading">Lade…</span>
</div>
</div>
{# 9e Stärken / Schwächen #}
{% if antrag.staerkster_wert and antrag.staerkster_wert.text %}
<div class="v2-kasten outline-green">
<h4>Stärkster Wert{% if antrag.staerkster_wert.titel %} — {{ antrag.staerkster_wert.titel }}{% endif %}</h4>
<p>{{ antrag.staerkster_wert.text }}</p>
</div>
{% elif antrag.staerken %}
<div class="v2-kasten outline-green">
<h4>Stärken</h4>
<ul>
{% for s in antrag.staerken %}<li>{{ s }}</li>{% endfor %}
</ul>
</div>
{% endif %}
{% if antrag.schwaechster_wert and antrag.schwaechster_wert.text %}
<div class="v2-kasten outline-blue">
<h4>Schwächster Wert{% if antrag.schwaechster_wert.titel %} — {{ antrag.schwaechster_wert.titel }}{% endif %}</h4>
<p>{{ antrag.schwaechster_wert.text }}</p>
</div>
{% elif antrag.schwaechen %}
<div class="v2-kasten outline-blue">
<h4>Schwächen</h4>
<ul>
{% for s in antrag.schwaechen %}<li>{{ s }}</li>{% endfor %}
</ul>
</div>
{% endif %}
{# 9f Konsistenz-Hinweis #}
{% if antrag.plenum_votes %}
{% set _state = consistency_state(antrag.verdict_title, antrag.plenum_votes) %}
{% set _decisive = decisive_outcome(antrag.plenum_votes) %}
{% if _state %}
<div class="v3-konsistenz {{ _state }}">
<strong>{% if _state == 'conflict' %}Mehrheit kontra GWÖ-Empfehlung{% else %}Mehrheit deckt sich mit GWÖ-Empfehlung{% endif %}</strong>
— Empfohlen: <em>{{ antrag.verdict_title }}</em>; Beschluss: <em>{{ _decisive | capitalize }}</em>.
</div>
{% endif %}
{% endif %}
{# 9g Marker-Legende — nur wenn ⚠ oder ! im Vote-Block vorkam #}
{% set _legende = namespace(heuchelei=false, opp=false) %}
{% for vv in (antrag.plenum_votes or []) %}
{% for f in (vv.fraktionen_nein or []) %}
{% if heuchelei_score(f, antrag.fraktions_scores) is not none %}{% set _legende.heuchelei = true %}{% endif %}
{% endfor %}
{% for f in (vv.fraktionen_ja or []) %}
{% if opportunismus_score(f, antrag.fraktions_scores) is not none %}{% set _legende.opp = true %}{% endif %}
{% endfor %}
{% endfor %}
{% if _legende.heuchelei or _legende.opp %}
<div class="v3-marker-legend">
{% if _legende.heuchelei %}
<div><span class="v3-marker heuchelei"></span> <strong>Heuchelei:</strong> Nein trotz hoher Wahlprogramm-Übereinstimmung (WP&nbsp;&nbsp;7/10).</div>
{% endif %}
{% if _legende.opp %}
<div><span class="v3-marker opp">!</span> <strong>Opportunismus:</strong> Ja trotz schwacher Wahlprogramm-Übereinstimmung (WP&nbsp;&lt;&nbsp;3/10).</div>
{% endif %}
</div>
{% endif %}
{# 9h News-Box (per JS gefuellt) #}
<div id="ad-news-box" class="v3-news-box" style="display:none;">
<h3 class="v3-h3">Aktuelle News passend zu diesem Antrag</h3>
<p class="v3-news-meta">Embedding-Match aus den letzten 90 Tagen. Quelle: Tagesschau-API + Bundestag-RSS.</p>
<div id="ad-news-list">
<div class="v3-loading">Lade …</div>
</div>
</div>
{# 9i Kommentare #}
<div class="v3-rest-block v3-comments">
<h3 class="v3-h3">Kommentare</h3>
<div id="v2-comments-list">
<span class="v3-loading">Lade…</span>
</div>
<div id="v2-comment-form" style="display:none;"> <div id="v2-comment-form" style="display:none;">
<div style="font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:0.07em;color:var(--ecg-dark);opacity:0.6;margin-bottom:8px;">Kommentar hinzufügen</div> <div class="v3-comment-label">Kommentar hinzufügen</div>
<textarea id="v2-comment-input" <textarea id="v2-comment-input" rows="3" placeholder="Kommentar…" class="v3-comment-textarea"></textarea>
rows="3" <div class="v3-comment-controls">
placeholder="Kommentar…" <select id="v2-comment-visibility" class="v3-comment-vis">
style="width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-sans);font-size:13px;background:var(--surface);color:var(--ecg-dark);resize:vertical;margin-bottom:8px;outline:none;"></textarea>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
<select id="v2-comment-visibility"
style="padding:5px 8px;border:1px solid var(--hairline);border-radius:4px;font-family:var(--font-mono);font-size:11px;background:var(--surface);color:var(--ecg-dark);">
<option value="all">Öffentlich</option> <option value="all">Öffentlich</option>
<option value="authenticated">Nur Angemeldete</option> <option value="authenticated">Nur Angemeldete</option>
<option value="private">Nur ich</option> <option value="private">Nur ich</option>
</select> </select>
<button onclick="v2DetailAddComment('{{ antrag.drucksache | e }}')" <button onclick="v2DetailAddComment('{{ antrag.drucksache | e }}')" class="v3-action-btn primary">Absenden</button>
style="padding:5px 14px;border:none;border-radius:4px;background:var(--ecg-blue);color:#fff;cursor:pointer;font-family:var(--font-mono);font-size:11px;font-weight:700;"> </div>
Absenden </div>
</button> <div id="v2-comment-login-hint" style="display:none;">
<button onclick="v2AuthModalOpen()" class="v3-action-btn">Anmelden um zu kommentieren</button>
</div> </div>
</div> </div>
<div id="v2-comment-login-hint" style="display:none;"> </section>{# v3-rest #}
<button onclick="v2AuthModalOpen()"
style="padding:5px 12px;border:1px solid var(--hairline);border-radius:4px;background:none;cursor:pointer;font-family:var(--font-mono);font-size:11px;color:var(--ecg-blue);"> {# ════════════════ Modals ═════════════════════════════════════════════ #}
Anmelden um zu kommentieren
</button> {# Matrix-Feld-Info-Modal — gleiche IDs wie v2, JS aus super() greift #}
<div id="v2-matrix-field-modal"
role="dialog" aria-modal="true" aria-label="Matrix-Feld Erklärung"
class="v3-modal"
onclick="if(event.target===this)this.style.display='none'">
<div class="v3-modal-card">
<div class="v3-modal-head">
<strong id="v2-matrix-field-title" class="v3-modal-title"></strong>
<span id="v2-matrix-field-rating" class="v3-modal-rating"></span>
<button onclick="document.getElementById('v2-matrix-field-modal').style.display='none'"
class="v3-modal-close" aria-label="Schließen">×</button>
</div>
<div id="v2-matrix-field-antrag" class="v3-modal-section" style="display:none;">
<div class="v3-modal-sublabel">Bewertung in diesem Antrag</div>
<div id="v2-matrix-field-label" class="v3-modal-itemlabel"></div>
<div id="v2-matrix-field-aspect" class="v3-modal-itemtext"></div>
</div>
<div class="v3-modal-section v3-modal-section--bordered">
<div class="v3-modal-sublabel">Was misst dieses Feld?</div>
<p id="v2-matrix-field-text" class="v3-modal-itemtext"></p>
</div> </div>
</div> </div>
</details> </div>
</div>{# .v3-page #}
{% endif %}{# antrag #}
{% endblock %} {% endblock %}
{# ─── Topbar-Pill + Toggle + Glossar-Tooltips (JS) ───────────────────── #}
{% block body_scripts %} {% block body_scripts %}
{{ super() }} {{ super() }}
<script> <script>
/* Topbar: Beta-Pill + Toggle zurueck zu Profi-Modus */ /* v3-Topbar-Pill + Toggle zurueck zu Profi-Modus */
(function () { (function () {
var bar = document.querySelector('.v2-topbar'); var bar = document.querySelector('.v2-topbar');
if (!bar) return; if (!bar) return;
var pill = document.createElement('span'); var pill = document.createElement('span');
pill.className = 'v3-beta-badge'; pill.className = 'v3-beta-badge';
pill.textContent = 'Bürger:innen-Modus · Beta'; pill.textContent = 'Bürger:innen-Modus · Beta';
pill.title = 'Vereinfachte Ansicht für Erst-Leser:innen.'; pill.title = 'Vereinfachte Ansicht. v3 ist eine frühe Vorschau.';
var toggle = document.createElement('a'); var toggle = document.createElement('a');
toggle.className = 'v3-modus-toggle'; toggle.className = 'v3-modus-toggle';
toggle.href = '/antrag/' + encodeURIComponent({{ antrag.drucksache | tojson }}); toggle.href = '/antrag/' + encodeURIComponent({{ antrag.drucksache | tojson if antrag is defined and antrag else '""' | safe }});
toggle.textContent = '→ Profi-Modus'; toggle.textContent = '→ Profi-Modus';
toggle.title = 'Volle GWÖ-Detailansicht (v2) öffnen'; toggle.title = 'Volle GWÖ-Detailansicht (v2) öffnen';
bar.appendChild(pill); bar.appendChild(pill);
bar.appendChild(toggle); bar.appendChild(toggle);
})(); })();
/* Glossar-Tooltips: Klick/Focus öffnet Pop-up mit Alltags-Definition. */
window._v3Glossar = {
"drucksache": {
titel: "Drucksache",
text: "Eine Drucksache ist die offizielle Nummer eines Antrags im Parlament. Format: <Wahlperiode>/<laufende Nummer>. Beispiel: '18/18246' = 18. Wahlperiode, Nummer 18246. Über diese Nummer findest du den Antrag in den Parlamentsakten."
},
"fraktion": {
titel: "Fraktion",
text: "Eine Fraktion ist die Gruppe der Abgeordneten einer Partei im Parlament. Sie ist nicht dasselbe wie die Partei selbst — die Partei ist die Organisation außerhalb, die Fraktion sind die gewählten Abgeordneten innerhalb."
},
"gwoe-score": {
titel: "Gemeinwohl-Score (010)",
text: "Der GWÖ-Score bewertet, wie stark der Antrag dem Gemeinwohl dient — auf einer Skala von 0 bis 10. Höher = besser für die Allgemeinheit. Der Wert ist ein gewichteter Durchschnitt aus 25 Bewertungs-Feldern (5 Werte × 5 Berührungsgruppen). Mehr unter /methodik."
},
"gwoe-matrix": {
titel: "GWÖ-Matrix 2.0",
text: "Die Gemeinwohl-Matrix ist ein Bewertungsraster mit 25 Feldern: 5 Werte (Menschenwürde, Solidarität, Nachhaltigkeit, Gerechtigkeit, Transparenz) × 5 Berührungsgruppen (Lieferant:innen, Finanzen, Verwaltung, Bürger:innen, Gesellschaft & Natur). Jedes Feld bekommt eine Bewertung von 5 (stark widersprechend) bis +5 (stark fördernd)."
},
"heuchelei": {
titel: "Heuchelei-Marker (⚠)",
text: "Wird angezeigt, wenn eine Fraktion mit Nein gestimmt hat, obwohl der Antrag inhaltlich gut zu ihrem eigenen Wahlprogramm passt (Wahlprogramm-Score ≥ 7 von 10). Der Marker macht die Lücke zwischen Wahlversprechen und Abstimmungsverhalten sichtbar."
},
"opportunismus": {
titel: "Opportunismus-Marker (!)",
text: "Wird angezeigt, wenn eine Fraktion mit Ja gestimmt hat, obwohl der Antrag schlecht zum eigenen Wahlprogramm passt (Wahlprogramm-Score < 3 von 10). Mögliches Anzeichen für taktisches Stimmverhalten."
}
};
window.v3OpenGlossar = function (key) {
var data = window._v3Glossar[key];
if (!data) return;
var modal = document.getElementById('v3-glossar-modal');
if (!modal) return;
document.getElementById('v3-glossar-title').textContent = data.titel;
document.getElementById('v3-glossar-body').textContent = data.text;
modal.style.display = 'flex';
};
document.addEventListener('click', function (e) {
var t = e.target.closest('.v3-glossar');
if (t) {
e.preventDefault();
window.v3OpenGlossar(t.dataset.glossar);
}
});
document.addEventListener('keydown', function (e) {
if ((e.key === 'Enter' || e.key === ' ') && document.activeElement && document.activeElement.classList.contains('v3-glossar')) {
e.preventDefault();
window.v3OpenGlossar(document.activeElement.dataset.glossar);
}
if (e.key === 'Escape') {
var m = document.getElementById('v3-glossar-modal');
if (m && m.style.display === 'flex') { m.style.display = 'none'; }
}
});
</script> </script>
{# Glossar-Modal #}
<div id="v3-glossar-modal" class="v3-glossar-modal" role="dialog" aria-modal="true"
aria-labelledby="v3-glossar-title"
onclick="if(event.target===this)this.style.display='none'">
<div class="v3-glossar-card">
<div class="v3-glossar-head">
<strong id="v3-glossar-title"></strong>
<button class="v3-glossar-close"
onclick="document.getElementById('v3-glossar-modal').style.display='none'"
aria-label="Schließen">×</button>
</div>
<p id="v3-glossar-body"></p>
</div>
</div>
{% endblock %} {% endblock %}