Commit Graph

24 Commits

Author SHA1 Message Date
Dotty Dotter
4c989ea443 fix(tour, csp): media-src für Tour-Audio + Tour global außer Administration
Zwei Bugs:

1) Audio kam nicht durch — die Content-Security-Policy hatte kein
   media-src und fiel auf default-src 'self' zurück. data:- (silent-WAV
   zum Element-Unlock) und blob:-URLs (ElevenLabs-MP3-Cache) wurden
   geblockt. Browser-Fehlermeldung im Console: „Loading media from
   ‚data:audio/wav;base64,…' violates the following Content Security
   Policy directive". Fix: ``media-src 'self' data: blob:;`` ergänzt.

2) Tour war nur auf Startseite + Antrag-Detail eingebunden. User-Wunsch:
   auf jeder Page außer Administration. Lösung: Tour-Engine-Include in
   v2/base.html, mit ``{% if v2_active_nav not in [admin_*] %}``-Guard.
   Pages ohne eigene ``window.GWOE_TOUR_STEPS`` bekommen einen Fallback
   mit drei Stationen (Logo+Konzept, Topbar, Sidebar).

   Topbar-Tour-Link sichtbar wenn ``window.gwoeTourStart`` existiert
   (Engine geladen) — nicht mehr abhängig von Page-eigenen Steps.

Aufräumen: redundante Tour-Includes aus durchsuchen.html und
antrag_detail.html entfernt — die Engine kommt jetzt nur einmal aus
base.html.
2026-05-09 08:43:35 +02:00
Dotty Dotter
e397ae5028 fix(tour, nav): Audio-Auto-Play entsperren + Daten-Nav für alle sichtbar
Drei zusammenhängende UI-Bugs:

1) Audio kam nicht — Browser-Auto-Play-Block. ``new Audio(blobUrl).play()``
   nach einem await zählt nicht mehr als User-Gesture; Safari/Chrome
   kassieren mit NotAllowedError. Fix: persistentes <audio>-Element wird
   einmal beim Tour-Start (im Click-Handler, synchron) mit einer
   1×1-silent-WAV entsperrt. Folgende src-Updates spielen ohne Block.

2) Tour-Bubble „Weiter"-Button sah 90er aus — der lokale CSS-Override
   ``.gwoe-tour-bubble .v3-action-btn.primary`` hat den modernen
   pill-shaped Style ausgehebelt. Override entfernt; nutzt jetzt das
   globale ``.v3-action-btn.primary`` (teal-solid, runde Ecken,
   weicher Drop-Shadow).

3) Tour erzählt anonymen User:innen über „Auswertungen" und
   „Stimmverhalten", die in der linken Nav für Anonyme nicht sichtbar
   waren. Aggregierte Daten sind öffentlich — Daten-Nav-Gruppe jetzt für
   alle sichtbar (Auswertungen, Stimmverhalten, Aktuelle Themen,
   Export-API, Atom-Feed). Persönliche Items (Merkliste, Abos, Neuer
   Antrag, Batch) bleiben eingeloggt. Cluster + Landtag-Suche bleiben
   eingeloggt/admin (Backend-Routen sind ohnehin require_auth).
