=== PageUpgrade ===
Contributors: pageupgrade
Tags: landing page, ai, website builder, shortcode, lead capture
Requires at least: 5.8
Tested up to: 6.7
Requires PHP: 7.4
Stable tag: 1.4.0
License: GPL-2.0+

Verbinde KI-generierte Websites mit WordPress. Visual Editor, Lead-Erfassung, Webhooks.

== Description ==

PageUpgrade ist die Brücke zwischen KI und WordPress. Erstelle Landing Pages mit ChatGPT oder Claude, passe sie im Visual Editor an, und veröffentliche sie per Shortcode auf WordPress.

**Neu in v1.4: Lead-Erfassung**
* Webhook-Endpunkte für KI-generierte Formulare
* Leads lokal in WordPress speichern (DSGVO-konform)
* Double-Opt-In mit Bestätigungs-E-Mail
* CSV-Export mit "Neue Leads"-Tracking
* Weiterleitung an Zapier, n8n, Make, KlickTipp
* E-Mail-Benachrichtigung bei neuen Leads

**Kernfunktionen:**
* Pages und Parts per Shortcode einbetten
* Funktioniert mit Elementor, Gutenberg, Divi und jedem Theme
* Popup-System
* Automatischer Cache mit Webhook-Sync
* Template Library

== Installation ==

1. Plugin ZIP hochladen unter Plugins → Installieren → Plugin hochladen
2. Plugin aktivieren
3. Unter PageUpgrade → Einstellungen den API Key eingeben
4. Verbindung testen

== Changelog ==

= 1.39.0 =
* Klare Trennung in der Auswertung: "Kontakt-Klicks" (Anruf, WhatsApp, E-Mail wurden angetippt — ob das Gespraech zustande kam, laesst sich technisch nicht messen) und "Anfragen im System" (nachweislich eingegangene Leads). Beides mit eigener Quote, statt zu einer Zahl vermischt zu werden
* NEU: Verlaufsdiagramm der Aufrufe und Besucher je Tag, passend zum gewaehlten Zeitraum. Als reines SVG umgesetzt — keine externe Bibliothek, kein zusaetzlicher Ladeaufwand. Beim Zeigen auf einen Balken erscheinen die genauen Werte des Tages

= 1.38.0 =
* NEU: Eingegangene Leads sind Teil der Statistik. Angezeigt werden Anzahl im Zeitraum, davon bestaetigt und offen (Double-Opt-In ausstehend), sowie die fuenf juengsten Eintraege mit Verweis auf die Lead-Liste
* Leads fliessen in die Konversionen ein — und zwar ANSTELLE der gezaehlten Formular-Klicks, nicht zusaetzlich: Ein abgeschicktes Formular kann gezaehlt worden sein, ohne dass ein Lead entstand (Abbruch, Spam-Filter, offener Double-Opt-In), und umgekehrt. Die Lead-Liste ist die belastbare Quelle, der Klick nur ein Signal

= 1.37.0 =
* NEU: Zeitraum-Auswahl in den Statistiken — Heute, 7 Tage, 30 Tage, 90 Tage und 1 Jahr
* NEU: Schalter "Angemeldete Benutzer mitzaehlen". Aufrufe angemeldeter Benutzer wurden schon bisher nicht gezaehlt, damit eigene Kontrollbesuche die Auswertung nicht verfaelschen — das ist jetzt sichtbar und bei Bedarf umstellbar

= 1.36.0 =
* Aufgeraeumt: Die Besucher- und Konversionsauswertung hat jetzt einen eigenen Menuepunkt "Statistiken" statt unter "Sync" zu stehen. Die Sitemap-Einstellung ist zu den "Einstellungen" gewandert. Unter "Sync" bleibt, was zur Synchronisation gehoert

= 1.35.0 =
* NEU: Knopf "Zahlen zuruecksetzen" in der Auswertung. Loescht alle gezaehlten Aufrufe und Konversionen — sinnvoll nach der Testphase, damit eigene Probeaufrufe das Ergebnis nicht verfaelschen. Mit Sicherheitsabfrage; die Zaehlung laeuft danach weiter

= 1.34.0 =
* NEU: Vorformulierter Absatz fuer die Datenschutzerklaerung direkt im Sync-Bereich, aufklappbar und mit einem Klick kopierbar. Der Text passt sich an: Ist die Besucher-Unterscheidung ausgeschaltet, entfaellt der Teil zur IP-Verarbeitung, weil dann keine personenbezogenen Daten verarbeitet werden

= 1.33.0 =
* NEU: Besucher werden unterschieden — ein Geraet wird einmal pro Tag gezaehlt, statt jeden Aufruf einzeln. Die Auswertung zeigt jetzt Besucher, Aufrufe, Konversionen und die Quote bezogen auf Besucher
* Die Unterscheidung laeuft ueber eine kurze Kennung aus einem TAEGLICH WECHSELNDEN Zufallswert, der IP und dem Browser. Der Zufallswert wird jede Nacht erneuert und der Vortagsbestand geloescht — dadurch keine Wiedererkennung ueber Tage hinweg und kein Personenbezug. Die IP-Adresse wird zu keinem Zeitpunkt gespeichert
* Abschaltbar im Sync-Bereich; ohne die Funktion werden weiterhin nur Aufrufe gezaehlt

