=== Biscotti CMP ===
Contributors: campcruisers, danielbosch
Donate link: https://www.biscotti-cmp.com
Tags: cookie-consent, gdpr, consent-management, tcf, privacy
Requires at least: 6.0
Tested up to: 7.0
Stable tag: 4.8.5
Requires PHP: 7.4
License: GPL v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

GDPR Cookie Consent Management with cloud-based detection, Google Consent Mode v2, and TCF 2.3.

== Description ==

**Biscotti CMP** is a WordPress plugin that connects your website to the [Biscotti Cloud](https://www.biscotti-cmp.com) consent management platform. The plugin's admin screens — including the banner designer, settings, compliance overview, and legal documents — are rendered **natively inside your WordPress dashboard** by the plugin's own PHP templates. The plugin communicates with the Biscotti Cloud API only to **exchange data** (your banner configuration, consent records, cookie-scan results, and legal documents) over a documented REST/JSON interface. Cookie scanning, consent analytics, and AI legal-document generation run on the Biscotti Cloud service; everything you see and edit in WordPress is rendered locally by the plugin.

**Important:** A free or paid Biscotti account is required. You can create an account at [app.biscotti-cmp.com](https://app.biscotti-cmp.com). By creating an account, you agree to the Campcruisers GmbH Terms of Service ([Deutsch](https://www.biscotti-cmp.com/de/agb) | [English](https://www.biscotti-cmp.com/terms)), Privacy Policy ([Deutsch](https://www.biscotti-cmp.com/de/datenschutz) | [English](https://www.biscotti-cmp.com/privacy)), and Data Processing Agreement ([Deutsch](https://www.biscotti-cmp.com/de/avv) | [English](https://www.biscotti-cmp.com/dpa)).

= Key Features =

* **GDPR & DSGVO Compliant** — Full compliance with European data protection regulations
* **Google Consent Mode v2** — Native integration for Google Analytics, Ads, and Tag Manager
* **Cloud-Based Cookie Detection** — Automatic scanning and categorization of cookies and tracking scripts via the Biscotti Cloud
* **EU AI Act Transparency (Art. 50)** — Dedicated "AI Services" consent category for chatbots, recommendation systems, and AI-generated content, supporting the transparency obligations under Article 50 of Regulation (EU) 2024/1689 (EU AI Act)
* **GPC (Global Privacy Control) Support** — Automatic detection and respect of browser privacy signals
* **Multi-Language** — Available in 44 languages including German, English, French, Spanish, Italian, Dutch, Polish, Portuguese (Brazil & Portugal), Danish, Swedish, Finnish, Norwegian, Czech, Slovak, Hungarian, Romanian, Bulgarian, Croatian, Bosnian, Serbian, Slovenian, Greek, Turkish, Russian, Ukrainian, Japanese, Chinese (Simplified & Traditional), Arabic, Hindi, Marathi, Hebrew, Thai, Vietnamese, Indonesian, Catalan, Basque, Galician, Lithuanian, Estonian, Latvian, Maltese, and Irish
* **Geo-IP Based Compliance** — Show appropriate banners based on visitor location
* **TCF 2.3 Compatible** — Full Transparency & Consent Framework 2.3 support with Vendor List, partner disclosure, and configurable TCF toggle in plugin settings
* **Accessibility Widget** — An optional, separate ♿ button (independent of the cookie banner) that lets visitors adjust contrast, text size, spacing, fonts, reading aids and read-aloud, with one-click profiles for visual impairment, dyslexia, ADHD, epilepsy and colour blindness, in all 44 languages. An assistive layer for visitors, not a replacement for accessible markup. Requires a Growth plan or higher and plugin version 4.3.1 or later
* **Customizable Design** — Match your brand colors and style with 21 template presets including seasonal and multicultural themes, plus custom icon upload
* **Zero-Waste Rollover** — Unused sessions roll over to next month (paid plans)

= Why Biscotti CMP? =

Unlike other consent tools, Biscotti CMP was built with **German privacy law expertise** and focuses on:

1. **Legal Compliance First** — Every feature is designed with GDPR Article 7 in mind
2. **Transparency** — Clear documentation of all cookies with purpose, duration, and legal basis
3. **User-Friendly** — Clean, modern interface that doesn't annoy your visitors
4. **Performance** — Lightweight script that won't slow down your website
5. **Fair Pricing** — Pay only for what you use with metered billing

= Perfect For =

* Bloggers and content creators
* E-commerce stores (WooCommerce compatible)
* Agencies managing multiple client websites
* Enterprises requiring detailed consent documentation
* Anyone using Google Analytics, Facebook Pixel, or marketing tools

= How It Works =

1. Create a free account at [app.biscotti-cmp.com](https://app.biscotti-cmp.com)
2. Install the plugin and enter your Website ID in the setup wizard
3. The cloud-based scanner automatically detects cookies and scripts on your website
4. Customize your consent banner appearance in the Biscotti Dashboard or via the WordPress admin
5. Go live with compliant cookie consent!

= Integrations =

* Google Analytics 4
* Google Tag Manager
* Google Ads
* Facebook Pixel
* Meta Conversions API
* Matomo
* HubSpot
* Hotjar
* LinkedIn Insight Tag
* TikTok Pixel
* All major marketing & analytics tools

= Page Builder Compatibility =

Biscotti CMP provides native widgets, blocks, or modules for all major page builders:

* **Gutenberg Block Editor** – 9 native blocks with inspector controls and live preview
* **Elementor** – 8 dedicated widgets in the Biscotti CMP category
* **Divi Builder** – 8 native modules with visual builder support
* **Beaver Builder** – 8 native modules in the Biscotti CMP group
* **WPBakery (Visual Composer)** – 8 mapped elements with visual controls
* **Oxygen Builder** – 8 native elements with option controls
* **Spectra** – Works automatically via Gutenberg blocks
* **Kadence Blocks** – Works automatically via Gutenberg blocks
* **Brizy** – Works via native shortcode support
* **Thrive Architect** – Works via native shortcode support

= Translation Plugin Compatibility =

* **WPML** – Full support via Language Bridge
* **Polylang** – Full support via Language Bridge
* **TranslatePress** – Full support via Language Bridge
* **Weglot** – Full support via Language Bridge
* **MultilingualPress** – Full support via Language Bridge
* **Loco Translate** – Full support (standard WP locale)

= WooCommerce Compatibility =

* Automatic cookie categorization (essential, analytics, marketing)
* Consent-aware tracking script deferral
* Google Analytics for WooCommerce integration

== External Services ==

This plugin connects to the external Biscotti Cloud platform to provide its core functionality. Cookie scanning, consent analytics, and legal-document generation are performed on the Biscotti Cloud service, not locally. The plugin's admin screens are rendered locally in WordPress by the plugin's own PHP templates; the plugin exchanges only **data** (not user-interface code) with the Cloud API. **The plugin requires an active connection to the Biscotti Cloud API to function.**

The following external services are used:

= 1. Biscotti Cloud API (Required Service) =

The Biscotti Cloud API is the core external service that powers the plugin's data and consent functionality. The plugin's WordPress admin pages are rendered **locally** by the plugin's PHP templates; they call the Cloud API over a documented REST/JSON interface to read and write data.

**Admin data exchange:** When you open a Biscotti CMP admin page, the plugin requests the relevant **data as JSON** from `api.biscotti-cmp.com` (for example: your saved banner configuration, settings, consent statistics, cookie-scan results, or legal documents) and renders it locally using its own PHP templates. When you save a setting, the plugin sends the changed values as JSON to the same API. No user-interface markup or executable code is downloaded from the API for the admin screens.

**Consent banner script:** On every frontend page load (when the banner is enabled), the plugin loads the consent banner JavaScript (`biscotti.min.js`) from `api.biscotti-cmp.com/scripts/biscotti.min.js`, so all sites receive the current engine without manual plugin updates. A copy of the same script ships with the plugin (in `assets/js/`) as an offline fallback. This script renders the consent banner, manages visitor consent choices, and communicates consent decisions back to the Cloud API.

**Domains contacted:**

* `api.biscotti-cmp.com` — API server for admin data exchange, configuration, consent data, and delivery of the consent banner script (`biscotti.min.js`)

**Data sent to the Biscotti Cloud API:**

* Website domain and Website ID (for account identification)
* Consent preferences (visitor consent choices per category)
* Anonymized visitor data (consent interactions, no personal data)
* Configuration settings (banner design, language, enabled features)
* Legal entity data (company name, address, contact details — only when using the Legal Suite)

**Service Provider:**
Campcruisers GmbH
Berliner Str. 21 B
D-14612 Falkensee, Germany

Commercial Register: HRB 40180 P (District Court of Potsdam)
VAT ID: DE368000447

* Terms of Service: [Deutsch](https://www.biscotti-cmp.com/de/agb) | [English](https://www.biscotti-cmp.com/terms)
* Privacy Policy: [Deutsch](https://www.biscotti-cmp.com/de/datenschutz) | [English](https://www.biscotti-cmp.com/privacy)
* Data Processing Agreement (AVV): [Deutsch](https://www.biscotti-cmp.com/de/avv) | [English](https://www.biscotti-cmp.com/dpa)
* Imprint: [Deutsch](https://www.biscotti-cmp.com/de/impressum) | [English](https://www.biscotti-cmp.com/imprint)

= 2. Google Gemini AI (Legal Document Generation) =

The plugin's Legal Suite feature uses **Google Gemini AI** to generate legal documents including Impressum (Legal Notice), Privacy Policy, and Cookie Policy.

**Trigger:** AI document generation is triggered **only by explicit user action** in the Legal Suite wizard. It is never triggered automatically. The site administrator must actively click the "Generate" button in the wizard to initiate AI document generation.

**Data flow:** When the administrator triggers document generation, the following data is sent from WordPress to the Biscotti Cloud API, which then forwards it to Google Gemini for processing:

* Company name, legal form, and registration details
* Business address and contact information (email, phone, fax)
* Data Protection Officer (DPO) information (if provided)
* Industry-specific details provided in the Legal Suite wizard

**Processing location:** All AI processing occurs server-side on the Biscotti Cloud infrastructure. No AI processing occurs within the WordPress installation. The generated documents are returned to WordPress via the Cloud API.

**Legal Disclaimer:** AI-generated legal documents may contain errors, omissions, or inaccuracies. Campcruisers GmbH does not guarantee the correctness, completeness, or legal validity of any AI-generated document. **We expressly recommend that all AI-generated documents be reviewed by a qualified legal professional (Fachanwalt) before publication on your website.** Use of AI-generated documents is at your own risk. Biscotti CMP does not constitute legal advice and cannot replace consultation with a qualified attorney.

* Google Gemini Terms of Service: [https://ai.google.dev/gemini-api/terms](https://ai.google.dev/gemini-api/terms)
* Google Privacy Policy: [https://policies.google.com/privacy](https://policies.google.com/privacy)

= 3. GeoIP Location Services =

The plugin uses external GeoIP APIs to determine visitor location for regional compliance (showing GDPR banners for EU visitors, CCPA notices for California visitors, etc.).

**Important — Pre-Consent Processing:** GeoIP lookups occur **before** the consent banner is displayed, because the plugin must determine the visitor's region to show the legally correct banner type. This processing is based on **legitimate interest (GDPR Art. 6(1)(f))** — without knowing the visitor's country, the plugin cannot fulfill its core purpose of providing region-appropriate consent management.

**Privacy-preserving measures:**

* The plugin first checks CDN/proxy headers (e.g., CloudFlare `CF-IPCountry`) which require no external request
* External API calls are only made as a **fallback** when no CDN header is available
* Only the **2-letter country code** is retrieved — no precise location, city, or coordinates
* Results are **cached server-side for 24 hours** to minimize external requests

**ipapi.co**

* Service: IP geolocation lookup (primary provider)
* When: On first page load by a visitor if no CDN header is available (result cached for 24 hours)
* Data sent: Visitor's IP address
* Data received: 2-letter country code only
* Privacy Policy: [https://ipapi.co/privacy/](https://ipapi.co/privacy/)

**ipinfo.io**

* Service: IP geolocation lookup (first fallback)
* When: If primary lookup (ipapi.co) fails, or for US state-level compliance (CCPA)
* Data sent: Visitor's IP address
* Data received: Country code or US state name
* Privacy Policy: [https://ipinfo.io/privacy](https://ipinfo.io/privacy)

**geoplugin.net**

* Service: IP geolocation lookup (second fallback)
* When: If both ipapi.co and ipinfo.io are unavailable
* Data sent: Visitor's IP address
* Data received: Country code (extracted from JSON response)
* Privacy Policy: [https://www.geoplugin.com/privacy](https://www.geoplugin.com/privacy)

= 4. IAB TCF Vendor List =

The plugin supports the **IAB Transparency & Consent Framework (TCF) versions 2.2 and 2.3**. When TCF is enabled, the plugin loads the IAB Global Vendor List from the Biscotti Cloud API and manages vendor consent signals.

**Data flow:** When a visitor grants consent via the TCF-compliant banner, vendor consent signals (TC String) are generated and transmitted to participating ad-tech vendors according to the IAB TCF specification. These signals indicate which vendors the visitor has consented to for specific processing purposes.

**Default state:** TCF support is **enabled by default**. Site administrators can disable TCF in the plugin settings under Settings → Consent.

* IAB TCF Policies: [https://iabeurope.eu/tcf-2-0/](https://iabeurope.eu/tcf-2-0/)

= 5. Third-Party Script Detection =

The plugin's consent banner script (`biscotti.min.js`) contains domain name patterns used to detect, manage, and block third-party services that may be present on your website. These patterns include references to domains such as `googletagmanager.com`, `google-analytics.com`, `facebook.net`, `connect.facebook.net`, `intercom.io`, `widget.intercom.io`, and many others.

**Important:** The plugin itself does **not** connect to any of these third-party services. These domain references are used **exclusively** for:

* **Consent management blocking rules** — intercepting and blocking scripts until the visitor grants consent
* **Content Security Policy (CSP) generation** — generating appropriate CSP headers based on consented services

The actual connections to these third-party services only exist if the site operator has independently installed them on their website. The plugin merely manages consent for those pre-existing services.

= 6. Cookie & Local Storage =

The plugin sets the following storage entries on the visitor's browser:

**Cookie: `biscotti_consent`**

* Purpose: Stores the visitor's consent decisions as a JSON object (which categories were accepted or rejected)
* Duration: 365 days (configurable by the site administrator)
* Set: Only after the visitor interacts with the consent banner (not on page load)
* Classification: Strictly necessary for consent management

**Local Storage: `biscotti_consent`**

* Purpose: Stores the same consent decisions in the browser's localStorage for client-side consent verification
* Persistence: Until the visitor clears browser data or revokes consent
* Classification: Strictly necessary for consent management

Both storage mechanisms are **strictly necessary** for the plugin's core purpose of managing and verifying cookie consent decisions. Neither is set until the visitor actively interacts with the consent banner.

= 7. Data Processing (GDPR Art. 28) =

When the plugin transmits visitor consent data and website configuration to the Biscotti Cloud API, **Campcruisers GmbH acts as data processor** under GDPR Article 28 on behalf of the website operator (data controller).

**Data Processor:**
Campcruisers GmbH
Berliner Str. 21 B
D-14612 Falkensee
Brandenburg, Federal Republic of Germany

Commercial Register: HRB 40180 P (District Court of Potsdam)
VAT ID: DE368000447
Managing Director: Daniel Bosch

**Data residency:** All data is stored on **EU servers located in Germany**. No data is transferred outside the European Union.

**Data Processing Agreement (DPA/AVV):**

* Deutsch: [https://www.biscotti-cmp.com/de/avv](https://www.biscotti-cmp.com/de/avv)
* English: [https://www.biscotti-cmp.com/dpa](https://www.biscotti-cmp.com/dpa)

Website operators who use this plugin are advised to enter into a Data Processing Agreement with Campcruisers GmbH to fulfill their obligations under GDPR Article 28.

**Full legal links:**

* Terms of Service: [Deutsch](https://www.biscotti-cmp.com/de/agb) | [English](https://www.biscotti-cmp.com/terms)
* Privacy Policy: [Deutsch](https://www.biscotti-cmp.com/de/datenschutz) | [English](https://www.biscotti-cmp.com/privacy)
* Data Processing Agreement (AVV): [Deutsch](https://www.biscotti-cmp.com/de/avv) | [English](https://www.biscotti-cmp.com/dpa)
* Imprint: [Deutsch](https://www.biscotti-cmp.com/de/impressum) | [English](https://www.biscotti-cmp.com/imprint)

== Source Code ==

The full plugin source code is publicly available at:
https://github.com/dbosch-a11y/biscotti-cmp-wordpress

The unminified source code for biscotti.min.js is included as biscotti.js in the assets/js/ directory.
The client-side consent engine source code repository is publicly available at:
https://github.com/dbosch-a11y/biscotti-cmp-client

Build instructions are documented in assets/js/BUILD.md.
Build tool: esbuild
Build command: npm run build:engine

== Installation ==

= Automatic Installation =

1. Go to Plugins → Add New in your WordPress admin
2. Search for "Biscotti CMP"
3. Click "Install Now" and then "Activate"
4. Open Biscotti CMP → Dashboard; the Express Setup wizard opens automatically on first activation

= Manual Installation =

1. Download the plugin ZIP file
2. Go to Plugins → Add New → Upload Plugin
3. Choose the ZIP file and click "Install Now"
4. Activate the plugin
5. Navigate to Biscotti CMP in your admin menu

= Configuration =

1. Complete the Express Setup wizard (opens automatically after activation)
2. Enter your Website ID from the [Biscotti Dashboard](https://app.biscotti-cmp.com)
3. Customize your banner under Biscotti CMP → Banner Designer
4. Review detected third-party services under Biscotti CMP → Services

**Note:** The plugin requires an active internet connection to the Biscotti Cloud API. The admin screens are rendered locally in WordPress; the plugin exchanges configuration and consent **data** with the Cloud API over a REST/JSON interface (see the External Services section above for full details).

== Frequently Asked Questions ==

= Is Biscotti CMP free? =

Yes! The Free plan includes 5,000 sessions/month with essential GDPR features. Paid plans start at €4.99/month for higher session limits and advanced features (managed entirely via your Biscotti Dashboard).

= Does it work with caching plugins? =

Yes, Biscotti CMP is fully compatible with all major caching plugins including WP Rocket, W3 Total Cache, LiteSpeed Cache, and WP Super Cache.

= Is it GDPR compliant? =

Absolutely. Biscotti CMP was designed by German privacy experts to meet all GDPR requirements including Article 7 (Conditions for consent), Article 12 (Transparent information), and Article 13 (Information to be provided).

= Does it support Google Consent Mode v2? =

Yes! Native Google Consent Mode v2 integration is included. Consent signals are automatically sent to Google Analytics, Ads, and Tag Manager based on user choices.

= Can I customize the appearance? =

Yes, you can fully customize colors, texts, position, and style of the consent banner to match your brand.

= Does it block scripts before consent? =

Yes, the auto-blocking feature prevents tracking scripts from loading until the user gives consent, ensuring legal compliance.

= Is there a cookie scanner? =

Yes! The cloud-based scanner automatically detects and categorizes cookies and tracking technologies on your website. The scan is performed by the Biscotti Cloud service — the plugin transmits your website's URL and receives a structured list of detected cookies, scripts, and third-party services. Any cookies detected include their name, provider, purpose, duration, and legal basis.

= How does the EU AI Act transparency category work? =

Biscotti CMP includes a dedicated "KI-Dienste" (AI Services) consent category. Article 50 of the EU AI Act (Regulation (EU) 2024/1689) requires providers and deployers of certain AI systems to ensure transparency — particularly that individuals are informed when they are interacting with an AI system (e.g., chatbots, recommendation engines, or AI-generated content). The "AI Services" category allows website operators to obtain and document separate user consent for AI-powered features on their website, supporting compliance with these transparency obligations.

Please note: This feature provides a technical framework for managing AI-related consent. It does not constitute legal advice and does not guarantee full compliance with the EU AI Act, which may impose additional obligations depending on the risk classification of specific AI systems.

= Can I use it on multiple websites? =

Yes! Depending on your plan, you can use Biscotti CMP on multiple domains. The Agency plan includes unlimited domains.

= Where is the data stored? =

All data is stored on EU servers (Germany) in compliance with GDPR. No data is transferred outside the EU. See the External Services section for full details.

= Does the plugin send data before consent is given? =

The plugin performs a GeoIP lookup before displaying the consent banner to determine the visitor's country. This is necessary to show the legally correct banner type (GDPR for EU, CCPA for California, etc.) and is based on legitimate interest (GDPR Art. 6(1)(f)). See the GeoIP Location Services section under External Services for full details on privacy-preserving measures.

== Screenshots ==

1. Modern, customizable consent banner
2. Detailed cookie information with legal basis
3. Granular consent options per category
4. WordPress admin dashboard
5. Setup wizard for easy configuration
6. Cookie scanner results
7. Analytics and consent statistics

== Changelog ==
= 4.8.5 =
* Security: The bundled pre-boot shield now keeps previously consented third-party scripts blocked while a one-time dashboard authentication handoff is removed from the address bar and copied into its first-party exchange request.
* Reliability: Rebuilt all consent-engine, pre-boot and platform mirror artifacts through the complete release producer chain so the immutable WordPress package contains the current guarded runtime.

= 4.8.4 =
* Security: Every mutating cloud request now requires a per-site Connection Token, while existing installations receive a clear reconnect notice instead of silently retaining Website-ID-only write access.
* Reliability: Paid registration, Stripe subscription changes and affiliate reporting now use durable idempotent operations, leased reconciliation workers and fail-closed account/customer bindings.
* Privacy: PartnerStack attribution runs only after marketing consent, is revoked across tabs and devices, removes SDK state and cookies, and never reports Stripe test invoices.
* Fix: The bundled consent engine and pre-boot shield now share the current exact-host tracker rules, live consent state and safe placeholder ownership across all platform copies.
* Fix: Jurisdiction-specific legal registration fields keep their original values when the business seat changes and remain consistent between WordPress, dashboard, API and Shopify.

= 4.8.3 =
* Security and reliability: Synchronizes the current consent engine, fail-closed website identity checks, scan lifecycle hardening and platform integration fixes into one immutable release package.

= 4.8.2 =
* Fix (important): Invalid, deleted or mistyped connection credentials are verified before they replace the working local connection. A failed cloud lookup now leaves the reconnect form visible instead of stranding the Settings page in an empty state.
* Performance: Public page requests no longer wait for Biscotti Cloud while loading white-label branding, A/B tests, the accessibility widget or GeoIP fallback data. Bounded background jobs refresh local snapshots and retain the last good result during temporary failures.
* Fix: Concurrent refreshes can no longer restore configuration that was invalidated by a save or disconnect; missed locks are retried and cleanup targets only the exact site's scheduled work.
* Fix: The regional shortcode translation resolver that was present in source but absent from the 4.8.1 package is now included. Visitor-facing shortcode labels resolve all 44 canonical locales, including Maltese, Brazilian Portuguese and Traditional Chinese.

= 4.8.1 =
* New: Biscotti CMP now has its own WordPress update channel. New releases appear in Plugins and in Dashboard → Updates, and WordPress can install them automatically when you enable auto-updates for Biscotti CMP.
* Important: Versions up to and including 4.8.0 did not contain this updater. Install 4.8.1 once manually; every later release can then arrive through WordPress.

= 4.8.0 =
* Fix (security, important): Any mutating request the plugin makes now requires a per-site Connection Token. Until this release the API accepted a write using only the Website ID — a value that is printed into the public HTML of every page the banner runs on, so anyone who viewed the page source could change that account's banner, company and register details, branding and documents, or run up its document-generation costs. Copy the Connection Token from the Biscotti dashboard (Installation) into the plugin's settings; until you do, the plugin can still READ your settings but no longer save them, and it will say so.
* New: Accessibility Widget can be switched on and configured here. Position, button colour, widget language, which of the 19 tools your visitors get and which of the 5 profiles — previously all of this could only be reached in the web app, even though the plugin already delivered the widget. The settings live in your Biscotti account, so this page and the web app always show the same values; there is no separate WordPress copy to get out of step.
* New: Legal documents can be translated into any of the 44 languages from the Documents page, using the translation key stored in your account. The wording is translated 1:1 — the legal basis is not re-generated — and the result is saved as a draft for you to review and publish.
* Fix (important): Documents in a regional language never rendered. A shortcode on a Brazilian-Portuguese or Taiwanese-Chinese page asked the API for "pt" or "zh", which is not where those documents are stored, and the page stayed empty. Regional languages are now resolved correctly (pt-BR, pt-PT, zh, zh-TW and the same for every other regional variant).
* Fix (important): Every language version of a document produced the SAME shortcode, so copying the shortcode from the Dutch row and the German row gave identical code and one of the two documents was unreachable. Each row now carries its own language. A second, language-free form is offered for WPML/Polylang sites, where each language already has its own page.
* Fix (important): A published document in a language the visitor's page does not have no longer shows an error aimed at the site operator. The closest available language is served instead — same region first, then the original the document was generated from, then English — with a short note naming the language shown.
* Fix: Machine-translated documents are marked as such, in both the plugin and the web app, and name the language they were translated from. A translation and a document generated for its own jurisdiction looked identical before, which mattered because only one of the two was written for the law it cites.
* Fix: Assigning a document to a page no longer appends a second shortcode when the page already has one for that document type in another language — it says which one is already there instead of rendering the document twice.
= 4.7.0 =
* Fix (important): The banner engine bundled with the plugin was two weeks behind the one our cloud delivers, so sites that load the plugin's own copy did not receive the banner fixes below. The bundled engine is rebuilt with this release.
* Fix (important): An uploaded icon was replaced by the browser's broken-image symbol, and the emoji fallback disappeared with it, so the floating button could render empty. Icons are read as embedded image data, which the previous release's link check rejected. The stored icon was never damaged.
* Fix (important): 73 banner values reached the page as markup instead of text, among them the IAB Global Vendor List purpose, feature and vendor descriptions, retention periods and data categories. All 338 places where the banner inserts a value are now accounted for.
* New: The white-label logo has a configurable width.
* Fix: Clicking a consent button sent more than one request to the consent endpoint.

= 4.6.0 =
* Fix (important): The Legal Wizard asked for 24 of the 176 details the cloud app asks for. Everything the app collects is now collected here: identity and representatives, register and tax numbers, contract formation, payment, delivery, liability, warranty, withdrawal, subscriptions, disputes, marketplace terms, packaging and electronics registration (EPR), product safety (GPSR), the transparency register, and the V.i.S.d.P. details for journalistic content. The wizard no longer keeps its own idea of what to ask — it renders the same definition the app does.
* Fix: Sub-Industry was a free-text box. It is a proper selection now, showing the sub-industries of the industry you picked — 69 of them across 12 industries.
* Fix: The industry list held 13 entries in a spelling the app does not use, so a company set up in the app matched none of them and the Marketplace and Blog steps never appeared. The list now comes from the same source as the app's.
* Fix: Only 7 payment methods were offered, worldwide. You are now offered the methods that are actually usual in your country — 39 in Germany, 37 in the Netherlands, 34 in France.
* Fix: A registered association (e.V.) was asked for a commercial-register number (HRB) it cannot have. Each legal form is now asked for its own register — VR for an association, HRB or HRA for a company, the foundation register for a Stiftung — and forms without a register are not asked at all.
* Fix: The authorised representative's role was saved as "CEO" regardless of the legal form. The field now suggests the titles that fit the form you chose (Geschäftsführer for a GmbH, Vorstand for an e.V., Gesellschafter for a GbR) and accepts your own wording.
* Fix: Two of the six business-context questions were missing, so you could not state that visitors contribute content or that you process personal data on behalf of clients. Both decide which documents you need.
* Fix: Saving from WordPress could overwrite details entered in the cloud app with empty values. Answers this wizard does not ask about are now preserved untouched.
* Fix: The Terms questionnaire offered 81 of its 151 questions. The missing 70 included the blog and affiliate questions, the withdrawal exceptions, the complaints officer, the right to repair, the cancellation button and the out-of-court dispute bodies.
* Fix: Seven payment-method names, one help text and eight currency labels were shown in English in every language. All 44 bundled translations were rebuilt.
* Fix: Türkiye appeared twice in the country list.
* Fix (important): "Next" in the Legal Wizard moved to the following step even when the save had been rejected, so the page you had just filled in was gone and nothing said why. It now stays put and names what was refused.
* Fix (important): The wizard saves three things at once — your company details, the Terms questionnaire and the data processing agreement — and only the first one was checked. A rejected questionnaire or DPA still showed "Saved!". All three are now waited for, and the message names the one that failed.
* Fix (important): Answers to the jurisdiction-specific questions (the V.i.S.d.P. details, § 18 MStV) were discarded whenever a field that changes the form was edited, because that step was rebuilt from the state the page had been loaded with.
* Fix: Questions that depend on a multiple-choice answer — a payment method, a category of personal data — never appeared, whatever you ticked. The list of selections was recorded as a yes/no instead of as a list.
* Fix: Every rejected save on the Settings page reported "Saved!". The check treated the presence of an error message as a sign of success.
* Fix: In the document editor, a slow-loading document could overwrite a different one you had opened in the meantime, and Save was clickable while the text was still loading, which stored the word "Loading…" as the document.
* Fix: Error messages from the API were inserted into the page as markup on three screens. They are now inserted as text.
* Fix: The Accessibility scan page existed but had no menu entry, so there was no way to open it.
* Fix: A/B test variant assignment was not remembered. The cookie was written after the page had already started being sent, which silently does nothing, so every page view drew a new variant and the test measured noise.
* Fix: The registration-field labels of 174 countries were shown in the country's own script to everyone — Bulgarian, Persian, Dzongkha, Dhivehi, Korean — because the researched English wording next to them was never used. The jurisdiction picker likewise mixed "الجزائر", "Deutschland" and "India" in every language. Both now read in your language, in all 44.
* Fix: The 213 payment-method names and the 165 answer options for data categories, data subjects, seller verification and industry were shown in English to 42 of the 44 languages.
* Fix (security): The consent banner and its details view put text from outside the plugin into the page as markup instead of as text, at 105 places. The sources were the banner configuration (service names, vendor names, storage durations, legal bases, the white-label brand name and logo) and the IAB Global Vendor List, which is fetched from a third-party address — so a name containing HTML was rendered as HTML on every page of the site. Every one of them is now escaped. Policy, imprint and vendor links are checked for their protocol, so a "javascript:" address cannot be reached by clicking "Privacy policy". The banner's colours and corner radii are written into a stylesheet and are now filtered to the characters a CSS value may contain, which closes the same hole in the one place where escaping HTML would not have worked. Completeness is enforced by a build gate that lists every value the banner interpolates and fails on any that is neither escaped nor provably harmless.
* Fix: Documents were always generated in German. The language now follows the selected jurisdiction, or the override you set, so a French or US business gets French or English documents.
* Fix: Generating Terms or a privacy policy takes longer than the two minutes the request is allowed, and the page reported a connection error although the document had been written. It now waits and reports what actually happened.
* Fix: The compliance panel answered "Please select a jurisdiction first" for a jurisdiction that was selected, for every EU seat.
* Fix (important): On multi-region sites the banner configuration was cached under one key for the whole site, so the first visitor after the cache expired decided the applicable law, the consent mode and the Google Consent Mode defaults for everybody else. One visitor from the United States was enough to give every European visitor an opt-out banner with consent presumed. The cache is now kept per region.
* Fix (important): On a site where the banner is switched off or not connected, the WooCommerce tracker and Google Site Kit's scripts were removed from the page and never restored — the code that re-enables them after consent attaches to the banner, which was not there. Analytics were silently gone. Nothing is removed any more unless it can also be brought back.
* Fix (security, important): The consent state — the visitor's choices, the providers and the TCF string — was sent to every iframe on the page on each change, and handed to any frame that asked for it. An advertising frame or a video embed received the visitor's decisions. Worse, an update arriving from any frame was accepted, so a third party could fabricate consent and have the page release trackers the visitor never agreed to. Consent is now exchanged only with frames of the same site, addressed to that site; a separate domain can be added deliberately via `crossFrame.trustedOrigins`.
* Fix: A generic administration request lost its entire payload when a value contained a "<" — the JSON was passed through a text filter that deletes from that character to the end. The request then reached the API with no content and reported nothing.

= 4.5.0 =
* Fix: Saving a banner twice in the Banner Designer created a second banner instead of updating the first. The editor did not take over the id the server assigned on the first save, so until the page was reloaded every save asked for a new banner to be created.
* Change: The plugin no longer describes the documents it generates as "legally compliant". Three screens said so — the dashboard intro, the document generator and the jurisdiction questions — and our own Terms exclude any warranty for the result, so the wording promised something we do not stand behind. The documents, the questions and the generator are unchanged; only the claim is gone.
* Fix: All 44 bundled translations were recompiled, so the changed wording appears in your language and not in English. Norwegian was 294 of 380 strings still verbatim English and has been translated properly.

= 4.4.0 =
* Fix (important): Tracker scripts printed directly into the page HTML by your theme or another plugin were contacted BEFORE consent. A `<script src="...">` that is already in the served markup is fetched by the browser while it parses the page, which is earlier than any JavaScript on the page can run — so client-side blocking alone could not prevent it. The visitor's IP address, browser and any existing cookies reached the tracker without consent. Such tags are now neutralised on the server, before the page is sent, and only activated after the visitor consents.
* New: Server-side pre-blocking covers both scripts registered via wp_enqueue_script() and tags hardcoded into theme templates. The tracker list is generated from the same source as the consent engine's, so the two can never drift apart. Our own scripts, first-party assets and anything not recognised as a tracker are never touched.
* Fix: Microsoft Advertising (UET) and other tags whose initialisation relies on the script element's onload handler never started after consent — the script file loaded, but the tag never initialised, so no conversions were recorded. Also affected jQuery.getScript() and similar loaders.
* Fix: A single unrestorable element no longer aborts the release of every remaining script, iframe, stylesheet and image after consent.
* Fix: Consent-gated iframes were reloaded up to three times when consent was granted, which reset embedded players.
* Escape hatch: define( 'BISCOTTI_DISABLE_HTML_PREBLOCK', true ) or the biscotti_enable_html_preblock filter turns off rewriting of hardcoded tags if it conflicts with your optimisation stack.

= 4.3.1 =
* New: Accessibility Widget — a dedicated ♿ button that lets visitors adapt your website to their needs: text size, line height, letter and word spacing, high contrast, inverted colours, dark mode, grayscale, colour-blindness filters, a readable and a dyslexia-friendly font, highlighted links and headings, a large cursor, a stronger focus outline, paused animations, a reading mask, a reading ruler, and read-aloud (text-to-speech). Five one-click profiles cover visual impairment, dyslexia, ADHD, epilepsy and colour blindness. The panel is available in all 44 plugin languages. Note: this entry was missing from the 4.3.1 release notes and has been added retroactively — the feature has shipped since 4.3.1.
* New: Accessibility Widget loads as its own small script, independent of the cookie banner, so it also works on sites where the consent banner is switched off (and vice versa). Button position and colour are configurable; the "Powered by" link can be hidden on plans that include white-label branding.
* Note: Enable the widget in the Biscotti dashboard under "Accessibility Widget". It requires a Growth plan or higher. While it is switched off, the script is not loaded at all. Changes take up to 5 minutes to appear on the front end (the widget configuration is cached).
* Note: The Accessibility Widget is an assistive layer for visitors, not a substitute for accessible markup. Use the Accessibility Scanner for actual WCAG fixes.
* Fix: Legal document shortcodes ([biscotti_privacy_policy], [biscotti_impressum], [biscotti_cookie_policy], [biscotti-document]) now fetch from the public, language-aware delivery endpoint. Previously they called an authenticated endpoint that always failed, so the published document did not render. Now the correct published document is served in the page language (works with WPML / Polylang).
* Fix: Only published documents are served (no more accidental draft exposure).
* Fix: Per-language local cache — different language versions of the same document no longer overwrite each other.

= 4.3.0 =
* New: Content Blocker — a "Biscotti Content Blocker" Gutenberg block and [biscotti_block] shortcode wrap any content (YouTube, Maps, iframes, embeds) in a consent-gated, server-rendered placeholder that loads only after the visitor accepts the relevant cookie category. No page reload, no layout shift.
* New: Automatic oEmbed blocking — YouTube/Vimeo (marketing) and Google Maps (functional) embeds are gated automatically; provider-to-category mapping is filterable (biscotti_oembed_provider_map), toggle via option biscotti_block_oembed.
* New: Placeholder defaults localized across all 44 locales; theme hooks biscotti_placeholder_html and biscotti_placeholder_defaults for full customization.
= 4.2.3 =
* New: Added Estonian, Latvian, Maltese and Irish — the plugin, consent banner and admin UI now cover all 24 EU official languages (44 locales in total). Portuguese remains split into pt-BR and pt-PT.
* Fix: Estonian, Latvian, Maltese and Irish WordPress sites no longer fall back to English for the consent banner and language picker.
= 4.2.2 =
* Critical Fix: Tracking scripts that are pre-blocked before consent (Google Analytics, Meta Pixel, Microsoft UET, etc.) are now correctly re-activated after the visitor accepts. Previously a pre-blocked script could be restored with a non-executable type and never run, so analytics/marketing recorded no data even after consent was granted.
* Improved: Faster first paint of the IAB TCF banner on a cold first visit (preconnect to the Biscotti API), which reduces a brief non-IAB to IAB banner flash.
* Internal: Updated bundled consent engine (US sale/share opt-out + IAB GPP handling, Do-Not-Sell reset on positive consent).

= 4.2.1 =
* Rename: The "White Label" admin section is now called "Branding" (logo, brand color, custom CSS) for clarity.
* Improved: Jurisdiction and Target Region selectors now offer the full server-resolvable jurisdiction list, including individual US states (e.g. Texas / TDPSA).
* New: The IAB TCF 2.3 toggle is now available directly in the Banner Designer (per website), and the live preview reflects the IAB layout.
* Remove: The redundant "Setup" page was removed — connect and configure directly from the Dashboard and the Banner Designer.
* Remove: Dropped the inaccurate fixed "document count" on the Subscription page; the required documents are determined by the Legal Wizard.
* Fix: The "complete the setup" admin notice now links to the Biscotti CMP dashboard (the obsolete setup-wizard link was removed).
* Fix: A/B Testing — variant editor labels and the Position/Type dropdowns (and the default variant name) are now translated; they previously appeared in English when adding a variant.
* Fix: Connection screen — the "Invalid Website ID format" error message is now translated.
* Fix: Jurisdiction & Targeting — hardened the consent-model hint against a rare load-order JavaScript error.
* i18n: Repo-wide translation-quality pass across all 40 languages — corrected 1235 wrong-sense/wrong-word-form translations (e.g. the "Running" A/B status had been translated as the sport, "Trial" as a court trial, "Minor" as underage, "Position" as a job title) and decoded 575 HTML-entity artifacts (e.g. &quot;, &#x27;) back to real characters. All 40 locales remain 100% translated.

= 4.2.0 =
* Admin UI: Completed the native rebuild — every admin screen (Dashboard, Scanner/Tools, Subscription, Documents, Services & Providers, Legal Wizard, Compliance, Jurisdiction, Company Data, Terms (AGB) Questionnaire, A/B Testing, White Label) now renders locally in WordPress via the plugin's own PHP templates and local scripts. No remote-loaded admin markup or scripts.
* i18n: All user-facing admin strings are now fully translated into 40 languages (.po/.mo shipped); the admin interface follows the selected plugin language consistently.
* New: Multi-step Legal Wizard, native Provider Editor, A/B test creation with per-variant colour pickers, and a conditional Terms (AGB) questionnaire.
* Fix: Document generation no longer times out for longer AI generations (extended the admin request timeout for generation actions).
* Fix: Plugin language can now be set to region-specific locales (e.g. pt-BR, pt-PT, zh-TW).
* Fix: A/B test creation persists variant banner configuration correctly.

= 4.1.0 =
* Architecture: Admin screens are now rendered **natively** inside WordPress by the plugin's own PHP templates (banner designer, settings, compliance, documents, about). The plugin no longer embeds or loads remote admin UI — it exchanges only data (JSON) with the Biscotti Cloud API.
* Fix: Banner Designer no longer renders a blank page — the native editor form always renders with saved-or-default values.
* Fix: Removed all iframe-based admin embedding in favor of local PHP rendering, aligning with WordPress.org plugin guidelines.
* Compliance: External Services disclosure rewritten to accurately describe the native-rendering / data-only architecture; removed an unused secondary API domain reference.
* Internal: Added native local templates for the remaining admin screens.

= 4.0.1 =
* Critical: Pre-Boot Shield now ships with the plugin and runs as the very first <script> in <head>
* Fix: Inline tracking pixels (Meta Pixel, Google Analytics, LinkedIn Insight, etc.) are now blocked BEFORE the network request fires, not just after the DOM mutation
* Fix: _fbp, _ga, _uet*, _clck and other tracker cookies no longer set pre-consent
* Improved: Site Kit blocking patterns extended (googlesitekit-consent-mode, googlesitekit-events-provider, googlesitekit-modules)
* Internal: Boot blob auto-generated from biscotti-shield.js + cmp-stub.js by build pipeline (no manual sync)

= 4.0.0 =
* Architecture: Admin pages now use sandboxed iframe (like Jetpack/Mailchimp) instead of injecting remote JS/CSS
* Fix: All CSS output uses wp_add_inline_style() — no more direct echo
* Fix: Google Site Kit restore script uses wp_add_inline_script() — no more raw <script> tags
* Fix: White-label customCss sanitized with wp_strip_all_tags() before output
* Fix: TranslatePress $TRP_LANGUAGE global access documented with clarifying comment
* Added: Public source code repository linked in readme (https://github.com/dbosch-a11y/biscotti-cmp-client)
* Added: Source Code section in readme.txt per WordPress.org Guideline 4
* Compliance: All escaping issues resolved per WordPress.org review feedback
* Compliance: No more externally-loaded executable code in admin context

For the complete version history, see changelog.txt (shipped with the plugin) or the source repository.

== Upgrade Notice ==

= 4.8.5 =
Security and release-integrity update: the rebuilt pre-boot shield keeps consent-enabled third parties blocked until a one-time dashboard authentication handoff has been removed from the address bar and copied into its first-party exchange request.

= 4.8.4 =
Security and reliability update: reconnect the plugin with the per-site Connection Token if WordPress asks you to. Existing settings remain readable, but writes intentionally stay blocked until pairing is complete.

= 4.4.0 =
Important fix: trackers hardcoded into your page HTML were contacted before consent; they are now blocked on the server and released only after consent. Also fixes Microsoft Advertising (UET) tags that never initialised. Enabled the Accessibility Widget but see no button? That needs 4.3.1 or later.

= 4.3.1 =
Adds the Accessibility Widget (a separate ♿ button for contrast, text size, reading aids and read-aloud; requires a Growth plan or higher) and fixes the legal document shortcodes, which previously rendered nothing because they called an authenticated endpoint. Recommended for all users.

= 4.2.2 =
Critical fix: analytics and marketing tags now fire correctly after consent. A pre-blocked tracker could previously stay disabled even after the visitor clicked Accept, so no data was recorded. Recommended for all users.

= 4.2.1 =
Translation-quality release: corrects 1200+ mistranslated UI labels across all 40 languages, renames White Label to Branding, removes the redundant Setup page, adds the TCF 2.3 toggle to the Banner Designer, and fixes several admin links and labels. Recommended for all users.

= 3.4.0 =
Compliance: Complete readme.txt rewrite with full External Services disclosure section covering Cloud API, AI usage, GeoIP services, TCF, cookies, data processing, and third-party script detection. All legal links bilingual (DE/EN). Recommended for all users.

= 3.3.2 =
Fix: TCF 2.3 compliance improvements — correct vendor counting, Purpose 11 in all 39 languages, tcfEnabled defaults to true. Recommended for all users.

= 3.2.2 =
Fix: Critical syntax errors in i18n file corrected. Full 40-locale dashboard translation coverage. 108-jurisdiction settings panel. Recommended for all users.

= 3.2.1 =
Fix: TCF vendor list now visible by default. Missing admin i18n keys added. Strongly recommended for all users.

= 3.2.0 =
Critical: Fixes broken dropdowns, missing 39-language selector, and TCF 2.3 compliance. All admin views now render correctly. Strongly recommended for all v3.0+ users.

= 3.1.0 =
New: Full page builder compatibility (Gutenberg, Divi, Beaver Builder, WPBakery, Oxygen), translation plugin support (WPML, Polylang, TranslatePress, Weglot, MultilingualPress, Loco Translate), WooCommerce integration, and API-first document sourcing with local archiving.

= 3.0.0 =
Major architecture rewrite! The plugin is now a lightweight cloud-bridge shell (238 lines, down from 2200). All admin pages are dynamically loaded from the Biscotti Cloud API. 39 languages supported. Please ensure your Website ID is configured after upgrading.

= 1.3.0 =
This update includes improved GDPR compliance with automatic storage duration detection and full vendor information display. Recommended for all users.
