"""
Cookie-Banner Compliance Checks — L1/L2 Hierarchy.

6 L1 checks (fundamental requirements) with 30 L2 detail checks.
Each check_key maps to an existing check in banner_text_checker.py
or banner_advanced_checks.py.

Legal references: EDPB Guidelines 3/2022 (Deceptive Design Patterns),
EDPB Guidelines 05/2020 (Consent), CNIL Leitlinien, EuGH C-673/17
(Planet49), §25 TDDDG, Art. 5(3) ePrivacy-RL, Art. 7/12/13 DSGVO.
"""

BANNER_CHECKLIST = [
    # =====================================================================
    # L1-1: Banner vorhanden
    # =====================================================================
    {
        "id": "banner_present",
        "label": "Cookie-Banner vorhanden und funktional",
        "level": 1,
        "parent": None,
        "check_key": "banner_detected",
        "severity": "CRITICAL",
        "hint": (
            "Wer nicht-essentielle Cookies oder Tracking einsetzt, braucht eine "
            "Einwilligungsabfrage BEVOR der Zugriff auf das Endgeraet erfolgt "
            "(ss25 Abs. 1 TDDDG, Art. 5(3) ePrivacy-RL). Ohne Banner ist jedes "
            "gesetzte Tracking-Cookie rechtswidrig. Ausnahme: Rein technisch "
            "notwendige Cookies (Session-ID, Warenkorb, Load-Balancer) benoetigen "
            "kein Banner (ss25 Abs. 2 TDDDG). Haeufiger Fehler: Website setzt "
            "Google Analytics, hat aber kein Banner — das ist ein Verstoss ab dem "
            "ersten Seitenaufruf."
        ),
    },
    # ── L2 unter banner_present ──────────────────────────────────────
    {
        "id": "banner_language_match",
        "label": "Banner-Sprache entspricht Seitensprache",
        "level": 2,
        "parent": "banner_present",
        "check_key": "banner_language_mismatch",
        "severity": "MEDIUM",
        "hint": (
            "Art. 12(1) DSGVO: Informationen muessen in 'klarer und einfacher "
            "Sprache' bereitgestellt werden. Ein englisches Banner auf einer "
            "deutschen Seite (oder umgekehrt) erfuellt dieses Kriterium nicht, "
            "weil Nutzer die Tragweite ihrer Einwilligung nicht verstehen koennen "
            "(ErwGr. 39, 42 DSGVO). Haeufiger Fehler: CMP-Standardsprache ist "
            "Englisch und wurde nie auf die Seitensprache umgestellt. Pruefung: "
            "<html lang='de'> vs. Banner-Text-Sprache vergleichen."
        ),
    },
    {
        "id": "banner_provider_identifiable",
        "label": "CMP-Anbieter identifizierbar",
        "level": 2,
        "parent": "banner_present",
        "check_key": "banner_provider_named",
        "severity": "LOW",
        "hint": (
            "Obwohl gesetzlich nicht explizit gefordert, erleichtert die "
            "Identifizierbarkeit des CMP-Anbieters (Cookiebot, OneTrust, "
            "Usercentrics etc.) die Pruefung, ob der Banner korrekt "
            "konfiguriert ist. Viele CMP-Anbieter sind Auftragsverarbeiter "
            "nach Art. 28 DSGVO — in diesem Fall muss ein AV-Vertrag "
            "vorliegen. Haeufiger Fehler: CMP sendet Consent-Signale an "
            "eigene Server ohne AV-Vertrag."
        ),
    },
    {
        "id": "banner_visible_on_load",
        "label": "Banner erscheint beim ersten Seitenaufruf",
        "level": 2,
        "parent": "banner_present",
        "check_key": "banner_detected",
        "severity": "HIGH",
        "hint": (
            "ss25 Abs. 1 TDDDG: Die Einwilligung muss VOR dem Zugriff "
            "auf das Endgeraet eingeholt werden. Wenn der Banner erst nach "
            "Scrollen, nach einer Verzoegerung oder gar nicht erscheint, "
            "aber gleichzeitig Tracking-Scripts geladen werden, liegt ein "
            "Verstoss vor. Haeufiger Fehler: CMP ist installiert, aber "
            "der Banner wird wegen eines JavaScript-Fehlers oder einer "
            "fehlerhaften Geolocation-Einstellung (z.B. 'nur fuer EU-"
            "Nutzer') nicht angezeigt. Auch Caching-Probleme koennen "
            "dazu fuehren, dass der Banner bei wiederholtem Besuch "
            "nicht geladen wird, obwohl kein Consent vorliegt."
        ),
    },

    # =====================================================================
    # L1-2: Wahlmoeglichkeit (Akzeptieren + Ablehnen)
    # =====================================================================
    {
        "id": "banner_choices",
        "label": "Wahlmoeglichkeit (Akzeptieren + Ablehnen)",
        "level": 1,
        "parent": None,
        "check_key": "reject_button_visible",
        "severity": "HIGH",
        "hint": (
            "EDPB Guidelines 05/2020, Rn. 41-42: Eine gueltige Einwilligung "
            "erfordert eine echte Wahlmoeglichkeit. Der Nutzer muss Cookies "
            "ebenso einfach ablehnen koennen wie annehmen. Die CNIL hat am "
            "31.12.2021 gegen Google (150 Mio. EUR) und Facebook (60 Mio. EUR) "
            "Bussgelder verhaengt, u.a. weil Ablehnung nicht gleichwertig "
            "moeglich war (CNIL SAN-2021-023/024). Ein Banner ohne Ablehnen-"
            "Option ist keine gueltige Einwilligungsabfrage."
        ),
    },
    # ── L2 unter banner_choices ──────────────────────────────────────
    {
        "id": "reject_visible",
        "label": "Ablehnen-Button auf erster Ebene sichtbar",
        "level": 2,
        "parent": "banner_choices",
        "check_key": "reject_button_visible",
        "severity": "HIGH",
        "hint": (
            "ss25 TDDDG i.V.m. EDPB Guidelines 05/2020, Rn. 86: Der "
            "Ablehnen-Button muss auf der ersten Ebene des Banners sichtbar "
            "sein — nicht hinter 'Einstellungen' oder 'Mehr Informationen' "
            "versteckt. Die franzoesische CNIL hat dies in ihrem Google-"
            "Entscheid (SAN-2021-023) explizit geruegt: 'refuser devait etre "
            "aussi facile qu'accepter'. Haeufiger Fehler: Banner zeigt nur "
            "'Akzeptieren' + 'Einstellungen', Ablehnung erst im Untermenue."
        ),
    },
    {
        "id": "reject_same_clicks",
        "label": "Gleiche Klickanzahl fuer Akzeptieren und Ablehnen",
        "level": 2,
        "parent": "banner_choices",
        "check_key": "click_count_asymmetry",
        "severity": "HIGH",
        "hint": (
            "CNIL SAN-2021-023 (Google, 150 Mio. EUR): 'Le refus necessitait "
            "plusieurs clics alors que l'acceptation pouvait se faire en un "
            "seul clic.' Ablehnung und Zustimmung muessen mit identischer "
            "Klickanzahl erreichbar sein. 1 Klick Accept vs. 2+ Klicks Reject "
            "ist ein Verstoss. Auch die oesterreichische DSB hat dies in "
            "Bescheid D155.520 bestaetigt. Haeufiger Fehler: Accept = 1 Klick, "
            "Reject = Einstellungen oeffnen + Toggle deaktivieren + Speichern "
            "= 3 Klicks."
        ),
    },
    {
        "id": "reject_same_size",
        "label": "Gleiche Button-Groesse (kein Dark Pattern)",
        "level": 2,
        "parent": "banner_choices",
        "check_key": "dark_pattern_button_size",
        "severity": "MEDIUM",
        "hint": (
            "EDPB Guidelines 3/2022 (Deceptive Design Patterns), Rn. 62: "
            "Akzeptieren und Ablehnen muessen 'gleichwertig praesentiert' "
            "werden. Konkret: Gleiche Schriftgroesse, Buttongroesse und "
            "Farbprominenz. Ein ueberproportional grosser Accept-Button "
            "(area ratio > 2.5x) gegenueber einem kleinen Reject-Link ist "
            "ein klassisches Dark Pattern. Die CNIL hat Google und Meta "
            "u.a. wegen solcher Klick-Asymmetrie bestraft. Pruefung: "
            "Button-Flaeche und Schriftgroesse beider Optionen vergleichen."
        ),
    },
    {
        "id": "reject_same_prominence",
        "label": "Gleiche Farbgebung/Kontrast (kein Contrast-Trick)",
        "level": 2,
        "parent": "banner_choices",
        "check_key": "color_contrast_dark_pattern",
        "severity": "MEDIUM",
        "hint": (
            "EDPB Guidelines 3/2022, Rn. 63-65 (Interface Interference): "
            "Wenn der Accept-Button farblich hervorgehoben ist und der "
            "Reject-Button die gleiche Farbe wie der Hintergrund hat "
            "(transparent oder kaum sichtbar), liegt ein Deceptive Design "
            "Pattern vom Typ 'Hidden in Plain Sight' vor. Beispiel: "
            "Gruener Accept-Button vs. grauer Text-Link 'Ablehnen' der "
            "im Hintergrund verschwindet. Pruefung: background-color des "
            "Reject-Buttons darf nicht identisch mit Banner-Hintergrund sein."
        ),
    },
    {
        "id": "reject_no_scroll",
        "label": "Ablehnen ohne Scrollen erreichbar",
        "level": 2,
        "parent": "banner_choices",
        "check_key": "nudging_reject_hidden",
        "severity": "HIGH",
        "hint": (
            "EDPB Guidelines 3/2022, Rn. 48-50 (Hindering): Der Ablehnen-"
            "Button darf nicht ausserhalb des sichtbaren Banner-Bereichs "
            "platziert werden, sodass Nutzer scrollen muessen. Dies ist ein "
            "'Longer than necessary'-Pattern. Der BGH hat in seiner "
            "Planet49-Nachfolgeentscheidung (I ZR 7/16) klargestellt, dass "
            "die Ablehnung 'ohne unzumutbaren Aufwand' moeglich sein muss. "
            "Haeufiger Fehler: Banner mit langem Text, Reject-Button erst "
            "am Ende sichtbar."
        ),
    },
    {
        "id": "reject_no_nudging",
        "label": "Keine manipulative Sprache (Stirring/Nudging)",
        "level": 2,
        "parent": "banner_choices",
        "check_key": "stirring_emotional_language",
        "severity": "LOW",
        "hint": (
            "EDPB Guidelines 3/2022, Rn. 55-59 (Emotional Steering): "
            "Formulierungen wie 'eingeschraenkte Funktionen', 'bestmoegliches "
            "Erlebnis' oder 'Website funktioniert moeglicherweise nicht "
            "richtig' erzeugen emotionalen Druck und beeintraechtigen die "
            "Freiwilligkeit der Einwilligung (Art. 7(4) DSGVO). Auch "
            "sogenannte 'Confirmshaming'-Texte auf dem Ablehnen-Button "
            "(z.B. 'Nein, ich moechte kein gutes Erlebnis') sind unzulaessig. "
            "Korrekt: Neutral formulieren, z.B. 'Alle akzeptieren' / "
            "'Nur Notwendige'."
        ),
    },

    # =====================================================================
    # L1-3: Rechtliche Links (Impressum + DSE)
    # =====================================================================
    {
        "id": "banner_legal_links",
        "label": "Rechtliche Links (Impressum + DSE erreichbar)",
        "level": 1,
        "parent": None,
        "check_key": "impressum_link",
        "severity": "HIGH",
        "hint": (
            "ss5 TMG / ss5 DDG (ab 2025): Das Impressum muss 'leicht erkennbar, "
            "unmittelbar erreichbar und staendig verfuegbar' sein — auch wenn "
            "ein Cookie-Banner die Seite ueberlagert. LG Rostock (Az. 3 O "
            "22/19): Ein ueberlagernder Banner, der das Impressum verdeckt, "
            "ohne selbst einen Link zu enthalten, verstoesst gegen die "
            "Impressumspflicht. Gleichzeitig verlangt Art. 13 DSGVO, dass "
            "die Datenschutzerklaerung VOR der Einwilligung einsehbar ist "
            "(informierte Einwilligung, ErwGr. 42)."
        ),
    },
    # ── L2 unter banner_legal_links ──────────────────────────────────
    {
        "id": "impressum_accessible",
        "label": "Impressum trotz Banner-Overlay erreichbar",
        "level": 2,
        "parent": "banner_legal_links",
        "check_key": "impressum_link",
        "severity": "HIGH",
        "hint": (
            "ss5 TMG / ss5 DDG, LG Rostock Az. 3 O 22/19: Wenn ein modaler "
            "Cookie-Banner die Seite ueberlagert, MUSS ein Impressum-Link "
            "entweder im Banner selbst oder hinter dem Banner sichtbar sein. "
            "Ohne erreichbares Impressum droht eine Abmahnung nach UWG "
            "(ss3a UWG i.V.m. ss5 TMG). Haeufiger Fehler: Banner ueberlagert "
            "den gesamten Viewport, Impressum-Link im Footer ist nicht "
            "anklickbar. Loesung: Impressum-Link direkt in den Banner "
            "integrieren."
        ),
    },
    {
        "id": "dse_link_present",
        "label": "Link zur Datenschutzerklaerung im Banner",
        "level": 2,
        "parent": "banner_legal_links",
        "check_key": "dse_link",
        "severity": "MEDIUM",
        "hint": (
            "Art. 13 DSGVO i.V.m. ErwGr. 42: Eine 'informierte Einwilligung' "
            "setzt voraus, dass der Nutzer die Datenschutzinformationen VOR "
            "seiner Entscheidung einsehen kann. Ohne DSE-Link im Banner fehlt "
            "die Informationsgrundlage — die Einwilligung kann unwirksam sein. "
            "Die belgische DPA hat im TCF-Entscheid (21-02-2022) festgestellt, "
            "dass fehlender Zugang zur DSE vor Consent die Einwilligung "
            "nichtig macht. Haeufiger Fehler: DSE-Link nur im Footer, der "
            "vom Banner verdeckt wird."
        ),
    },
    {
        "id": "dse_link_own_domain",
        "label": "DSE-Link zeigt auf eigene Domain (nicht Drittanbieter)",
        "level": 2,
        "parent": "banner_legal_links",
        "check_key": "third_party_dse_link",
        "severity": "HIGH",
        "hint": (
            "Art. 13/14 DSGVO: Jeder Verantwortliche muss eine EIGENE "
            "Datenschutzerklaerung bereitstellen. Ein Verweis auf die DSE "
            "des CMP-Anbieters, des Hosters oder einer Muttergesellschaft "
            "genuegt nicht, wenn der Website-Betreiber selbst Verantwortlicher "
            "ist. Bei gemeinsamer Verantwortlichkeit (Art. 26 DSGVO) muss die "
            "Vereinbarung offengelegt werden. Haeufiger Fehler: Shopify-Shop "
            "verlinkt auf Shopify-DSE statt eigene."
        ),
    },
    {
        "id": "dse_readable_before_consent",
        "label": "DSE vor Einwilligung einsehbar (nicht hinter Consent-Gate)",
        "level": 2,
        "parent": "banner_legal_links",
        "check_key": "dse_link",
        "severity": "MEDIUM",
        "hint": (
            "ErwGr. 42 DSGVO: 'Damit die Einwilligung in Kenntnis der "
            "Sachlage erteilt wird, sollte die betroffene Person mindestens "
            "wissen, wer der Verantwortliche ist und fuer welche Zwecke ihre "
            "personenbezogenen Daten verarbeitet werden sollen.' Wenn die DSE-"
            "Seite selbst erst nach Cookie-Akzeptanz ladbar ist (z.B. weil "
            "sie hinter einem Cookie-Wall liegt), ist die Einwilligung nicht "
            "informiert und damit unwirksam. Pruefung: DSE-Link im Banner "
            "muss ohne vorherige Consent-Entscheidung oeffenbar sein."
        ),
    },

    # =====================================================================
    # L1-4: Gueltige Einwilligung (keine Planet49-Verstoesse)
    # =====================================================================
    {
        "id": "banner_consent_valid",
        "label": "Gueltige Einwilligung (DSGVO-konform)",
        "level": 1,
        "parent": None,
        "check_key": "pre_ticked_checkboxes",
        "severity": "HIGH",
        "hint": (
            "EuGH C-673/17 (Planet49, 01.10.2019): Eine gueltige Einwilligung "
            "erfordert eine 'aktive Handlung' — vorausgefuellte Checkboxen, "
            "Weitersurfen oder Inaktivitaet genuegen nicht. Art. 4(11) DSGVO "
            "definiert Einwilligung als 'freiwillig fuer den bestimmten Fall, "
            "in informierter Weise und unmisstdeutig abgegebene "
            "Willensbekundung'. Jeder dieser vier Bestandteile muss erfuellt "
            "sein — fehlt einer, ist die gesamte Einwilligung unwirksam."
        ),
    },
    # ── L2 unter banner_consent_valid ────────────────────────────────
    {
        "id": "no_pre_ticked",
        "label": "Keine vorausgewaehlten Checkboxen (Planet49)",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "pre_ticked_checkboxes",
        "severity": "HIGH",
        "hint": (
            "EuGH C-673/17 (Planet49), Rn. 52-58: 'Ein voreingestelltes "
            "Ankreuzkaestchen genuegt nicht.' Der BGH hat dies in der "
            "Folgeentscheidung (I ZR 7/16) bestaetigt. Konkret: Checkboxen "
            "fuer Marketing, Analytics oder Drittanbieter-Cookies duerfen "
            "NICHT vorangehakt sein. Nur technisch notwendige Kategorien "
            "(die keiner Einwilligung beduerfen) duerfen vorausgewaehlt und "
            "ausgegraut sein. Haeufiger Fehler: CMP setzt 'Funktionale "
            "Cookies' vorab auf aktiv — wenn diese Kategorie Drittanbieter "
            "enthaelt, ist das ein Planet49-Verstoss."
        ),
    },
    {
        "id": "no_wrong_dse_wording",
        "label": "Keine falsche Formulierung ('Zustimmung zur DSE')",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "wrong_dse_consent",
        "severity": "HIGH",
        "hint": (
            "Art. 13 DSGVO: Die Datenschutzerklaerung ist eine "
            "Informationspflicht des Verantwortlichen — der Nutzer kann sie "
            "nur 'zur Kenntnis nehmen', nicht 'zustimmen' oder 'akzeptieren'. "
            "Formulierungen wie 'Ich stimme der Datenschutzerklaerung zu' "
            "oder 'Ich akzeptiere die Privacy Policy' sind rechtlich falsch "
            "und koennen die Einwilligung anfechtbar machen. Korrekt: 'Ich "
            "habe die Datenschutzinformationen zur Kenntnis genommen.' "
            "Haeufiger Fehler: CMP-Standardtexte verwenden 'agree to privacy "
            "policy' und werden nicht angepasst."
        ),
    },
    {
        "id": "no_modal_dismiss",
        "label": "Klick ausserhalb des Banners ist keine Einwilligung",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "non_modal_dismiss",
        "severity": "HIGH",
        "hint": (
            "EuGH C-673/17 (Planet49), Rn. 49-50: 'Stillschweigen, bereits "
            "angekreuzte Kaestchen oder Untaetigkeit der betroffenen Person "
            "stellen keine Einwilligung dar.' EDPB Guidelines 05/2020, "
            "Rn. 77: Inaktivitaet, Weitersurfen oder Wegklicken eines "
            "Dialogs darf NICHT als Einwilligung gewertet werden. Wenn ein "
            "nicht-modaler Dialog bei Klick auf den Hintergrund verschwindet "
            "und dabei Consent gesetzt wird, ist diese Einwilligung nichtig. "
            "Loesung: Dialog muss modal sein (aria-modal='true'), nur "
            "explizite Button-Klicks zaehlen."
        ),
    },
    {
        "id": "no_coupling",
        "label": "Kein Koppelungsverbot-Verstoss (Art. 7(4))",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "registration_consent_coupling",
        "severity": "HIGH",
        "hint": (
            "Art. 7(4) DSGVO, ErwGr. 43: Wenn die Erfuellung eines Vertrags "
            "(z.B. Registrierung, Kauf) von einer Einwilligung abhaengt, die "
            "fuer die Vertragserfuellung nicht erforderlich ist, besteht ein "
            "Koppelungsverbot-Verstoss. Beispiel: Login-Button erteilt "
            "gleichzeitig Marketing-Einwilligung ohne separate Checkbox. "
            "Die oesterreichische DSB hat in Bescheid 2021-0.586.257 "
            "klargestellt, dass 'bundling' verschiedener Zwecke in einer "
            "einzigen Einwilligung die Freiwilligkeit ausschliesst. "
            "Pruefung: Login-/Registrierungsformulare auf versteckte "
            "Consent-Kopplung pruefen."
        ),
    },
    {
        "id": "no_emotional_language",
        "label": "Keine manipulative/emotionale Sprache (Stirring)",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "stirring_emotional_language",
        "severity": "LOW",
        "hint": (
            "EDPB Guidelines 3/2022, Rn. 55-59 (Emotional Steering): "
            "Formulierungen die Angst, Schuldgefuehle oder Verlustangst "
            "erzeugen, beeintraechtigen die Freiwilligkeit nach Art. 7(4) "
            "DSGVO. Typische Muster: 'Ohne Cookies koennen wir Ihnen kein "
            "optimales Erlebnis bieten', 'Einige Funktionen stehen nicht zur "
            "Verfuegung'. Die spanische AEPD hat in Entscheid PS/00543/2021 "
            "emotionale Sprache in Cookie-Bannern als Verstoss gewertet. "
            "Korrekt: Sachliche Beschreibung der Zwecke ohne Wertung."
        ),
    },
    {
        "id": "no_false_necessity",
        "label": "Keine falsche Notwendigkeits-Behauptung (Dark Pattern Language)",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "dark_pattern_language",
        "severity": "MEDIUM",
        "hint": (
            "EDPB Guidelines 05/2020, Rn. 70, Art. 7(4) DSGVO: "
            "Formulierungen wie 'Cookies muessen akzeptiert werden', "
            "'Cookies sind erforderlich' oder 'must be downloaded' fuer "
            "nicht-essentielle Cookies suggerieren eine technische "
            "Notwendigkeit, die nicht besteht. Dies untermieniert die "
            "Freiwilligkeit der Einwilligung. Abzugrenzen: Die Aussage "
            "'Technisch notwendige Cookies sind erforderlich fuer den "
            "Betrieb' ist zulaessig — aber nur wenn sie sich klar auf die "
            "essentielle Kategorie bezieht. Haeufiger Fehler: Pauschale "
            "Formulierung 'Alle Cookies sind fuer den Betrieb notwendig' "
            "obwohl Analytics- und Marketing-Cookies enthalten sind."
        ),
    },
    {
        "id": "consent_revocable",
        "label": "Einstellungen erneut zugaenglich (Widerruf, Art. 7(3))",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "re_access_settings",
        "severity": "MEDIUM",
        "hint": (
            "Art. 7(3) DSGVO: 'Der Widerruf der Einwilligung muss so einfach "
            "wie die Erteilung der Einwilligung sein.' Konkret muss ein "
            "persistenter Link zu den Cookie-Einstellungen vorhanden sein — "
            "typischerweise im Footer, als schwebendes Icon oder ueber ein "
            "Fingerprint-Symbol. Die DSK-Orientierungshilfe Telemedien "
            "(Dez. 2021) fordert dies explizit. Haeufiger Fehler: Nach "
            "Schliessung des Banners gibt es keinen Weg zurueck zu den "
            "Einstellungen — der Nutzer muesste Cookies manuell loeschen. "
            "Loesung: Permanenter Footer-Link oder CMP-Widget."
        ),
    },
    {
        "id": "consent_expiry_13m",
        "label": "Consent-Cookie max. 13 Monate gueltig (CNIL)",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "consent_cookie_expiry_13m",
        "severity": "MEDIUM",
        "hint": (
            "CNIL-Leitlinie (01.10.2020), Art. 5: 'La validite du "
            "consentement est de 13 mois maximum.' Die Consent-Entscheidung "
            "darf maximal 13 Monate (ca. 395 Tage) gespeichert werden — "
            "danach muss der Nutzer erneut gefragt werden. Auch die DSK-"
            "Orientierungshilfe Telemedien empfiehlt dies. Haeufiger Fehler: "
            "CMP-Default ist 12 Monate (365 Tage) — das ist im Rahmen. "
            "Aber manche setzen 2 Jahre oder 'Session' (kein Ablauf). "
            "Pruefung: Consent-Cookie Expiry-Datum vs. aktuelles Datum, "
            "Differenz darf 395 Tage nicht uebersteigen."
        ),
    },

    # =====================================================================
    # L1-5: Keine Vorab-Cookies/Tracking vor Consent
    # =====================================================================
    {
        "id": "banner_pre_consent",
        "label": "Kein Tracking vor Einwilligung (Phase A)",
        "level": 1,
        "parent": None,
        "check_key": "cookies_before_consent",
        "severity": "CRITICAL",
        "hint": (
            "ss25 Abs. 1 TDDDG (vormals ss15(3) TMG): Der Zugriff auf das "
            "Endgeraet des Nutzers (Cookie setzen, Script laden, "
            "Fingerprinting) ist NUR zulaessig, wenn der Nutzer zuvor "
            "eingewilligt hat. Tracking vor jeder Banner-Interaktion ist ein "
            "klarer Verstoss. EuGH C-673/17 (Planet49) und BGH (I ZR 7/16) "
            "bestaetigen: Die Einwilligung muss VOR dem Cookie-Setzen "
            "vorliegen. Haeufiger Fehler: Google Tag Manager laedt GA4 "
            "bereits beim Seitenaufruf — GTM selbst ist zulaessig, aber "
            "die darin konfigurierten Tags muessen consent-gesteuert sein."
        ),
    },
    # ── L2 unter banner_pre_consent ──────────────────────────────────
    {
        "id": "no_tracking_scripts_before",
        "label": "Keine Tracking-Scripts vor Consent geladen",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "tracking_before_consent",
        "severity": "CRITICAL",
        "hint": (
            "ss25 Abs. 1 TDDDG, Art. 5(3) ePrivacy-RL: Auch das blosse "
            "Laden eines Tracking-Scripts (ohne Cookie) ist ein 'Zugriff "
            "auf das Endgeraet', weil dabei Informationen (IP, User-Agent, "
            "Viewport) uebermittelt werden. Dies gilt fuer GA4, Meta Pixel, "
            "TikTok Pixel, LinkedIn Insight Tag etc. Pruefung: Netzwerk-"
            "Requests in Phase A (vor Consent) auf Tracking-Domains pruefen. "
            "Haeufiger Fehler: CMP-Integration ueber Google Tag Manager, "
            "aber Consent Mode nicht korrekt konfiguriert — Tags feuern "
            "trotzdem."
        ),
    },
    {
        "id": "no_tracking_cookies_before",
        "label": "Keine Tracking-Cookies vor Consent gesetzt",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "cookies_before_consent",
        "severity": "CRITICAL",
        "hint": (
            "EuGH C-673/17 (Planet49), Rn. 61: Die Einwilligung muss 'vor "
            "dem Speichern von Informationen' eingeholt werden. Cookies wie "
            "_ga, _gid, _fbp, _fbc, IDE, _gcl_*, fr, _pin_*, _tt_*, "
            "li_sugr, _hj* duerfen erst NACH expliziter Einwilligung "
            "gesetzt werden. Pruefung: document.cookie in Phase A auf "
            "bekannte Tracking-Cookie-Patterns pruefen. Haeufiger Fehler: "
            "CMP setzt Consent-Default auf 'granted' — dann werden Cookies "
            "sofort gesetzt und erst bei Ablehnung geloescht (zu spaet)."
        ),
    },
    {
        "id": "gcm_default_denied",
        "label": "Google Consent Mode Default = denied",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "google_consent_mode_defaults",
        "severity": "CRITICAL",
        "hint": (
            "Google Consent Mode v2: Die Standardwerte fuer analytics_storage, "
            "ad_storage, ad_user_data und ad_personalization muessen auf "
            "'denied' stehen, bis der Nutzer explizit einwilligt. Ein Default "
            "von 'granted' bedeutet, dass Google sofort Daten erhebt — das "
            "ist ein Verstoss gegen ss25 TDDDG. Pruefung: gtag('consent', "
            "'default', {...}) im Quelltext suchen und pruefen ob "
            "analytics_storage oder ad_storage auf 'granted' steht. "
            "Haeufiger Fehler: CMP-Plugin nicht korrekt konfiguriert, "
            "Default-Werte werden nicht ueberschrieben."
        ),
    },
    {
        "id": "no_facebook_pixel_before",
        "label": "Kein Meta/Facebook Pixel vor Consent",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "tracking_before_consent",
        "severity": "CRITICAL",
        "hint": (
            "Das Meta Pixel (ehem. Facebook Pixel) uebermittelt bei jedem "
            "Seitenaufruf personenbezogene Daten (IP, User-Agent, fbp/fbc-"
            "Cookies) an Meta Platforms Ireland Ltd. — einen Empfaenger in "
            "einem Land, das keinen Angemessenheitsbeschluss hat (Meta "
            "Schrems-II-Problematik). OeVGH (W211 2249247-1, 'noyb vs. "
            "Meta'): Uebermittlung an Meta ohne Einwilligung ist rechtswidrig. "
            "Das Pixel darf ERST nach expliziter Einwilligung geladen werden. "
            "Pruefung: Netzwerk-Requests an connect.facebook.net oder "
            "facebook.com/tr in Phase A."
        ),
    },
    {
        "id": "no_google_analytics_before",
        "label": "Kein Google Analytics vor Consent",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "tracking_before_consent",
        "severity": "CRITICAL",
        "hint": (
            "Mehrere EU-Datenschutzbehoerden haben den Einsatz von Google "
            "Analytics ohne Einwilligung fuer rechtswidrig erklaert: "
            "oesterreichische DSB (Bescheid 2021-0.586.257, 'noyb-Beschwerde'), "
            "franzoesische CNIL (Mise en demeure, Feb. 2022), italienische "
            "GPDP (Provvedimento 9782890, Juni 2022). GA4 darf erst NACH "
            "Consent geladen werden. Auch mit Server-Side-Tagging oder "
            "IP-Anonymisierung bleibt ein personenbezogener Datentransfer "
            "bestehen. Pruefung: Requests an googletagmanager.com oder "
            "google-analytics.com in Phase A."
        ),
    },

    # =====================================================================
    # L1-6: Ablehnung wird respektiert (Phase B)
    # =====================================================================
    {
        "id": "banner_post_reject",
        "label": "Ablehnung wird respektiert (Phase B)",
        "level": 1,
        "parent": None,
        "check_key": "tracking_after_reject",
        "severity": "CRITICAL",
        "hint": (
            "ss25 Abs. 1 TDDDG: Wird die Einwilligung verweigert, darf "
            "KEIN nicht-essentieller Zugriff auf das Endgeraet erfolgen. "
            "Tracking, das nach Ablehnung weiterlaeuft, ist ein schwerer "
            "Verstoss — der Nutzer hat seinen Willen ausdruecklich erklaert. "
            "Die franzoesische CNIL hat in mehreren Entscheiden (u.a. "
            "SAN-2022-009, Criteo, 40 Mio. EUR) geruegt, dass Tracking "
            "trotz Ablehnung fortgesetzt wurde. Pruefung: Phase A vs. "
            "Phase B vergleichen — neue Tracking-Scripts oder Cookies "
            "nach Reject sind ein Verstoss."
        ),
    },
    # ── L2 unter banner_post_reject ──────────────────────────────────
    {
        "id": "tracking_stops_after_reject",
        "label": "Tracking-Scripts werden nach Ablehnung entfernt",
        "level": 2,
        "parent": "banner_post_reject",
        "check_key": "tracking_after_reject",
        "severity": "CRITICAL",
        "hint": (
            "Nach Ablehnung duerfen keine neuen Tracking-Scripts geladen "
            "werden. Bestehende Scripts sollten deaktiviert oder entfernt "
            "werden. ss25 TDDDG kennt keinen 'Bestandsschutz' fuer bereits "
            "geladene Tracker — auch wenn ein Script in Phase A (vor Consent) "
            "fehlerhaft geladen wurde, muss es nach Reject gestoppt werden. "
            "Pruefung: Netzwerk-Requests in Phase B auf neue Tracking-Domains "
            "pruefen. Haeufiger Fehler: GA4 Enhanced Measurement sendet "
            "weiterhin scroll/outbound-Events trotz Ablehnung, weil der "
            "Consent Mode 'update'-Befehl nicht korrekt feuert."
        ),
    },
    {
        "id": "cookies_removed_after_reject",
        "label": "Tracking-Cookies nach Ablehnung entfernt",
        "level": 2,
        "parent": "banner_post_reject",
        "check_key": "tracking_after_reject",
        "severity": "HIGH",
        "hint": (
            "Wenn in Phase A faelschlicherweise Tracking-Cookies gesetzt "
            "wurden, muessen diese nach Ablehnung geloescht werden. Ein CMP "
            "sollte bei Reject ein 'Cookie-Cleanup' durchfuehren: _ga, _gid, "
            "_fbp etc. entfernen. Die CNIL-Leitlinie (Okt. 2020), Rn. 23 "
            "verlangt, dass 'der Verantwortliche sicherstellt, dass die "
            "Ablehnung effektiv umgesetzt wird'. Pruefung: Cookie-Liste vor "
            "und nach Reject vergleichen — Tracking-Cookies sollten "
            "verschwunden sein."
        ),
    },
    {
        "id": "no_new_tracking_after_reject",
        "label": "Keine neuen Tracker nach Ablehnung",
        "level": 2,
        "parent": "banner_post_reject",
        "check_key": "tracking_after_reject",
        "severity": "CRITICAL",
        "hint": (
            "Der gravierendste Verstoss: NACH expliziter Ablehnung werden "
            "NEUE Tracking-Services geladen, die vorher nicht aktiv waren. "
            "Dies kann passieren, wenn das CMP den Reject-Status nicht "
            "korrekt an den Tag Manager weitergibt oder wenn hardcoded "
            "Scripts im HTML stehen, die nicht consent-gesteuert sind. "
            "CNIL SAN-2022-009 (Criteo, 40 Mio. EUR): 'Des traceurs "
            "continuaient d'etre deposés malgre le refus de l'utilisateur.' "
            "Pruefung: Diff der Tracking-Services zwischen Phase A und "
            "Phase B — neue Eintraege sind ein Verstoss."
        ),
    },
    {
        "id": "site_functional_after_reject",
        "label": "Seite bleibt nutzbar nach Ablehnung (kein Cookie-Wall)",
        "level": 2,
        "parent": "banner_post_reject",
        "check_key": "cookie_wall",
        "severity": "HIGH",
        "hint": (
            "EDPB Guidelines 05/2020, Rn. 39: 'Access to services and "
            "functionalities must not be made conditional on the consent "
            "of a user to the storing of information.' Eine sogenannte "
            "'Cookie Wall', die den Zugang zur Website nach Ablehnung "
            "vollstaendig blockiert, macht die Einwilligung unfreiwillig. "
            "Ausnahme: 'Consent or Pay'-Modelle (EDPB Opinion 08/2024) "
            "sind unter engen Bedingungen zulaessig — dafuer muss die "
            "Bezahlalternative 'angemessen' sein und darf nicht "
            "ueberzogen sein. Haeufiger Fehler: Website zeigt nach "
            "Ablehnung eine leere Seite oder Redirect auf Fehlerseite."
        ),
    },

    # =====================================================================
    # Deep Verification L2 Checks (consent interceptor data)
    # =====================================================================
    {
        "id": "datalayer_events_before",
        "label": "Keine DataLayer-Tracking-Events vor Consent",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "datalayer_events_before",
        "severity": "HIGH",
        "hint": (
            "ss25 Abs. 1 TDDDG: Jeder DataLayer-Push, der ein Tracking-Event "
            "ausloest (z.B. page_view, purchase, conversion, gtm.click), "
            "stellt einen Zugriff auf das Endgeraet dar, weil dabei "
            "personenbezogene Daten (Client-ID, Session-Daten, URL, Referrer) "
            "an Drittanbieter-Server uebermittelt werden. Die CNIL hat in "
            "ihrer Google-Entscheidung (SAN-2021-023) explizit bestaetigt, "
            "dass bereits das Ausloesen eines GA4-Events vor Consent einen "
            "Verstoss darstellt. Pruefung: DataLayer auf Tracking-Events "
            "wie page_view, add_to_cart, conversion etc. vor jeder Banner-"
            "Interaktion pruefen. Ausnahme: gtm.js, gtm.dom, consent_update "
            "sind technisch notwendig und zulaessig."
        ),
    },
    {
        "id": "localstorage_tracking_before",
        "label": "Keine Tracking-Keys in localStorage vor Consent",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "localstorage_tracking_before",
        "severity": "MEDIUM",
        "hint": (
            "ss25 Abs. 1 TDDDG, Art. 5(3) ePrivacy-RL: localStorage und "
            "sessionStorage sind funktional aequivalent zu Cookies — der "
            "Zugriff auf den lokalen Speicher des Endgeraets erfordert "
            "dieselbe Einwilligung. Die EDPB Guidelines 05/2020, Rn. 10-11 "
            "stellen klar, dass 'any information stored on the terminal "
            "equipment' erfasst ist, unabhaengig von der technischen "
            "Implementierung. Bekannte Tracking-Keys: _ga, _gid, _fbp, "
            "_hjSession, _clck, amplitude_*, mixpanel_*. Pruefung: "
            "Storage.setItem()-Aufrufe vor Consent auf bekannte Tracking-"
            "Praefix-Muster ueberpruefen."
        ),
    },
    {
        "id": "gcm_runtime_denied",
        "label": "Google Consent Mode Runtime = denied vor Consent",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "gcm_runtime_denied",
        "severity": "HIGH",
        "hint": (
            "Google Consent Mode v2 (GCM): Die Laufzeit-Werte fuer "
            "analytics_storage, ad_storage, ad_user_data und "
            "ad_personalization muessen nach dem gtag('consent','default') "
            "Aufruf tatsaechlich auf 'denied' stehen. Diese Pruefung geht "
            "ueber den statischen Quelltext hinaus und verifiziert den "
            "effektiven Runtime-Zustand im Browser. Haeufiger Fehler: Der "
            "CMP sendet gtag('consent','default',{...}) korrekt, aber ein "
            "spaeterer gtag('consent','update',{...}) ueberschreibt die "
            "Werte zu 'granted' noch VOR der Nutzer-Interaktion. Auch "
            "Region-basierte Defaults (z.B. 'granted' fuer Nicht-EU) "
            "koennen bei fehlerhafter Geo-Erkennung zu einem Verstoss "
            "gegen ss25 TDDDG fuehren."
        ),
    },
    {
        "id": "datalayer_events_after_reject",
        "label": "Keine neuen DataLayer-Events nach Ablehnung",
        "level": 2,
        "parent": "banner_post_reject",
        "check_key": "datalayer_events_after_reject",
        "severity": "CRITICAL",
        "hint": (
            "ss25 Abs. 1 TDDDG, CNIL SAN-2022-009 (Criteo, 40 Mio. EUR): "
            "Wenn nach ausdruecklicher Ablehnung weiterhin DataLayer-"
            "Tracking-Events gefeuert werden (z.B. page_view, conversion), "
            "liegt ein schwerwiegender Verstoss vor. Der Nutzer hat seinen "
            "Willen unmissverstaendlich erklaert — jedes weitere Tracking-"
            "Event ist rechtswidrig. Haeufiger Fehler: Der CMP setzt den "
            "Consent-Status korrekt, aber GTM-Container-Tags pruefen den "
            "Status nicht oder verwenden veraltete Trigger-Konfigurationen. "
            "Pruefung: DataLayer nach dem Reject-Klick auf neue Tracking-"
            "Events ueberwachen."
        ),
    },
    {
        "id": "gcm_stays_denied",
        "label": "Consent Mode bleibt denied nach Ablehnung",
        "level": 2,
        "parent": "banner_post_reject",
        "check_key": "gcm_stays_denied",
        "severity": "CRITICAL",
        "hint": (
            "Google Consent Mode v2: Nach Ablehnung MUSS der CMP den "
            "Befehl gtag('consent','update',{analytics_storage:'denied', "
            "ad_storage:'denied', ...}) senden. Wenn der Consent Mode "
            "nach Reject auf 'granted' steht oder unveraendert bleibt, "
            "sendet GA4 weiterhin vollstaendige Hits statt consent-"
            "reduzierter Pings. Die CNIL Leitlinie (Okt. 2020) und "
            "EDPB Guidelines 05/2020, Rn. 112 fordern, dass technische "
            "Massnahmen die Ablehnung 'effektiv umsetzen'. Pruefung: "
            "Runtime-Werte von analytics_storage, ad_storage, "
            "ad_user_data, ad_personalization nach Reject verifizieren."
        ),
    },
    {
        "id": "storage_cleared_after_reject",
        "label": "Tracking-Storage nach Ablehnung geleert",
        "level": 2,
        "parent": "banner_post_reject",
        "check_key": "storage_cleared_after_reject",
        "severity": "MEDIUM",
        "hint": (
            "CNIL Leitlinie (Okt. 2020), Rn. 23: Der Verantwortliche muss "
            "sicherstellen, dass 'le refus est effectivement mis en oeuvre'. "
            "Wenn nach Ablehnung weiterhin Tracking-Schluesse in "
            "localStorage/sessionStorage geschrieben werden (z.B. _ga, "
            "_hjSession, _clck), ist die Ablehnung nicht wirksam umgesetzt. "
            "Auch bestehende Tracking-Eintraege sollten idealerweise "
            "bereinigt werden. Pruefung: Storage.setItem()-Aufrufe nach "
            "dem Reject-Klick auf bekannte Tracking-Keys ueberpruefen. "
            "Haeufiger Fehler: CMP loescht Cookies, vergisst aber "
            "localStorage-Eintraege von Hotjar, Clarity oder Amplitude."
        ),
    },
    {
        "id": "cookie_domain_check",
        "label": "Keine 3rd-Party-Tracking-Cookies vor Consent",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "cookie_domain_check",
        "severity": "HIGH",
        "hint": (
            "ss25 Abs. 1 TDDDG, EuGH C-673/17 (Planet49), Rn. 61: "
            "Tracking-Cookies wie _ga, _gid, _fbp, _fbc, IDE, _gcl_*, "
            "_tt_*, _pin_*, li_sugr, _hj* duerfen erst NACH expliziter "
            "Einwilligung geschrieben werden. Diese Pruefung ueberwacht "
            "document.cookie-Schreibvorgaenge in Echtzeit und erkennt "
            "Tracking-Cookie-Patterns bereits beim Setzen — nicht erst "
            "beim nachtraeglichen Cookie-Scan. Haeufiger Fehler: CMP "
            "konfiguriert Consent-Default auf 'granted', wodurch GA4 "
            "sofort _ga/_gid setzt und erst bei Ablehnung loescht — "
            "zu diesem Zeitpunkt wurde der Zugriff aber bereits "
            "rechtswidrig durchgefuehrt."
        ),
    },
    {
        "id": "cookie_expires_check",
        "label": "Tracking-Cookies nicht ueber 13 Monate",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "cookie_expires_check",
        "severity": "MEDIUM",
        "hint": (
            "CNIL Leitlinie (01.10.2020), Art. 5: Die Gueltigkeitsdauer "
            "von Tracking-Cookies darf 13 Monate (ca. 395 Tage) nicht "
            "uebersteigen. Auch die DSK-Orientierungshilfe Telemedien "
            "(Dez. 2021) empfiehlt diese Obergrenze. Pruefung: Das "
            "Expires/Max-Age-Feld der per document.cookie geschriebenen "
            "Tracking-Cookies auswerten. Haeufiger Fehler: GA4 setzt "
            "_ga mit Standardablauf von 2 Jahren (730 Tage) — das "
            "ueberschreitet die CNIL-Empfehlung deutlich. Loesung: "
            "Cookie-Lebensdauer in der GA4-Konfiguration auf maximal "
            "13 Monate begrenzen."
        ),
    },
    {
        "id": "tcf_consent_valid",
        "label": "TCF v2.2 Consent-Status korrekt",
        "level": 2,
        "parent": "banner_consent_valid",
        "check_key": "tcf_consent_valid",
        "severity": "MEDIUM",
        "hint": (
            "IAB TCF v2.2 Specification, ss4.1: Wenn ein CMP das "
            "Transparency and Consent Framework implementiert, muss die "
            "__tcfapi('getTCData') Antwort valide sein — insbesondere "
            "gdprApplies, purpose.consents und vendor.consents muessen "
            "den tatsaechlichen Consent-Status widerspiegeln. Die "
            "belgische DPA hat im TCF-Entscheid (02/2022) festgestellt, "
            "dass fehlerhafte TC-Strings die gesamte Consent-Kette "
            "ungueltig machen. Pruefung: __tcfapi verfuegbar, tcString "
            "nicht leer, gdprApplies korrekt gesetzt. Haeufiger Fehler: "
            "CMP meldet gdprApplies=false fuer EU-Nutzer wegen "
            "fehlerhafter GeoIP-Erkennung."
        ),
    },
    {
        "id": "response_blocked_before",
        "label": "Tracking-Requests werden vor Consent blockiert",
        "level": 2,
        "parent": "banner_pre_consent",
        "check_key": "response_blocked_before",
        "severity": "MEDIUM",
        "hint": (
            "ss25 Abs. 1 TDDDG, EDPB Guidelines 05/2020, Rn. 10: Auch "
            "navigator.sendBeacon()-Aufrufe an Tracking-Domains stellen "
            "einen Zugriff auf das Endgeraet dar, weil dabei Nutzer-"
            "Informationen (URL, Referrer, Timing-Daten) uebermittelt "
            "werden. Diese Methode wird haeufig fuer Analytics-Pings "
            "verwendet (GA4 Measurement Protocol, Meta CAPI). Pruefung: "
            "sendBeacon-Aufrufe vor Consent auf bekannte Tracking-"
            "Domains (google-analytics.com, facebook.com/tr, "
            "analytics.tiktok.com etc.) ueberpruefen. Haeufiger Fehler: "
            "Web-Vitals-Library sendet Metriken per sendBeacon an "
            "Google Analytics noch bevor der CMP geladen ist."
        ),
    },
]