= 1.32.0 =
* NEU: Besucher- und Konversionszaehlung (Sync-Bereich, opt-in). Gezaehlt werden Seitenaufrufe und Konversionen: Anruf, WhatsApp, E-Mail und Formularabsendungen werden automatisch erkannt, eigene Ziele lassen sich mit data-pgu-ziel="name" auszeichnen
* Datensparsam gebaut: keine Cookies, keine IP-Adressen, keine Besucherkennungen — gespeichert werden ausschliesslich Tageszaehler je Seite und Ziel. Dadurch kein Personenbezug und keine Einwilligung erforderlich
* Die Zaehlung laeuft ueber ein kleines Signal aus dem Browser, damit sie auch bei aktivem Static Cache funktioniert (dort wird kein PHP ausgefuehrt)
* Auswertung im Sync-Bereich: Aufrufe, Konversionen und Quote der letzten 30 Tage, dazu die Ziele und die meistbesuchten Seiten. Eigene Aufrufe als angemeldeter Benutzer werden nicht mitgezaehlt

= 1.31.0 =
* UMBAU: Die Sitemap wird jetzt als echte Datei (pgu-sitemap.xml im Wurzelverzeichnis) erzeugt statt bei jedem Aufruf von PHP ausgegeben. Damit liefert der Webserver sie direkt mit korrektem Dateityp aus — die Ursache dafuer, dass die Google Search Console sie zuvor nicht abrufen konnte (WordPress sendet den Typ text/html, bevor ein Plugin ihn aendern kann)
* Die Datei wird beim Aktivieren geschrieben und nach Inhaltsaenderungen automatisch erneuert; beim Deaktivieren wird sie entfernt
* Der Sync-Bereich zeigt Anzahl der URLs und Zeitpunkt der letzten Erzeugung; fehlende Schreibrechte werden als klare Meldung gemeldet statt still zu scheitern

= 1.30.1 =
* FIX: Die Sitemap wurde mit Content-Type text/html ausgeliefert — die Google Search Console lehnt sie dann mit "Konnte nicht abgerufen werden" ab. WordPress sendet den Header, bevor die Sitemap zum Zug kommt; jetzt werden offene Ausgabepuffer verworfen und der Header ueberschrieben (application/xml)
* Der Static Cache uebergeht .xml-Adressen komplett, damit er den Content-Type nicht erneut verfaelscht
* Die Sitemap-Anfrage wird jetzt auch unabhaengig von der Rewrite-Regel am Pfad erkannt

= 1.30.0 =
* FIX: Inhalte, die als vollstaendiges HTML-Dokument gespeichert wurden (<html><head>...<body>), werden beim Ausliefern auf den Body-Inhalt reduziert. Bisher landete der komplette Kopfbereich mitten in der WordPress-Seite: doppelte viewport- und charset-Angaben, ein Titel-Tag im Body und Hilfsstile des visuellen Editors im Frontend
* Styles aus dem Kopfbereich bleiben erhalten, damit das Design unveraendert bleibt; reine Editor-Stile (vvveb, pgu-editor-mode) werden entfernt
* Seiten, die wie vorgesehen nur Inhalt enthalten, bleiben unangetastet

= 1.29.1 =
* FIX: Das Aktivieren der Sitemap schlug mit HTTP 500 fehl — es wurde eine nicht existierende WordPress-Funktion aufgerufen (get_post_password). Ersetzt durch die Pruefung des Feldes post_password
* FIX: Hilfsaufrufe der Sitemap-Klasse registrierten die Hooks ein zweites Mal; Hilfsinstanzen laufen jetzt ohne Hook-Registrierung

= 1.29.0 =
* Sitemap ist jetzt VOLLSTAENDIG: Sie listet alle veroeffentlichten Seiten und Beitraege der Installation, nicht mehr nur die mit PageUpgrade-Inhalt. Damit kann sie die einzige Sitemap der Website sein — wichtig fuer Installationen ohne SEO-Plugin
* Ist sie aktiv, wird die WordPress-eigene Core-Sitemap abgeschaltet und /wp-sitemap.xml per 301 auf /pgu-sitemap.xml umgeleitet: genau eine Sitemap, keine widersprechenden Angaben
* Passwortgeschuetzte und auf noindex gesetzte Inhalte werden ausgelassen