2026-05-09 08:07:27 +02:00
Dotty Dotter
6ec05d2b86 feat(tour): ElevenLabs-Voice für die Tour (#185 Phase 2)
Audio-Backend:
- ``app/tour_audio.py`` ruft ElevenLabs-TTS mit voice_id=Domi
  (AZnzlk1XvdvUeBnXmlld) und model=eleven_multilingual_v2. ENV-konfiguriert
  via ``ELEVENLABS_API_KEY``, ``ELEVENLABS_VOICE_ID``, ``ELEVENLABS_MODEL_ID``.
- Voice-Settings: stability 0.55, similarity_boost 0.7 (warm, klar, natürlich).
- Caching: SHA-256(text|voice|model) → ``data/tour_audio/<hash>.mp3``.
  Folgeabrufe gehen aus dem Datei-Cache, kein API-Quota-Verbrauch.

Endpoint: ``GET /api/tour/voice?text=...`` rate-limited 30/min,
liefert audio/mpeg mit Cache-Control 30 Tage. Bei fehlendem
API-Key 503 — Frontend fällt dann auf ``speechSynthesis`` zurück
(Browser-eingebaute Stimme).

Frontend (tour.html):
- ``speak()`` versucht erst Server-Audio (ElevenLabs), bei 503/Fehler
  Fallback auf Web Speech API.
- Session-Cache via Blob-URL: Vor/Zurück-Navigation in der Tour zieht
  nicht jedes Mal eine neue Network-Roundtrip.
- ``stopSpeak()`` stoppt beide Audio-Pfade sauber.

Konfiguration für dev: ``ELEVENLABS_API_KEY`` und (optional)
``ELEVENLABS_VOICE_ID`` in ``/opt/gwoe-antragspruefer-dev/.env`` setzen,
dann Container restart.
2026-05-09 03:17:06 +02:00
Dotty Dotter
e31ee1ad07 feat(tour): Welcome-Banner + Tour auf Startseite, Logo-Klick zur Startseite
Drei zusammenhängende UI-Bausteine:

1) Tour-Engine ist jetzt page-agnostisch — sie liest die Stationen aus
   ``window.GWOE_TOUR_STEPS`` (pro Page hinterlegt), nicht mehr aus einem
   eingebauten Konstanten. Tour-Komponente wird per ``{% include %}``
   eingehängt; das Page-Template definiert vorher seine eigenen Steps.
   Antrag-Detail-Tour wurde entsprechend in das eigene Template gezogen.

2) Startseite (v2/screens/durchsuchen.html): „Du bist neu hier?"-Banner
   oben mit zwei Buttons — „🧭 Tour starten" und „Nein, danke". Banner
   bleibt sichtbar, bis explizit weggeklickt wird (localStorage-Flag),
   oder die Tour gestartet wird. Fünf Stationen für die Startseite:
   Marken-Block, Suche, Score-Filter + Sortierung, Antrags-Liste,
   linke Navigation.

3) Logo-Klick führt jetzt zur Startseite — sowohl in v2/base.html als
   auch in components/appshell.html. ``v2-brand`` und ``v2-brand-sub``
   sind in einen ``<a href="/">`` mit Hover-Highlight gewickelt
   (``.v2-brand-link``).

