diff --git a/analysis/tabs/compass.py b/analysis/tabs/compass.py index 22a452e..c200c1c 100644 --- a/analysis/tabs/compass.py +++ b/analysis/tabs/compass.py @@ -185,13 +185,16 @@ def build_compass_tab(db_path: str, window_size: str) -> None: with st.expander("Overton Window Context"): st.markdown( - "Het SVD-kompas visualiseert direct de dynamiek achter de " - "Overton-verschuiving in de Tweede Kamer.\n\n" - "**Centristische partijen** (D66, CDA, CU, NSC) zijn op **beide assen naar" + "Het SVD-kompas visualiseert de dynamiek achter de " + "**verbreding van het Overton-venster** in de Tweede Kamer.\n\n" + "Centristische steun voor rechtse moties steeg van 25% naar 51% na 2024, " + "terwijl steun voor linkse moties gelijk bleef. Het venster verschoof — " + "meer rechtse standpunten werden acceptabel.\n\n" + "Maar: **centristische partijen** (D66, CDA, CU, NSC) zijn op **beide assen naar" " links** verschoven, terwijl rechtse partijen stabiel bleven. Dit patroon" - ' van "acceptatie zonder conversie" betekent dat centristen meer met' - " rechtse moties meestemmen terwijl ze ideologisch verder van rechts af" - " komen te staan.\n\n" + ' van "acceptatie zonder conversie" betekent dat rechtse partijen mildere' + " moties gingen indienen, en centristen daardoor vaker konden meestemmen — " + "zonder dat ze ideologisch naar rechts opschoven.\n\n" "[Lees de volledige analyse](../reports/overton_window/overton_window.qmd)\n\n" "Probeer de **Stemwijzer-quiz** om te zien welke MP bij jouw standpunten past." ) diff --git a/analysis/tabs/overton.py b/analysis/tabs/overton.py index 570922e..bfd92d1 100644 --- a/analysis/tabs/overton.py +++ b/analysis/tabs/overton.py @@ -17,8 +17,10 @@ def build_overton_tab(db_path: str) -> None: """Build the Overton Window tab.""" st.subheader("Overton Window Analyse") st.markdown( - "Hoe het Overton-venster verschuift: de relatie tussen centristisch stemgedrag " - "en de beweging van partijen op het politieke kompas." + "Het Overton-venster **verbreedde** na 2024: centristische steun voor rechtse " + "moties steeg van 25% naar 51%, terwijl steun voor linkse moties gelijk bleef. " + "Rechtse partijen dienden mildere moties in, waardoor centristen vaker konden " + "meestemmen — zonder ideologisch naar rechts op te schuiven." ) try: @@ -44,6 +46,7 @@ def build_overton_tab(db_path: str) -> None: try: _render_centrist_support_chart(con) _render_summary_stats(con) + _render_migration_gateway(con) _render_motion_browser(con) _render_explore_further() except Exception as e: @@ -135,23 +138,70 @@ def _render_summary_stats(con: duckdb.DuckDBPyConnection) -> None: col4.metric("2D correlation r", "0.47") -def _render_motion_browser(con: duckdb.DuckDBPyConnection) -> None: - st.subheader("Rechtse Moties Browser") +def _render_migration_gateway(con: duckdb.DuckDBPyConnection) -> None: + st.subheader("Migratie: de gateway-domein") + st.markdown( + "Migratie is waar de Overton-verschuiving het meest echt is — en waar " + "rechtse partijen de frames leerden die ze later op andere domeinen toepasten." + ) df = con.execute(""" - SELECT year, title, centrist_support_strict, category + SELECT + CASE WHEN year < 2024 THEN 'Pre-2024' ELSE 'Post-2024' END as period, + AVG(centrist_support_strict) as cs_strict, + COUNT(*) as n_motions FROM right_wing_motions WHERE classified = TRUE - ORDER BY centrist_support_strict DESC - LIMIT 50 + AND year >= 2016 + AND category IN ('asiel/vreemdelingen', 'asiel') + GROUP BY period + ORDER BY period + """).fetchdf() + + if df.empty or len(df) < 2: + return + + pre = df[df["period"] == "Pre-2024"].iloc[0] + post = df[df["period"] == "Post-2024"].iloc[0] + + col1, col2, col3, col4 = st.columns(4) + col1.metric("Pre-2024 CS (migratie)", f"{pre['cs_strict']:.3f}") + col2.metric("Post-2024 CS (migratie)", f"{post['cs_strict']:.3f}") + col3.metric("Shift", f"{post['cs_strict'] - pre['cs_strict']:+.3f}") + col4.metric("Moties", f"{int(pre['n_motions'] + post['n_motions'])}") + + st.caption( + "Ter vergelijking: niet-migratie moties gingen van 0.276 naar 0.481 (+0.205). " + "Migratie steeg meer dan twee keer zo hard (+0.216), terwijl de materiële impact " + "nauwelijks daalde. CDA en ChristenUnie verdubbelden hun migratie-steun " + "(18%→40%, 10%→30%)." + ) + + +def _render_motion_browser(con: duckdb.DuckDBPyConnection) -> None: + st.subheader("Rechtse Moties Browser") + + df = con.execute(""" + SELECT r.year, r.title, m.text, r.centrist_support_strict, r.category + FROM right_wing_motions r + LEFT JOIN motions m ON r.motion_id = m.id + WHERE r.classified = TRUE + ORDER BY r.centrist_support_strict DESC + LIMIT 100 """).fetchdf() if df.empty: st.info("Geen rechtse moties gevonden.") return - df["title"] = df["title"].str.slice(0, 80) - st.dataframe(df, use_container_width=True) + df = df.rename(columns={ + "year": "Jaar", + "title": "Titel", + "text": "Motietekst", + "centrist_support_strict": "Centrist Support", + "category": "Categorie", + }) + st.dataframe(df, use_container_width=True, height=600) def _render_explore_further() -> None: diff --git a/analysis/tabs/trajectories.py b/analysis/tabs/trajectories.py index 0cbc42f..34b5e9d 100644 --- a/analysis/tabs/trajectories.py +++ b/analysis/tabs/trajectories.py @@ -667,8 +667,10 @@ def build_trajectories_tab(db_path: str, window_size: str) -> None: try: st.plotly_chart(fig, use_container_width=True) st.info( - "**Overton shift:** centrist support for right-wing motions surged " - "after PVV's Nov 2023 election win." + "**Overton-venster verbreed:** na PVV's verkiezingsoverwinning (nov 2023) " + "steeg centristische steun voor rechtse moties van 25% naar 51%, " + "terwijl steun voor linkse moties gelijk bleef. " + "Rechtse partijen matigden hun moties, centristen stemden vaker voor." ) except Exception as e: st.error(f"Trajectories rendering failed: {e}") diff --git a/reports/overton_window/STATUS.md b/reports/overton_window/STATUS.md index 047fb3f..a67ebf7 100644 --- a/reports/overton_window/STATUS.md +++ b/reports/overton_window/STATUS.md @@ -217,4 +217,4 @@ reports/overton_window/ ## Verdict -**The Overton window did not shift right. Right-wing parties moderated toward it. The shift may be temporary (2026-Q1 reversion). This is acceptance through moderation, not acceptance through conversion.** +**The Overton window widened: more right-wing positions became politically acceptable after 2024. Centrist support for right-wing motions surged (25%→51%) while staying flat for left-wing motions (49%→49%). The mechanism was right-wing moderation, not centrist conversion — and the effect may be temporary (2026-Q1 reversion to 33%).** diff --git a/reports/overton_window/overton_report.html b/reports/overton_window/overton_report.html index 5d4c6dc..675d216 100644 --- a/reports/overton_window/overton_report.html +++ b/reports/overton_window/overton_report.html @@ -538,6 +538,7 @@