= 1.28.0 =
* NEU: Eigene XML-Sitemap unter /pgu-sitemap.xml (Sync-Bereich, opt-in). Sie listet alle publizierten PageUpgrade-Seiten mit ihrer oeffentlichen URL
* Der eigentliche Mehrwert ist das lastmod-Datum: SEO-Plugins nehmen das WordPress-Aenderungsdatum, das sich beim Uebertragen von Inhalten aus PageUpgrade NICHT aendert — Google bekommt dadurch veraltete Daten und crawlt spaeter neu. Diese Sitemap nutzt das echte Aenderungsdatum aus PageUpgrade
* Beruecksichtigt nur publizierte Seiten, die auch in WordPress publiziert und eingebunden sind; noindex-Seiten werden ausgelassen. Traegt selbst X-Robots-Tag: noindex

= 1.27.0 =
* SICHERHEIT: Ausliefer-Sperre bei leerem Inhalt. Ist der Seiteninhalt leer — etwa weil der lokale Cache geleert wurde UND die Verbindung zum PageUpgrade-Server gestoert ist —, wird nicht mehr eine weisse Seite ausgeliefert. Stattdessen greift das Plugin auf den letzten gesicherten Stand aus der Sicherungstabelle zurueck; fehlt auch der, erscheint eine ehrliche Wartungsmeldung
* Damit ist die Kette lueckenlos: nicht ausliefern (1.27), nicht cachen (1.26), Cache nicht ohne Sicherung leeren (1.25)

= 1.26.0 =
* SICHERHEIT (Lehre vom 24.08.2026): Der Static Cache speichert keine Seiten mehr, deren Inhaltsbereich leer ist. Konnte das Plugin den Inhalt nicht vom Server laden, wurde bisher ein vollstaendiges HTML-Geruest mit leerem <main> als Cache verewigt und jedem Besucher ausgeliefert — die bisherige 200-Zeichen-Pruefung griff nicht, weil Kopfbereich und Skripte allein schon ueber 20 KB ergeben
* NEU: Static Cache im Sync-Bereich per Knopf an- und abschaltbar (Not-Aus). Beim Abschalten wird der vorhandene Cache mit geleert; WordPress rendert dann jede Seite frisch — langsamer, aber immer aktuell

= 1.25.0 =
* SICHERHEIT (Lehre vom 23.08.2026): "Gesamten Cache leeren" legt jetzt VOR dem Leeren automatisch eine Sicherung der Inhalts-Tabellen an (Pages, Parts, Gruppen) und wird VERWEIGERT, wenn der PageUpgrade-Server gerade nicht erreichbar ist — denn dann koennte der Cache nicht neu befuellt werden und die Website waere ohne Inhalte
* NEU: Knopf "Cache aus Sicherung wiederherstellen" im Sync-Bereich, inkl. Anzeige von Zeitpunkt und Umfang der letzten Sicherung. Die Wiederherstellung raeumt Render- und Static-Cache mit auf, damit sofort der gesicherte Stand ausgeliefert wird
* NEU: Notfall-Endpunkt pgu/v1/prime-page (Key-Hash-Auth wie purge-cache): Der Server kann Seiteninhalte direkt in den lokalen Cache uebertragen, falls die ausgehende Verbindung der Installation gestoert ist
* Einzelseiten-Leeren bleibt unveraendert moeglich (ungefaehrlich, da gezielt nachladbar)

= 1.24.4 =
* FIX: Die Cookie-Banner-Designs Karte, Zentriert, Sidebar und Minimal erzeugten bei jedem Seitenaufruf PHP-Warnungen (Undefined variable btnfg) und setzten die Knopf-Textfarbe teils leer — die Textfarben-Berechnung wurde beim Vier-Designs-Umbau nicht in die ausgelagerten Methoden uebernommen
* Alle vier Designs unter error_reporting E_ALL getestet: warnungsfrei, Knopf-Textfarbe ueberall korrekt gesetzt

= 1.24.3 =
* FIX: Nach dem Aufraeumen des Platzhalters konnte die Karte selbst verschwinden. Der Aufraeum-Schritt aus 1.24.2 entfernte alles ausser der Karte — steckte die Karte jedoch in einem Zwischen-Container, wurde dieser samt Karte mit entfernt
* Die Karte wird jetzt zuerst direkt in den Rahmen umgehaengt und erst danach aufgeraeumt; damit ist der Ablauf unabhaengig von der umgebenden Struktur
* Statt des kompletten Hintergrunds wird nur noch das Muster-Bild zurueckgesetzt
* Im DOM gegen beide Strukturvarianten getestet (Karte direkt im Rahmen und Karte in einem Zwischen-Container)

= 1.24.2 =
* FIX: Nach dem Klick auf "Karte laden" blieb die Hinweis-Box ueber der geladenen Karte stehen und machte sie unbedienbar. Ursache war ein Folgefehler der Muster-Ebene aus 1.24.0: Das Skript entfernte nur das ERSTE Element des Platzhalters — seit 1.24.0 ist das die Muster-Ebene, nicht mehr die Hinweis-Box
* Entfernt werden jetzt alle Platzhalter-Ebenen; zusaetzlich werden Innenabstand und Musterhintergrund zurueckgesetzt, die sonst als Rand um die Karte stehen blieben
* Im DOM getestet: Klick entfernt Overlay vollstaendig, Reload laedt direkt ohne Overlay, neue Besucher sehen den Platzhalter weiterhin