Phase 2 (ElevenLabs-Voice) ist der nächste Schritt — bisher läuft das
Audio über die Web Speech API.
2026-05-09 02:47:04 +02:00
Dotty Dotter
1c74cb8801 feat(antrag-detail): geführte Tour mit Sprachausgabe (#185 Phase 1)
Schaltfläche „🧭 Tour" in der userrow neben „Merken". Klick öffnet ein
Spotlight-Overlay mit vier Stationen, ermächtigend statt vereinfachend
formuliert:

1. Die Gemeinwohl-Note (was die Zahl 0–10 sagt, was die Empfehlung ist)
2. Die GWÖ-Matrix (5 Werte × 5 Berührungsgruppen, Farbcodierung)
3. Programm-Treue pro Fraktion (Score + Belege)
4. Stimmverhalten + Marker (Heuchelei ⚠ und Opportunismus !)

Audio: Web Speech API (Browser-eingebaute Stimme), de-DE, möglichst
weibliche Stimme. „Stimme an / aus"-Toggle in der Bubble. Bei
ESC oder Klick auf Overlay-Hintergrund: Tour-Ende, Audio stoppt.

Phase 2 (separate Iteration) wird das Audio-Backend gegen ElevenLabs
tauschen — Tour-Skript + UI bleiben gleich, nur ``speak()`` ruft dann
einen Server-Endpoint, der eine vorgenerierte und gecachte MP3 liefert.

Komponente in ``app/templates/v3/components/tour.html``, included am
Ende von antrag_detail.html. CSS inline in der Komponente (1 ``<style>``-
Block, keine ``style=""``-Attribute — Anti-Regression-Wache aus #184
respektiert).
2026-05-09 02:39:01 +02:00
Dotty Dotter
c7eab5a695 feat(pdf): Heuchelei-/Opportunismus-Marker im Vote-Block (#175)
Web-Detail zeigt diese Marker bereits — pro NEIN-Fraktion einen ⚠ wenn
der eigene Wahlprogramm-Score ≥ 7/10 ist (Heuchelei: stimmt gegen die
eigenen Versprechen), pro JA-Fraktion einen ! wenn der Wahlprogramm-
Score < 3/10 (Opportunismus: stimmt zu obwohl Antrag inhaltlich nicht
zum eigenen Programm passt). Im PDF fehlten sie bisher.

Daten-Pfad: report.py rechnet die Marker einmal vor (heuchelei_score /
opportunismus_score aus app/marker.py, gefüttert mit umgemappten
fraktions_scores aus assessment.wahlprogramm_scores) und reicht zwei
Maps fraktion → score ans Template. Template macht nur noch Lookup:
``opportunismus_by_fraktion.get(f)`` neben jeder JA-Fraktion,
``heuchelei_by_fraktion.get(f)`` neben jeder NEIN-Fraktion. Plus
kompakte Legende unter dem Vote-Block, falls überhaupt Marker
vorkommen.

Stimmverhalten und Programm-Treue-Begründungen sind im PDF schon da
(verifiziert bei der Code-Inspektion). Damit ist die "PDF auf Augenhöhe
mit Web-Detail"-Liste aus #175 bis auf News-Match abgehakt; News-Match
explizit out-of-scope nach User-Entscheidung.
2026-05-09 02:21:12 +02:00
Dotty Dotter
1a3aa9bbcb fix(antrag-detail, merkliste): Score in Merkliste + Metadaten-Whitespace (#177)
Zwei kleine UI-Bugs:

1) Score in Merkliste fehlte. Ursache: /api/assessment liefert ``gwoeScore``
   (camelCase), das Merkliste-Template las ``a.gwoe_score`` (snake_case).
   Fix: beide Schreibweisen akzeptieren.

2) Metadaten-Zeile im Antrag-Detail rendert mit Whitespace-Müll, weil die
   Jinja-If-Blöcke ohne Whitespace-Steuerung Newlines durchlassen, die
   der Browser zu Spaces collapst:
   - "13.04.2026 , qwen-plus" (Komma mit Leerzeichen davor) statt
     "13.04.2026, qwen-plus".
   - "NRW-WP18. Wahlperiode" statt "18. Wahlperiode": ``antrag.wahlperiode``
     ist der Filter-Key wie "NRW-WP18", nicht die reine Zahl.

   Fix: Whitespace-Steuerung ``{%- ... -%}`` an allen relevanten If-Tags
   in der Metadaten-Zeile + Byline. Plus neues Feld
   ``antrag.wahlperiode_zahl`` (nur die Zahl) im _row_to_detail-Mapping,
   das bevorzugt vor ``antrag.wahlperiode`` zur Anzeige genutzt wird.

Vorher: NRW · Drs. 18/18246 · Antrag · NRW-WP18. Wahlperiode · eingebracht 18.03.2026
        Eingebracht von SPD — Analyse 13.04.2026 , qwen-plus · 5 Zitate verifiziert
Nachher: NRW · Drs. 18/18246 · Antrag · 18. Wahlperiode · eingebracht 18.03.2026
         Eingebracht von SPD — Analyse 13.04.2026, qwen-plus · 5 Zitate verifiziert
2026-05-09 02:11:02 +02:00
Dotty Dotter
35fed45339 ui(antrag-detail): Programm-Treue als 2-Spalten-Grid, Trennlinien raus
User-Feedback: vorherige Pillen-Variante zu schmal/dicht. Stattdessen
gewünscht: Layout wie ursprünglich (head + 2 Programm-Reihen), aber
mit weniger Whitespace und ohne Trennlinien — und die Partei in eine
linke Spalte gezogen, damit head und Programm-Rows nebeneinander
sitzen statt stapeln.

Layout: ``.v3-fraktion`` als CSS-Grid mit zwei Spalten:
- links (140px–25%): Fraktion-Name + Pillen vertikal
- rechts (1fr): zwei Programm-Reihen (Wahlprogramm / Parteiprogramm)
  ohne border-top zwischen den Reihen, padding 3px statt 10/8,
  font kleiner (11px statt 12px).
2026-05-09 01:23:53 +02:00
Dotty Dotter
2f700adbb8 ui(antrag-detail): Programm-Treue-Box auf 1/3 der Höhe verkleinert
Pro Fraktion bisher ~137px (Head + Wahlprogramm-Reihe + Parteiprogramm-
Reihe in drei vertikalen Blöcken). Nutzer wollte deutlich kompakter.

Layout: Fraktion-Name + Antragsteller-/Regierungs-Pillen + zwei
Programm-Pillen (WP / PP) jetzt in EINER flex-row. Pro Programm-Pille
ein klickbares <details> mit Mini-Score-Chip; beim Aufklappen fließt
der Body (Begründung + Zitate) dank ``flex-basis: 100%`` unter die
Zeile in voller Breite.

Höhe collapsed: ~40-45px pro Fraktion (von vorher ~137px). Begründung
in der ausgeklappten Box bekommt zusätzlich einen ``Wahlprogramm:``-/
``Parteiprogramm:``-Präfix, da der Programm-Typ aus der Pille nicht
mehr im Body explizit auftaucht.

Doppelte v3-prog-text-Regel im CSS entfernt (war vergessenes Cruft).
2026-05-09 01:18:27 +02:00
Dotty Dotter
d16cacc7fe feat: Bewertungs-Kontext mit PDF-Link + Snapshot-Hinweis
Erweiterung des Geltungs-Kontext-Blocks (Antrag-Detail):
- Programm-Titel als Link auf das PDF ({titel} klickbar →
  /static/referenzen/{pdf}, opens in new tab).
- Seitenzahl als ergaenzende Info: "116 S." neben Geltungsdatum.
- Snapshot-Zeile am Block-Ende: "Diese Bewertung wurde am
  {datum} mit {modell} gegen den oben genannten Programm-Stand
  erzeugt." — macht klar, dass die Anzeige eine Momentaufnahme der
  damaligen LLM-Bewertung ist und nicht "live" gegen heutige
  Programme misst.

CSS:
- v3-geltung-pdf: ECG-blauer Link mit dezenter underline-Linie.
- v3-geltung-snapshot: kursiv, getrennt durch hairline-border, gedaempft.

Tests: 88 grün (test_legislaturen + test_wahlprogramme + test_embeddings).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:48:30 +02:00
Dotty Dotter
c7861cfb58 feat: Antrag-Detail um Bewertungs-Kontext erweitert
Vor der Programm-Treue-Sektion eine kompakte Info-Box, die transparent
macht, was zur Antragszeit fuer das jeweilige Parlament galt:

- **Wahlperiode** (Nummer + Konstituierung-bis-Ende-Spanne) ueber
  legislatur_zum_zeitpunkt(bl, antrag_datum)
- **Regierung zur Antragszeit** (Name + Koalitionsparteien + Vereidigung,
  ggf. Endedatum bei Sukzessionen wie Dreyer III -> Schweitzer I) ueber
  regierung_zum_zeitpunkt(bl, antrag_datum)
- **Bewertet gegen die folgenden Wahlprogramme** (pro Antragsteller-
  Fraktion mit gueltig-seit-Datum) ueber wahlprogramm_zum_zeitpunkt
  pro Partei

Daten kommen aus den neuen Modulen app/legislaturen.py + app/programme.py.
Helper laufen historisch korrekt — z.B. ein Antrag aus 2020-02-15 in TH
wuerde "Kemmerich I (FDP)" zurueckliefern.

Aktuell zeigen alle Antraege die jeweils "aktuelle" Regierung & das
aktuelle Programm, weil keine historischen Wahlprogramme im Embeddings-
Index sind. Die Architektur ist aber fuer den Tag vorbereitet, wo
historische Programme indiziert werden.

Implementation:
- main.py: _render_antrag_detail laedt geltung_kontext und gibt es ans
  Template weiter. ISO-Datum aus row["datum"] (nicht aus dem display-
  formatierten antrag["datum"]).
- v3/screens/antrag_detail.html: neue Sektion v3-geltung vor Programm-
  Treue-Block.
- static/v3/v3.css: neue v3-geltung-Klassen mit dezentem Doku-Look.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:45:44 +02:00
Dotty Dotter
c8bce35a16 fix: WAHLPROGRAMME['BUND'] mit Grundsatzprogrammen befuellt + Permalink-Copy-Click
User: 'Aber für diesen speziellen Antrag müssten doch alle Programme
verfügbar sein. https://gwoe-dev.toppyr.de/antrag/21/1594'

Ursache: WAHLPROGRAMME (in app/wahlprogramme.py) hatte keinen 'BUND'-
Eintrag, daher hat check_missing_programmes() fuer jeden Bundestags-
Antrag ALLE 8 Fraktionen als fehlend markiert. Im Embedding-Index
(app/embeddings.py) sind die *-grundsatzprogramm.pdf Dateien aber
laengst registriert (typ=parteiprogramm, ohne bundesland-Bindung).
Die Lookup-Tabellen waren inkonsistent.

Fix: WAHLPROGRAMME['BUND']-Eintrag mit den 6 Grundsatzprogrammen
(CDU/SPD/GRUENE/FDP/AfD/LINKE) ergaenzt — entspricht der Realitaet
im embeddings.py-Index. CSU + BSW haben keine indizierten Programme
und werden weiterhin als fehlend gemeldet (was korrekt ist).

Bestehende BUND-Assessments mit fehlende_programme=[8 Parteien] in
der DB bleiben erst mal so (waehrend einer Re-Analyse korrekt). Issue
#186 (historische BTW-Wahlprogramme) bleibt offen — Grundsatzprogramme
sind nur ein Notbehelf gegen die 'alle fehlen'-Anzeige.

Plus: Permalink-Klick kopiert jetzt die absolute URL in die Zwischen-
ablage statt zur Page zu navigieren. window.v3CopyPermalink in
v2/screens/antrag_detail.html (wird via super() von v3 mitvererbt).
Link-Text 'Permalink kopieren', 1.6s 'Permalink kopiert ✓'-Flash
nach Copy. Fallback auf window.prompt() wenn Clipboard-API fehlt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:27:33 +02:00
Dotty Dotter
332453b940 fix: Programm-Treue-Block bei BUND-/Antraegen mit komplett fehlenden Programmen ausblenden
User: '?? Hinweis: Für folgende Parteien lag kein Wahl-/Parteiprogramm
vor — keine Treue-Bewertung möglich: CDU, CSU, AfD, SPD, GRÜNE, LINKE,
BSW, FDP.' Vorher zeigte die UI den Disclaimer + trotzdem die LLM-
halluzinierten Programm-Treue-Scores aller 8 BT-Parteien — schlechte UX.

WAHLPROGRAMME['BUND'] ist aktuell leer (keine Bundestags-Wahlprogramme
indiziert), daher wird check_missing_programmes alle BT-Fraktionen als
'fehlend' markieren. Bisher wurden trotzdem die LLM-Scores rausgespielt.

Fix in v3/screens/antrag_detail.html und v3/pdf/antrag_pdf.html:
- Wenn _all_missing (alle Fraktionen fehlen) → ganze Programm-Treue-
  Sektion ausblenden, nur klare Disclaimer-Box zeigen ('Programm-Treue
  nicht verfuegbar — fuer dieses Parlament sind aktuell keine Programme
  indiziert').
- Wenn nur einzelne Fraktionen fehlen → die einzelnen Karten via
  {% if fs.fraktion not in _missing_set %} skippen, Disclaimer fuer
  die wegfallenden Fraktionen zeigen ('werden hier nicht aufgefuehrt').

Damit keine LLM-halluzinierten Scores mehr gezeigt werden, wo es keine
Quelle gibt. Issue #186 (historische Programme indizieren) ist die
langfristige Loesung — diese Aenderung macht die UI bis dahin ehrlich.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:59:00 +02:00
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
Dotty Dotter
79e7937d51 feat: PDF-Generierung auf v3-Layout, Werkstatt-Link im Admin, Share nur bei Login
Drei Aufgaben in einem Schwung:

1. Werkstatt-Link im Admin
   admin_stand bekommt eine Sektion 'Design-Werkstaetten' mit Link auf
   /v2/scorecard-werkstatt — damit Admins den Live-Editor finden ohne
   die URL kennen zu muessen.

2. Share-Block nur fuer angemeldete User
   Der ganze Share-Block (Kopieren, Threads, Mastodon, LinkedIn,
   Instagram, E-Mail, Scorecard, Stock-Bild) bekommt id=v2-share-block
   und wird per initAuth() display:none/block geschaltet — analog zum
   Comment-Form. Default im Markup: display:none, damit Gaeste ihn
   nicht waehrend des Auth-Roundtrips kurz sehen. Funktioniert in v2
   und v3 (gleicher JS-Handler via super-Inheritance).

3. PDF-Layout = v3-Layout
   Neues Template v3/pdf/antrag_pdf.html (single column, A4) reused die
   Visuallogik aus der Online-Detailseite:
   - Score-Hero-Block mit Farb-Tint
   - Matrix 5×5 mit Achsen-Labels (Werte oben, Berührungsgruppen links)
   - Programm-Treue pro Fraktion mit Begruendung + Zitaten + Fallback-
     Hinweis bei fehlenden Zitaten
   - Verbesserungsvorschlaege mit Redline-Format
   - Abstimmungsergebnis (best-effort via get_plenum_votes) inkl.
     Konsistenz-Hinweis

   Online-spezifisches gestrichen: Merken-Button, Vote-treffend, Share,
   Kommentare, News-Box, Reanalyze, Historie, Modals.

   NEU im PDF: 'Schwerpunkte erklaert'-Sektion direkt unter der Matrix.
   Listet die Top-4 positiven und Top-4 negativen Matrix-Felder mit
   ihrem LLM-generierten label + aspect — Ersatz fuer den interaktiven
   Klick, der im PDF nicht funktioniert.

   report.generate_html_report_v3() neu, generate_pdf_report() ruft
   diese statt der alten Inline-HTML-Variante. Alte generate_html_report
   bleibt als Fallback erhalten.

   WeasyPrint rendert mit @page A4, Footer mit Drucksache + Branding +
   Seitenzahl 'Seite X von Y'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:48:19 +02:00
Dotty Dotter
2821b8566e feat: Scorecards in Share-Block sichtbar + Instagram-Button
User-Frage: 'Wo kann ich jetzt die Cards angucken? Vielleicht verbunden
mit einem Instagram Sharing Button?'

Endpoints existieren laengst (#179):
- /v2/scorecard?format=og     → 1200×630 LinkedIn/Twitter-Card
- /v2/scorecard?format=square → 1080×1080 Instagram
- /api/assessment/scorecard.png?format=square&scale=2 → PNG

In der Share-Row jetzt drei neue Eintraege:

1. '📷 Instagram' — oeffnet Square-PNG (1080×1080) im neuen Tab,
   legt Begleittext in die Zwischenablage. Instagram hat keinen
   Web-Share-Endpoint, daher: Bild speichern + Text einfuegen.
2. '📊 Scorecard ansehen' — oeffnet die OG-Format-Vorschau (1200×630)
   im neuen Tab, der User sieht wie die Card auf LinkedIn/Twitter
   aussehen wird.
3. '🖼 Stock-Bild' — alter Magnific-Stockphoto-Knopf, jetzt klar
   gelabelt damit er nicht mit der Scorecard verwechselt wird.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:56:37 +02:00
Dotty Dotter
535c2f15e4 fix: Citation-Binding partei-skopiert (Cross-Partei-Misattribution gestoppt)
Bug: AfD-Parteiprogramm-Block enthielt ein Zitat mit quelle "CDU
Grundsatzprogramm 2024, S. 33" (DRS 21/4939). Ursache: reconstruct_zitate
hatte alle Chunks aller Parteien in einen Pool gemischt. Wenn der LLM
unter AfD-Parteiprogramm einen Text emittierte, der zufaellig auch im
CDU-Programm vorkam, matched der Code den CDU-Chunk und ueberschrieb
quelle/url mit CDU-Werten.

Fix: Match strikt auf chunks_by_party[fraktion][kind]. Fallback auf
gleiche Partei/andere Kategorie (z.B. AfD hat nur Grundsatz-, kein
Wahlprogramm im Index). Wenn kein Match in der eigenen Partei → Zitat
verwerfen statt fremde quelle behalten. Lieber 0 Zitate als ein
Misattributions-Zitat.

Plus v3-UI:
- News-Box von ganz hinten nach oberhalb "Neu analysieren" verschoben
- News-Liste auf 1 Item gekuerzt + 9-Zeilen-Clamp via -webkit-line-clamp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:51:03 +02:00
Dotty Dotter
1ef5578e02 feat(v3): v3 wird Default unter /antrag/{drs}, v2 zieht nach /v2/antrag/{drs}
Routen:
- /antrag/{drs}     → v3 (Standard, Bürger:innen-Modus single column)
- /v2/antrag/{drs}  → v2 (alter Profi-Modus, weiterhin erreichbar)
- /v3/antrag/{drs}  → Alias auf v3 (für alte Bookmarks)

UI-Hinweise auf alternative Ansichten ausgeblendet:
- v3-Topbar-Pill "Bürger:innen-Modus · Beta" + "→ Profi-Modus"-Toggle raus
- v2-Topbar-Link "→ Bürger:innen-Modus · v3 Beta" raus

Im Admin-Bereich (/v2/admin/stand) neuer Block "Alternative Ansichten"
mit Beispiel-Drucksache, Live-Link auf v3 (Default) und v2 (Profi).
Nur Admins sehen die Hinweise auf v2.

Trennlinien-Cleanup im Rest-Block:
- Doppellinie unter Abstimmungsergebnis aufgelöst (.v3-rest hatte
  border-top, das v3-section.border-bottom doppelt war).
- Neue Trennlinien via Klasse v3-rest-divider-top vor:
  · Teilen-Block (zwischen Ähnliche und Teilen)
  · Aktions-Links (zwischen Teilen und administrativem Bereich)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:29:06 +02:00
Dotty Dotter
4f4d9f3478 feat(v3): Merken+Vote-Zeile direkt unter Metadaten, Aktions-Links unter Teilen
User-Wünsche:
- Merken + Bewertung-treffend nach oben — direkt unter den Metadaten,
  noch vor der Zusammenfassung.
- PDF-Bericht / Original / JSON / Permalink-Zeile aus dem Kopf des
  Rest-Blocks weiter nach unten — zwischen Teilen und Neu-Analysieren.

Reihenfolge im Rest-Block jetzt: Ähnliche Anträge → Teilen → Aktions-
Links (PDF/…) → Neu analysieren → Historie → News → Kommentare.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:24:10 +02:00
Dotty Dotter
9eaa376fbe feat(v3): Wortwahl entjargonisiert, Layout-Reorganisation, Score mit Farb-Tint
Wortwahl (User-feedback: "vermeide Konfidenz, neutralere Formulierung
als Heuchelei"):
- "Konfidenz" → "Bewertungs-Sicherheit" (Tooltip behält wissenschaftl. Begriff)
- "Heuchelei-Marker"/"Opportunismus-Marker" → einheitlich
  "Wahlprogramm-Konflikt" (Tooltip behält wissenschaftl. Klassifikation)
- "Mehrheit kontra GWÖ-Empfehlung" → "Mehrheit gegen GWÖ-Empfehlung"
- Marker-Legende-Block entfernt (Tooltip pro Marker reicht)

Layout-Reorganisation:
- Stärkster/Schwächster Wert nach oben — zwischen Bewertung und
  User-Aktionen (vorher in "Rest"). Grid 2-spaltig, responsive.
- Konsistenz-Hinweis ("Mehrheit ... GWÖ-Empfehlung") an die Spitze
  des Abstimmungsergebnis-Blocks (vorher in "Rest").
- Marker-Legende komplett raus.

Visuelles:
- Score x/10 mit Farb-Tint hinterlegt: --score-{high,mid,low}-bg/-fg
  aus tokens.css. Farbe folgt der Score-Schwelle (≥7 grün, 4-6 mittel,
  <4 rot).
- Globale `body.v2 p { max-width: 72ch }` mit höherer Spezifität für
  .v3-page-Children überschrieben — alle Body-Texte nehmen volle Breite.

Programm-Treue Fallback:
- Wenn keine Zitate für ein Programm gefunden: erklärender
  Hinweistext im Body-Bereich, statt leerer Block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:16:39 +02:00
Dotty Dotter
f471586f33 feat(v3): Themen, Kernpunkte, Schwerpunkt, Konfidenz, fehlende Programme, Wahlperiode + Ähnliche Anträge
DB-Felder die bisher in der UI fehlten, jetzt in v3 sichtbar:

- _row_to_detail() liefert themen, kernpunkte, schwerpunkt, link,
  konfidenz, fehlende_programme, wahlperiode an's Frontend.
- _wahlperiode_silent() leitet die WP aus datum+bundesland ab via
  wahlperioden.wahlperiode_for() — silent-fail bei Lookup-Fehler.

v3-Template:
- Wahlperiode in der Antrag-ID-Zeile ("18. Wahlperiode")
- Themen-Chips als Reihe unter byline
- Kernforderungen als Bullet-Liste in der Zusammenfassungs-Sektion
- Konfidenz-Pille (hoch/mittel/niedrig) neben der Empfehlung
- Schwerpunkt-Felder (Top-Matrix-Cells) als Chips über der Matrix
- Disclaimer "fehlende Programme" am Programm-Treue-Block
- Original-PDF-Link im Aktions-Block
- Ähnliche Anträge als eigener Block, geladen via JS aus
  /api/assessment/similar (Re-Use des bestehenden Endpoints aus #108)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:01:18 +02:00
Dotty Dotter
4de2df9cea 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>
2026-05-07 10:51:33 +02:00
Dotty Dotter
895187ac36 feat(v3): Bürger:innen-Modus visuell ausgebaut — Wort-Etikett, 5-Werte-Bars, Glossar, Collapsibles
v2 unangetastet — v3 überschreibt nur Sub-Blocks. Neue v2-Blocks
für Override-Punkte: antrag_id_section, score_hero_section,
matrix_section, verbesserungen_section, aktions_section,
comments_section.

v3-Anpassungen:
1. Drucksache-ID: "Antrag im Landtag NRW · Drucksache 18/18246" —
   "Drucksache" als Glossar-Hinweis klickbar.
2. Score-Hero: großes Wort-Etikett ("Stark gemeinwohlfördernd" /
   "Gemischt" / "Widerspricht dem Gemeinwohl") aus verdict_title oder
   abgeleitet. Score-Zahl als kleiner Untertitel mit Glossar-Link.
   Akzentfarbe links (grün/blau/rot je nach Score).
3. Matrix: 5 Werte als Diverging-Bars (-5..+5) sofort sichtbar;
   volle 5×5-Matrix in <details>. Aggregation = Spalten-Mittel über
   die 5 Berührungsgruppen.
4. Verbesserungsvorschläge: <details> default zu, Hint-Text mit Anzahl.
5. Aktions-Links: JSON-Export raus — nur PDF + Permalink.
6. Kommentare: <details> default zu (für Erst-Leser:innen unwichtig).

Glossar-System: 6 Begriffe (Drucksache, Fraktion, GWÖ-Score,
GWÖ-Matrix, Heuchelei-Marker, Opportunismus-Marker) mit Klick/Focus-
Tooltip via Modal. Trigger: Element mit class="v3-glossar"
data-glossar="<key>".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:15:50 +02:00
Dotty Dotter
c4750d3274 feat(v3): Bürger:innen-Modus-Sandbox unter /v3/antrag/{drs}
Skelett für Issues #184 + #185 — minimal, nicht-disruptiv:

- v3/base.html extendet v2/base.html (Topbar/Sidebar/Footer geteilt)
- v3/screens/antrag_detail.html extendet vorerst v2-Screen 1:1 und
  injiziert nur Beta-Pill + Toggle "→ Profi-Modus"
- v2/screens/antrag_detail.html bekommt Topbar-Link "→ Bürger:innen-
  Modus (v3 Beta)" → /v3/antrag/<drs>
- _render_antrag_detail() teilt DB-Reads/Context zwischen v2 + v3 —
  Datenbasis garantiert in Sync, Unterschied ist nur template_name
- _MATRIX_EXPLANATIONS auf Modul-Ebene ausgelagert (war bisher
  inline im v2-Route, jetzt von beiden Modi referenziert)
- v3.css als Add-On nach v2.css (lädt im v3/base head)

Was v3 noch NICHT tut: Score-Hero-Vereinfachung, Matrix→5-Werte,
Glossar-Tooltips, Default-Collapsing der Profi-Blöcke (Verbesserungen,
Kommentare). Diese Iterationen folgen pro PR — v2 bleibt unberührt.

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