= 1.24.1 =
* FIX (Zwei-Klick-Platzhalter, mobil): Die Hinweis-Karte wurde auf schmalen Displays zusammengedrueckt und ragte ueber den Rand hinaus. Ursache war das Flex-Layout — ein Flex-Element darf schrumpfen. Der Platzhalter nutzt jetzt Grid-Zentrierung, bei der die Karte ihre Breite behaelt
* Zusaetzlich Umbruchschutz fuer lange Woerter und etwas mehr Innenabstand

= 1.24.0 =
* NEU: Karten-Platzhalter zeigen jetzt standardmaessig ein unscharfes Kartenmuster statt einer leeren Farbflaeche — der Platzhalter sieht nach Karte aus und wird sofort als solche erkannt
* Das Muster ist ein im Plugin erzeugtes SVG mit erfundenen Strassen, Wasser und Gruenflaechen: keine echten Ortsdaten, keine Lizenzfrage, kein zusaetzlicher Serverabruf (unter 2 KB, direkt eingebettet). Es folgt dem Banner-Theme (helle bzw. dunkle Kartenfarben)
* Die Unschaerfe liegt auf einer eigenen Ebene, damit Hinweistext und Knopf gestochen scharf bleiben; der Text wird auf einer halbtransparenten Karte hinterlegt
* Ein eigenes Vorschaubild per data-pgu-poster hat weiterhin Vorrang; Video- und Audio-Dienste bekommen kein Kartenmuster

= 1.23.3 =
* FIX (Zwei-Klick-Platzhalter, mobil): Der Lade-Knopf wurde auf schmalen Displays abgeschnitten und der Hinweistext zu einer schmalen Spalte gequetscht. Ursache war die feste Hoehe aus dem iframe-Attribut in Kombination mit overflow:hidden — auf dem Handy braucht der Text mehr Platz als das Original hoch ist
* Der Platzhalter waechst jetzt mit dem Inhalt (min-height statt height), der Textblock nutzt die volle Breite (box-sizing, width 100%), Schriftgroessen leicht angehoben
* Der Lade-Knopf ist jetzt komfortabel antippbar (volle Breite bis 260px, groessere Trefferflaeche) und der Hinweistext auf eine Zeile gekuerzt

= 1.23.2 =
* NEU (Doku): Aufklappbare Erklaerung des Spamschutzes in den Formular-Einstellungen — wie die Honigtopf-Falle und die Zeitpruefung funktionieren, warum die Pruefung serverseitig laeuft, warum bewusst kein Google reCAPTCHA eingesetzt wird und was mit erkanntem Spam passiert. In verstaendlicher Sprache, auch zur Weitergabe an Kunden geeignet

= 1.23.1 =
* FIX: Der Spamschutz brauchte einen Shortcode — PageUpgrade-Seiten liefern ihr HTML aber direkt aus und laufen nicht durch die Shortcode-Verarbeitung, der Shortcode blieb dort als Text stehen. Der Schutz wird jetzt AUTOMATISCH in jedes Formular eingesetzt, das an /pgu-form/ sendet. Uebrig gebliebener Shortcode-Text wird entfernt
* FIX: Die Zeitfalle war nicht Static-Cache-fest — der Zeitstempel alterte mit der Cache-Datei und haette echte Anfragen abgelehnt. Die Untergrenze (zu schnelles Absenden) prueft jetzt die tatsaechliche Verweildauer im Browser, die signierte Serverzeit deckt nur noch uralte Formularkopien ab. Besucher ohne JavaScript werden nicht abgelehnt; der Honeypot wirkt dort weiterhin
* Die Nachruestung ist idempotent und laesst fremde Formulare unberuehrt

= 1.23.0 =
* NEU: Spamschutz fuer Formulare ohne externen Dienst — Shortcode [pgu_form_schutz] erzeugt ein unsichtbares Honeypot-Feld und einen signierten Zeitstempel (Zeitfalle: unter 3 Sekunden oder aelter als 12 Stunden wird abgelehnt). Beides wird SERVERSEITIG geprueft, weil Bots das Formular direkt ansprechen und kein JavaScript ausfuehren
* Optional [pgu_form_schutz rechnen="1"]: zusaetzliche Rechenaufgabe mit signierter Loesung — erst einsetzen, wenn trotz Honeypot Spam durchkommt, da sie echte Besucher einen Denkschritt kostet
* Erkannter Spam erhaelt stillschweigend die Erfolgsseite, damit Bots nicht lernen, an welcher Huerde sie gescheitert sind. Bestandsformulare ohne Schutzfelder funktionieren unveraendert weiter

= 1.22.0 =
* NEU: Integrierte Google-Analytics-4-Einbindung (Einstellungen -> Cookie-Banner -> "Google Analytics 4"). Nur die Mess-ID eintragen — das Plugin erzeugt den Code selbst: blockiert bis zur Statistik-Einwilligung, mit Consent Mode v2 (alle Signale denied als Ausgangszustand, nach Einwilligung ausschliesslich analytics_storage granted), Google Signals aus, Werbesignale aus, IP-Anonymisierung
* Verhindert den klassischen Fehler, dass der gtag-Code von Google am Banner vorbei in den Theme-Header kopiert wird und ungebremst laedt
* Mess-IDs werden validiert (G-Format); ungueltige Eingaben erzeugen keinen Code. Im Admin steht die Checkliste fuer die Pflichten ausserhalb des Plugins: AV-Vertrag, Aufbewahrungsdauer, Datenschutzerklaerung
* Der Datenschutz-Check erkennt die Einbindung und wertet sie als korrekt geblockt

= 1.21.0 =
* FIX/NEU (Zwei-Klick-Loesung): Die Freischaltung wird jetzt gemerkt. Der Klick gilt je Dienst und bleibt im Browser gespeichert — beim naechsten Seitenaufruf und auf anderen Seiten laedt der Inhalt direkt, ohne erneuten Klick. Vorher sperrte jeder Reload die Karte wieder
* Das Merken laeuft im Browser des Besuchers (localStorage, je Dienst); neue Besucher sehen weiterhin den Platzhalter. Funktional im DOM getestet: Klick -> Reload laedt automatisch -> fremder Besucher bleibt gesperrt
* NEU (Admin): Anleitung direkt an der Option — wie das Vorschaubild per data-pgu-poster gesetzt wird (inkl. Lizenz-Hinweis und Verweis auf Medien -> Bild-Links) und Hinweis auf die Merkfunktion

= 1.20.2 =
* NEU: Optionales Vorschaubild fuer die Zwei-Klick-Platzhalter — data-pgu-poster="URL" am iframe setzt ein lokal gehostetes Bild als Hintergrund; Datenhinweis und Lade-Knopf liegen auf einer halbtransparenten Karte und bleiben auf jedem Motiv lesbar
* WICHTIG: Nur eigene, lokal gehostete Bilder verwenden. Ein live von Google Static Maps geladenes Poster wuerde beim Seitenaufruf wieder Google kontaktieren und den Zweck der Zwei-Klick-Loesung zerstoeren; Google-Karten-Screenshots dauerhaft zu hosten ist zudem lizenzrechtlich untersagt. Geeignet: OpenStreetMap-Ausschnitt (ODbL, mit Attribution), eigene stilisierte Karte oder ein Foto des Standorts
* Das Poster-Attribut wird nach Gebrauch aus dem entschaerften iframe entfernt; Werte werden gegen CSS-Ausbrueche neutralisiert

= 1.20.1 =
* Zwei-Klick-Loesung um 8 Dienste erweitert: Instagram, TikTok, Pinterest, Facebook-Plugins (Like-Box/Seiten-Widget), X (Twitter), Spotify, SoundCloud und Google Kalender — jeweils mit passendem Knopftext (Beitrag/Inhalt/Player/Kalender laden)
* Der Datenschutz-Check kennt die neuen Dienste ebenfalls (inkl. Anbieter- und Datenschutzerklaerungs-Stichpunkten) — was die Zwei-Klick-Loesung sperrt, kann der Check auch benennen
* Instagram-Profil-LINKS und OpenStreetMap-Einbettungen bleiben bewusst unberuehrt; Hinweis im Admin: Script-basierte Einbettungen (blockquote + Skript) gehoeren in die Marketing-Codes des Banners

= 1.20.0 =
* NEU: Zwei-Klick-Loesung fuer externe Inhalte (Einstellungen -> Cookie-Banner -> "Externe Inhalte"). Google-Maps-, YouTube- und Vimeo-iframes laden sonst sofort und uebertragen die IP, bevor der Besucher zustimmen konnte — iframes lassen sich nicht ueber den Cookie-Banner blockieren
* Statt des iframes erscheint ein Platzhalter in Originalgroesse (Theme- und Akzentfarben-bewusst) mit Datenhinweis; erst der Klick laedt den Inhalt und ist zugleich die informierte Einwilligung nach Art. 6 Abs. 1 lit. a DSGVO
* Unbekannte und eigene iframes bleiben unberuehrt; Lazy-Load-iframes (data-src + src) werden korrekt behandelt; die Umschreibung ist idempotent und Static-Cache-kompatibel
* Der Datenschutz-Check erkennt die Platzhalter und wertet solche Einbindungen als geblockt

= 1.19.3 =
* FIX (HTTPS): Die erzwungene Weiterleitung lief nur als PHP-Hook — der Static Cache liefert Seiten aber per .htaccess an PHP vorbei aus, fuer jede gecachte Seite war die Weiterleitung damit wirkungslos. Bei aktivem Schalter schreibt das Plugin jetzt einen 301-Regelblock in die .htaccess, VOR die Cache-Regeln
* Der Regelblock prueft X-Forwarded-Proto und vermeidet so Endlosschleifen hinter Reverse Proxies; beim Abschalten wird er rueckstandsfrei entfernt
* Die Speichern-Meldung sagt jetzt klar, ob die .htaccess-Weiterleitung aktiv ist oder die Datei nicht schreibbar war

= 1.19.2 =
* FIX: Die Banner-Vorschau in den Einstellungen war eine hartkodierte JS-Kopie der vier Designs und kannte weder Theme (hell/dunkel) noch Anrede (Du/Sie) noch die Button-Schrift-Automatik — sie zeigte dunkle Box und Du-Texte, egal was gewaehlt war
* Die Vorschau rendert jetzt das ECHTE Banner (get_banner_html) mit den gerade im Formular gewaehlten, noch ungespeicherten Werten — kuenftige Design-Aenderungen erscheinen automatisch, nichts laeuft mehr auseinander
* Theme-, Anrede- und Button-Schrift-Wechsel aktualisieren die geoeffnete Vorschau live
* Das Consent-Skript wird der Vorschau bewusst NICHT mitgegeben (es wuerde im Admin echte Cookies setzen und die Consent-Statistik verfaelschen); ein kleines Shim haelt die Knoepfe klickbar

= 1.19.1 =
* NEU (Lasche, mobil): Auf dem Handy erscheint die Lasche erst, wenn der Besucher das Seitenende erreicht — sanft eingeblendet, mit einstellbarem Abstand ueber einer festen Fussleiste (Standard 72px). Beim Hochscrollen verschwindet sie wieder. Desktop unveraendert: dauerhaft am unteren Rand angedockt
* Die Hoehe einer Sticky-Leiste ist Seiteninhalt und dem Plugin unbekannt — der Abstand ist deshalb einstellbar statt geraten
* Sichtbarkeit laeuft ueber Opacity, nicht ueber display — kein Konflikt mit der Consent-Logik (wShow/wHide). prefers-reduced-motion wird respektiert; kurze Seiten ohne Scrollweg zeigen die Lasche sofort

= 1.19.0 =
* NEU: Vierte Widerrufs-Variante "Lasche am unteren Rand" — flacher beschrifteter Reiter im Stil von Complianz/Borlabs. Beschriftet statt Icon-Raetsel, folgt dem Banner-Theme (hell/dunkel) und der Links/Rechts-Position
* Erscheint wie die anderen Varianten erst nach der Entscheidung des Besuchers und oeffnet die Auswahl mit den zuvor gesetzten Haken

= 1.18.0 =
* NEU: Banner-Theme hell/dunkel waehlbar — die helle Box hebt sich auf dunklen Seiten (dunkler Hero) als eigenstaendiger Dialog ab, statt mit dem Hintergrund zu verschwimmen. Gilt fuer alle Designs; das Sidebar-Design war bereits hell
* NEU: Button-Schriftfarbe auf dem Akzent-Button — automatisch nach Helligkeit der Akzentfarbe (auf Gelb/Orange dunkle Schrift, auf Blau helle; weisse Schrift auf Gelb erreicht nur ~1,6:1 Kontrast), manuell uebersteuerbar
* Der Theme-Tausch laeuft ausschliesslich ueber das Banner-Markup — hinterlegte Statistik-/Marketing-Codes und die Consent-Logik werden nie angefasst

= 1.17.2 =
* FIX: Doppelter Cookie-Banner — die tatsaechliche Ursache sass im Static Cache. Beim Schreiben jeder Cache-Datei wurde der Banner bedingungslos vor </body> injiziert, obwohl der wp_footer-Hook ihn laengst gerendert hatte: jede gecachte Seite trug ihn doppelt, die ungecachte Auslieferung nicht. Injiziert wird jetzt nur noch, wenn kein Banner im HTML steht (Fallback fuer Templates ohne wp_footer)
* Die Absicherungen aus 1.17.1 (idempotentes render_banner, Cleaner-Deduplizierung) bleiben als zusaetzliche Netze bestehen
* Nach dem Update Static Cache leeren — die vorhandenen Cache-Dateien tragen den Banner noch doppelt und werden erst beim Neuaufbau korrekt

= 1.17.1 =
* FIX: Cookie-Banner konnte doppelt im DOM stehen. Zwei Absicherungen: (1) render_banner ist jetzt idempotent, falls ein Theme wp_footer mehrfach aufruft; (2) der HTML-Cleaner entfernt einen im gespeicherten Seiteninhalt steckenden Alt-Banner (z.B. aus einem Re-Import einer live gerenderten Fassung) und behaelt die frisch gerenderte Fassung
* Die Dedup-Logik nutzt balancierte div-Zaehlung statt Regex — der Banner enthaelt verschachtelte Container, eine non-greedy Regex haette ihn zerschnitten. Bei unbalanciertem HTML wird nichts angefasst

= 1.17.0 =
* NEU: Schalter "HTML-Kommentare entfernen" unter Einstellungen -> Quelltext bereinigen. Der Kommentar-Strip lief bisher immer und ohne Option — jetzt abschaltbar, Standard bleibt an
* Entfernt werden nur Kommentare in der AUSLIEFERUNG; der gespeicherte Seiteninhalt samt Arbeitsnotizen bleibt unveraendert
* Funktionsmarker des Plugins (pgu-*) und Conditional Comments bleiben in jedem Fall erhalten
* Beim Umschalten wird der Static Cache geleert, damit die Aenderung sofort greift

= 1.16.0 =
* NEU: Seite "Bild-Links" unter Medien — alle Bild-URLs einer Mediathek-Gruppe auf einen Blick. Klick auf einen Link kopiert ihn, "Alle kopieren" legt die komplette Liste (eine URL je Zeile) in die Zwischenablage
* Auch "Ohne Gruppe" waehlbar — zeigt Bilder, die keinem Ordner zugeordnet sind
* Clipboard-Fallback fuer http-Seiten und aeltere Browser

= 1.15.2 =
* FIX (Datenschutz-Check): Google-Maps-Einbindungen in der Query-Form (google.com/maps?q=...&output=embed) wurden nicht erkannt — nur /maps/embed und die JS-API. Neuer Marker output=embed; reine Maps-LINKS schlagen weiterhin nicht an, da ein Link beim Seitenaufruf nichts laedt
* FIX (Datenschutz-Check): Kontakt, Impressum, Datenschutz und Anfahrt werden jetzt immer mitgescannt. Vorher zaehlten nur die zuletzt geaenderten Seiten — gerade Kontaktseiten mit Karte werden aber selten bearbeitet und fielen so aus der Auswahl
* FIX (Datenschutz-Check): Bei aktiver Baustellenseite haette der Scan auf jeder URL nur die Baustellenseite geprueft. Der Scan schaltet sich jetzt mit dem Vorschau-Schluessel frei
* Die Ergebnisseite listet jetzt auf, WELCHE Seiten geprueft wurden — nicht nur wie viele

= 1.15.1 =
* FIX (WebP-Upload): Hochkant-Handyfotos waeren nach der Konvertierung quer gelegen. Die EXIF-Rotation wird jetzt VOR der Konvertierung angewandt — WordPress haette sie erst danach angewandt, auf der dann bereits EXIF-losen WebP-Datei
* FIX (Datenschutz-Check): Bei 20 gewaehlten Seiten konnte der Scan das PHP-Zeitlimit reissen und wortlos abbrechen. Timeout je Abruf gesenkt, Zeitbudget je Durchlauf verlaengert

= 1.15.0 =
* NEU: Datenschutz-Check — neue Adminseite, die die eigene Website abruft und eingebundene Drittanbieter-Dienste findet
* Erkennt Google Analytics, Tag Manager, Maps, Fonts, YouTube, Vimeo, Instagram, Meta-Pixel, reCAPTCHA, Matomo, Hotjar, Stripe und externe CDNs
* Gescannt wird das ausgelieferte HTML, nicht nur die Plugin-Liste — Dienste aus Theme oder Customizer werden so ebenfalls gefunden
* Prueft je Fund, ob der Dienst bereits ueber den Cookie-Banner blockiert wird oder ungefragt laedt
* Gibt Stichpunkte aus, was in die Datenschutzerklaerung gehoert — bewusst KEINE fertigen Textbausteine, da die Rechtsgrundlage von der konkreten Konfiguration abhaengt
* Liste haeufig gefragter, aber nicht relevanter Dienste (Search Console, Bing Webmaster Tools, Sitemap-Plugins)

= 1.14.0 =
* NEU: Mediathek-Uploads koennen automatisch zu WebP konvertiert werden — Schalter auf der Seite "Bilder (WebP)", standardmaessig aus
* Nutzt dieselben Werte wie der Bild-Localizer (Qualitaet, max. Abmessungen) — nur ein Ort fuer die Einstellungen
* Konvertiert wird vor dem Anlegen des Anhangs, deshalb entstehen auch Thumbnail, Medium und Large als WebP
* Wird die WebP-Datei groesser als das Original (kommt bei kleinen PNGs mit wenigen Farben vor), bleibt das Original erhalten
* Animierte GIFs und vorhandene WebP-Dateien bleiben unangetastet; ohne WebP-Unterstuetzung des Servers passiert nichts
* Statistik auf der Admin-Seite: Anzahl konvertierter Uploads und gesparter Speicher

= 1.13.1 =
* NEU: Anrede des Cookie-Banners zwischen Du und Sie umstellbar — gilt fuer alle vier Designs
* Bannertexte zentral abgelegt statt ueber die Designs verteilt; nebenbei fehlende Kommas in den Saetzen ergaenzt
* Bestehende Installationen bleiben bei Du, solange nichts umgestellt wird

= 1.13.0 =
* NEU: HTTPS-Bereich in den Einstellungen — findet http-Adressen der eigenen Domain in der Datenbank und zeigt an, in welchen Tabellen sie stecken
* NEU: Serialisierungssichere Migration von http auf https. Ein einfaches SQL-REPLACE zerstoert serialisierte Werte wie theme_mods (Customizer-Logo), weil dort die Stringlaenge fest kodiert ist — hier wird jeder Wert ausgepackt, ersetzt und neu serialisiert
* NEU: Testlauf vor der Migration, der nur zaehlt und nichts schreibt
* NEU: Sofortmassnahme — verbliebene http-Adressen der eigenen Domain werden im ausgelieferten HTML hochgestuft, ohne Datenbankeingriff. Behebt Mixed-Content-Warnungen der Browserkonsole
* NEU: Optionale 301-Weiterleitung von http auf https, erkennt X-Forwarded-Proto hinter einem Reverse Proxy und vermeidet so Weiterleitungsschleifen
* Fremde http-Adressen bleiben in allen Faellen unberuehrt

= 1.12.1 =
* NEU: Der Widerrufs-Zugang laesst sich jetzt als unauffaelliger Textlink am Seitenende ausgeben statt als schwebendes Symbol — auf dem Handy lag das runde Icon oft ueber Inhalt oder Sticky-Buttons
* Drei Varianten waehlbar: schwebendes Symbol, Textlink am Seitenende, keiner (fuer Seiten mit eigenem [pgu_cookie_settings] im Footer)
* Bestehende Installationen behalten ihr bisheriges Verhalten

= 1.12.0 =
* NEU: Baustellenseite — die komplette Website laesst sich fuer Besucher sperren, waehrend das Redesign live entsteht
* NEU: Als Baustellenseite kann eine im Dashboard gebaute PageUpgrade-Page gewaehlt werden; ohne Auswahl greift eine schlichte eingebaute Seite
* NEU: Geheim-Link (?vorschau=SCHLUESSEL) schaltet ein Geraet fuer 30 Tage frei; Schluessel jederzeit neu erzeugbar, alte Links werden damit ungueltig
* NEU: Angemeldete Redakteure sehen die Website waehrend der Sperre normal
* Antwortet mit HTTP 503 + Retry-After statt 200 — sonst indexiert Google die Baustellenseite als Seiteninhalt
* Static Cache wird bei aktiver Sperre geleert und abgeschaltet, sonst wuerde Apache gecachte Seiten daran vorbei ausliefern
* Login, Admin, REST, Cron, Let's-Encrypt und die /pgu-form/-Endpunkte bleiben immer erreichbar

= 1.11.1 =
* FIX: Quiz-Antworten gingen beim Absenden verloren — der Formular-Handler aus 1.11.0 lief in der Capture-Phase und damit VOR dem submit-Listener des Quiz, der die versteckten Felder erst befuellt. Folge: kein Score, kein pgu_score-Parameter auf der Danke-Seite. Betrifft alle Formulare mit eigenen submit-Listenern.

= 1.11.0 =
* NEU: Leadformulare zeigen eine Erfolgsmeldung an der Stelle des Formulars, statt still auf dieselbe Seite zurueckzuleiten
* NEU: Umschalter "Erfolgsmeldung anzeigen" / "Auf Danke-Seite weiterleiten" je Formular, mit eigenem Text und eigener Double-Opt-In-Variante
* NEU: Absenden per fetch() ohne Seitenwechsel, Schutz gegen Doppelklick, Event pgu:lead:success fuer GTM/GA4
* NEU: Google Fonts werden jetzt auf der GESAMTEN Seite lokalisiert (auch Theme und Fremd-Plugins), nicht mehr nur im PageUpgrade-HTML
* NEU: Einstellungspanel fuer Schriften inkl. Cache-Statistik und Pruefung des Frontends auf verbliebene Google-Referenzen
* NEU: Dauerhaftes Cookie-Widget in der Fussecke zum Widerruf der Einwilligung (Art. 7 Abs. 3 DSGVO)
* FIX: Fehlgeschlagener Font-Download faellt nicht mehr stillschweigend auf den Google-Link zurueck
* FIX: Cookie-Auswahl wird beim erneuten Oeffnen mit der bestehenden Einwilligung vorbelegt
* FIX: Widerruf einer Kategorie laedt die Seite neu, damit bereits eingebundene Tracking-Skripte tatsaechlich verschwinden

= 1.4.0 =
* NEU: Lead-Erfassung mit Webhook-Endpunkten
* NEU: Double-Opt-In Prozess
* NEU: CSV-Export mit Filtern und exported_at-Tracking
* NEU: Weiterleitung an Zapier, n8n, Make, KlickTipp, Custom Webhooks
* NEU: Lead-KPIs Dashboard (Gesamt, Neue, Bestätigt, Heute)
* NEU: Honeypot Spam-Schutz
* NEU: Feature-Gate für Pro/Agency Pläne

= 1.3.0 =
* NEU: Application Password für Bilder-Upload
* NEU: Media-Upload Einstellungen pro API Key

= 1.2.0 =
* Popup-System
* CSS Override
* Template Library
