== Changelog (full history) ==

= 4.8.5 =
* Security: The bundled pre-boot shield 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. (The matching server-side fix, which rejected such a save with "Argument `name` is missing", is already live and needs no plugin update.)
* 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

= 3.9.3 =
* Fix: Google Site Kit consent-mode scripts now properly blocked until user consent (PHP-level dequeue + client-side fallback)
* Fix: Added Site Kit pattern matching to client-side blocking engine
* Improved: Scripts from google-site-kit plugin directory are automatically detected and blocked

= 3.9.2 =
* Fix: White Label branding save button now functional — JS event handler wired up, proxy action registered
* Fix: A/B Testing now supports up to 5 variants with per-variant design properties (color, border radius, position, type)
* Fix: A/B Testing "Start" button added for DRAFT tests
* Fix: Admin notices from other plugins no longer appear on Biscotti pages
* Fix: Proxy action allowlist extended with save-branding, start-ab-test, update-ab-test
* Fix: White Label config fetched from API and forwarded to BiscottiConfig for frontend branding
* Fix: A/B test variant assignment via cookie for consistent visitor experience
* Fix: WordPress Plugin Check compliance — mt_rand() replaced with wp_rand()

= 3.9.1 =
* Fix: CRITICAL — Cache invalidation race condition fixed. Saving banner colors/settings via WP plugin now correctly invalidates the Redis cache.
* Fix: CacheService now awaits Redis connection before performing operations.
* Fix: wp-plugin.js and auth.js now use module-level CacheService singleton.

= 3.9.0 =
* Fix: CRITICAL — All API calls now routed through WordPress AJAX (admin-ajax.php) instead of direct browser-to-API fetch.
* Fix: White Label branding colors now correctly override banner theme colors.
* Fix: Consent log deduplication — same visitor can no longer create duplicate entries within 5 seconds.
* Fix: A/B Testing now tracks impressions correctly.
* New: A/B Testing view with Create, Stop, Delete buttons.
* Improvement: Better error messages when API calls fail.

= 3.8.1 =
* Fix: CRITICAL — Admin pages failed to render because inline JS was passed through wp_strip_all_tags().
* Fix: Documents modal no longer opens automatically on page load.
* Fix: Removed empty Integrations admin page.

= 3.8.0 =
* Fix: [biscotti-document] shortcode registered and renders correctly
* Fix: Admin JS receives dashboardUrl and complete i18n bag
* Fix: BiscottiConfig forwards tcfEnabled, googleConsentMode, autoBlock, consentDuration, position
* Fix: Eliminated duplicate floating button
* Fix: Save handler persists all settings fields
* Fix: Admin CSS scoped to Biscotti pages only
* Fix: wp_kses preserves data-biscotti-* attributes
* Fix: GeoIP failure falls back to default region
* Fix: State mappings normalized between PHP and client
* Fix: Elementor/WooCommerce integration load-order fixed
* Fix: Removed dead assets/js/consent.js
* Fix: MultiRegion banner fatal error fixed — replaced non-existent get_option() method call
* Fix: Settings link shortcode now uses Language Bridge instead of raw get_locale()
* Fix: Transient cache cleanup now uses delete_transient() API instead of direct database queries
* Fix: 76 cross-language translation contamination fixes in banner.ok, banner.doNotSell, banner.managePrivacy
* Fix: tcfEnabled hardcoded to true in BiscottiConfig bridge — old WP plugins can no longer disable TCF
* Compliance: BUILD.md added with full JavaScript build instructions and source file documentation
* Compliance: Directory listing protection — index.php silence files added to all plugin directories
* Compliance: Contributors field corrected to WordPress.org username format
* Compliance: Author header cleaned (removed developer name)
* Compliance: Uninstall cleanup expanded — biscotti_settings, biscotti_legal_documents, biscotti_legal_archive now deleted
* Removed: Unused biscotti-banner-v2.js and biscotti.bundle.min.js files
* Version bump to 3.5.0

= 3.4.0 =
* Compliance: Complete readme.txt rewrite with full External Services disclosure section
* Compliance: AI usage disclosure — Google Gemini for Legal Suite document generation
* Compliance: GeoIP service disclosure — ipapi.co, ipinfo.io, geoplugin.net with privacy policy links
* Compliance: Cookie & Local Storage disclosure — biscotti_consent cookie and localStorage
* Compliance: Data Processing (GDPR Art. 28) disclosure with full Campcruisers GmbH company information
* Compliance: IAB TCF 2.2/2.3 vendor list disclosure
* Compliance: Third-party script detection disclosure (blocking rules and CSP generation)
* Compliance: Biscotti Cloud API Cloud-Bridge architecture fully documented with all domains and data flows
* Compliance: All legal links now bilingual (Deutsch | English) — ToS, Privacy Policy, DPA/AVV, Imprint
* Compliance: Legal disclaimer for AI-generated documents recommending review by qualified legal professional
* Compliance: GeoIP pre-consent processing documented with legitimate interest legal basis (GDPR Art. 6(1)(f))
* Version bump to 3.4.0

= 3.3.2 =
* Fix: tcfEnabled now always defaults to true (server is Single Point of Truth, overrides local WP option)
* Fix: Vendor counts now include ALL services including essentials (WordPress, Cloudflare, Elementor) — only Biscotti CMP excluded
* Fix: All services (IAB and non-IAB) now appear in category tabs with IAB TCF badge for IAB-certified vendors
* Fix: Purpose 11 (Use limited data to select content) added in all 39 languages with official IAB translations
* Fix: Data Processing page text changed from misleading "Click below to begin" to "Manage them in the Biscotti Dashboard"
* Improved: TCF 2.3 compliance — IAB TCF badge on services, correct vendor counting, Purpose 11

= 3.3.1 =
* Fix: Compliance page no longer returns "Page not found" (added to server-side PAGE_REGISTRY and view router)
* Fix: Legal Suite page no longer returns "Page not found" (added to server-side PAGE_REGISTRY and view router)
* Fix: Third-party admin notices (MC4WP, WooCommerce, etc.) suppressed on all Biscotti CMP pages
* New: Documents page added to plugin menu (matches Dashboard app navigation)
* Removed: AGB Questionnaire as separate menu item (integrated into Legal Suite, matching Dashboard app)
* Improved: Plugin menu structure aligned with Dashboard app navigation

= 3.3.0 =
* New: TCF 2.3 enable/disable toggle in Settings → Consent tab (saves to server, server-side override for old plugin versions)
* New: 249 global jurisdictions loaded live from database (replaces static 170-entry list)
* New: Region selector grouped by 8 regions (Europe, North America, Latin America, Asia-Pacific, Africa, Middle East, Oceania, Eurasia) with consent model badges
* New: Custom service blocking — blockingPatterns, cookiePatterns, and embedCode fields now persisted in database
* New: Session Extension Pack prominently visible in Billing → Subscription tab
* New: Multi-Jurisdiction Compliance Matrix with interactive comparison table in dashboard
* New: Jurisdiction-specific legal requirements from Knowledge Base (186 countries) in Compliance page
* New: Playwright headless browser for Policy Audit SPA scraping (React, Vue, Next.js sites)
* New: 80+ cookie name patterns for accurate first-party cookie categorization in scanner
* New: 36 additional Knowledge Base country matrices (186 total, 11 sectors each)
* New: Knowledge Base translations expanded from 23 to 39 languages (~3118 items each)
* Improved: Google service separation — Analytics, Ads, YouTube, GTM properly categorized
* Improved: Cloudflare categorized as Essential, Microsoft Clarity as Analytics
* Improved: Public scanner jurisdiction-aware via TLD detection with pre-resolved config
* Improved: Scanner score calculation penalizes ALL non-essential pre-consent cookies
* Improved: Compliance page shows real privacy_link/cookie_policy checks (not always-pass)
* Improved: Custom file upload buttons in Banner Designer (no browser-native text)
* Improved: SEO fixes for marketing site (zh-TW regex, slug fallback, knowledge articles)
* Improved: JURISDICTION_LEGAL_RULES loaded from server config (89 jurisdictions)
* Fix: Removed dead render_page_privacy_wizard() method
* Fix: CookieChecker polling timeout increased from 60s to 120s
* Fix: Session Extension Pack i18n translations for all languages

= 3.2.5 =
* New: Implemented `_retryFailedSyncs` in the engine for persistent consent collection on network failure
* Improved: Client engine initialization synchronized with floating button icons
* Internal: Updated all script copies to latest 1:1 build parity

= 3.2.4 =
* Fix: Dashboard rendering stability improvements for Websites view

= 3.2.3 =
* Compliance: EU AI Act description rewritten with Art. 50 Regulation (EU) 2024/1689 reference
* Compliance: Added Legal Disclaimer section (AI-generated documents, no liability, recommend Fachanwalt)
* Compliance: Added Cookie & Local Storage disclosure (biscotti_consent)
* Compliance: Added full Company Information section (Campcruisers GmbH)
* Compliance: All legal links now bilingual (Deutsch | English)
* New: 12 additional privacy regions added to class-regions.php (KR, IN, TH, SG, CN, ID, TR, AE, IL, EG, KE, NG)
* Improvement: Cloud-Bridge architecture transparently documented in readme.txt
* Improvement: Third-party service detection reference updated to biscotti-banner-v2.js

= 3.2.2 =
* Fix: 9 unterminated string literal syntax errors in `wp-views-i18n.js` corrected (trailing `,'` removed from ar, he, ja, ko, zh, th, vi, hi, ru locale entries)
* New: Full `dash.*` i18n key coverage for all 40 supported locales (dashboard UI now fully translatable)
* New: Dashboard admin page refactored to use `getPageText()` i18n system — no more hardcoded English strings
* New: Settings panel now renders all 108 jurisdictions dynamically from the authoritative `JURISDICTIONS` matrix (replaces hardcoded 13-region list)
* Improved: WordPress admin dark-mode contrast fix — header `h2` color enforced via `!important` in shared CSS
* Improved: Tested up to WordPress 6.9

= 3.2.1 =
* Fix: TCF vendor list now visible by default — tcfEnabled changed from false to true
* Fix: 8 missing i18n keys added (common.edit, common.cancel, settings.disconnect, etc.)
* Improved: All 39 languages verified complete in admin view translation registry

= 3.2.0 =
* Fix: All admin view <select> dropdowns now render correctly (wp_kses allowlist fix for select/option tags)
* Fix: 39-language selector added to Settings page — plugin UI fully translatable
* Fix: Critical rendering and save failures resolved across all dynamic views
* Fix: JS globals, settings i18n, banner save, and save-settings proxy corrected
* Fix: Banner i18n fallback for legacy texts, config translations
* Fix: Plugin compliance test errors resolved
* New: TCF 2.3 compliance — isServiceSpecific, vendorsDisclosed, deletedDate, withdrawal hint, storage disclosure
* New: Agency subaccount logic and WP Legal Suite integration with jurisdiction questions
* New: About page title, scanner labels, version bump, docs link
* Improved: Phase 5-6 full codebase remediation (audit cleanup)
* Improved: All server-side views updated with correct form elements and i18n

= 3.1.0 =
* New: Universal Language Bridge — automatic language detection for WPML, Polylang, TranslatePress, Weglot, MultilingualPress, and Loco Translate
* New: 8 native Gutenberg blocks with server-side rendering and inspector controls (also works with Spectra and Kadence Blocks)
* New: 8 Divi Builder modules with visual builder support
* New: 8 Beaver Builder modules with settings panels
* New: 8 WPBakery (Visual Composer) elements with visual controls
* New: 8 Oxygen Builder elements with option controls
* New: WooCommerce compatibility — automatic cookie categorization, consent-aware tracking, GA deferral
* New: API-first document sourcing — the Biscotti App is now the single source of truth for legal documents
* New: Local document archiving — old versions are archived when updated from the app (max 10 per type)
* New: Draft documents show a visual notice instead of blank content
* Improved: Conditional module loading — builder integrations only load when the respective builder is active
* Improved: Banner widget now receives the detected language from Language Bridge
* Improved: All hardcoded `get_locale()` calls replaced with Language Bridge detection

= 3.0.0 =
* **Major Architecture Rewrite: Cloud-Bridge Pattern**
* New: Plugin reduced from 2200 lines to 238 lines — now a thin shell that dynamically loads admin UI from the Biscotti Cloud API
* New: `class-api-client.php` — standardized API communication with transient caching
* New: `class-admin.php` — dynamic view loader with plan-based feature gating
* New: `class-banner.php` — extracted frontend banner injection
* New: Server-side `wp-plugin.js` API endpoints for view serving, features, and translations
* New: 39-language localization (up from 9) with `.pot` master template
* New: Connection wizard with Website ID validation and API heartbeat verification
* New: Plan upgrade prompts for premium features (served via HTTP 403 from API)
* Removed: 12 embedded PHP admin views (300KB) — now served dynamically from API
* Removed: 17 page-specific JS files (700KB+) — replaced by 2 shell scripts (251 lines)
* Security: All outputs escaped via `esc_html()`, `esc_url()`, `esc_attr()`, `wp_kses_post()`
* Security: All AJAX handlers use `check_ajax_referer()` + `current_user_can('manage_options')`
* Security: No inline `<script>` or `<style>` tags — all via `wp_enqueue_*` and `wp_add_inline_*`
* Compliance: 100% open source — no embedded proprietary code

= 2.9.5 =
* Fix: Banner script now loads biscotti.min.js from API server instead of legacy widget.js placeholder — resolves banner not rendering correctly on WordPress sites
* Fix: Cache invalidation added to dashboard banner create/update/delete and white-label config save
* Fix: All banner theme properties now applied: borderRadius, position, buttonStyle, iconStyle, floatingButtonIcon
* Fix: White-label primaryColor/accentColor override applied in banner rendering
* Fix: Brand name shown standalone when hidePoweredBy is enabled
* Fix: White-label customCss (including hideFooter) now injected into banner
* Fix: BISCOTTI_VERSION constant synchronized with plugin header version

= 2.9.4 =
* Fix: Banner name (e.g. "Mein Banner") is now synced from the app and displayed as read-only field in the WordPress banner settings
* Improved: Sync from App now correctly maps the internal banner name alongside localized texts and theme settings

= 2.9.3 =
* New: Banner sync from app — "Sync from App" button on banner settings page pulls all banner config (texts, colors, CSS, position, layout, consent type) from the Biscotti Dashboard
* New: Automatic banner sync on first plugin connection — banner settings are pulled immediately when API key is saved
* New: Portuguese (pt) and Danish (da) banner texts added — all 9 supported languages now available in banner configuration
* Improved: Full localized text mapping — sync correctly maps localized texts to individual flat fields (title, text, accept, reject, settings, save)
* Improved: Theme sync now includes layout, custom CSS, floating button, and ethical UI mode settings

= 2.9.2 =
* Fix: Legal Suite document generation no longer fails with "Legal entity not configured" error
* Fix: Legal entity is automatically synced to server before each document generation request
* Fix: Server-side accountId resolution from websiteId for WordPress plugin installations without stored account ID

= 2.9.1 =
* Fix: Resolved CORS errors on Compliance, Dashboard, Privacy Wizard, and Integrations pages by adding 12 server-side AJAX proxy endpoints
* Fix: Data Processing, A/B Testing, Audit Log, and Webhooks pages now correctly unwrap nested API response data
* Fix: Banner hex color input no longer produces double-hash values (##D4A574)
* Fix: Reset Defaults button now saves actual default settings via AJAX instead of just reloading the page
* Improved: All external API calls routed through WordPress server-side proxies for reliability

= 2.9.0 =
* New: Smart Interview step added to Legal Suite wizard (Step 9) — 55+ fields for comprehensive legal document generation
* New: 7 collapsible sections: Dispute Resolution, Consumer Rights, Subscriptions, DSA Content Moderation, Data Handling, Industry Specific, Promotions & Final Provisions
* New: Collapsible details UI with smooth expand/collapse animations
* Improved: Wizard now has 10 steps matching full dashboard LegalWizard parity
* Improved: Dynamic field population automatically supports all new Smart Interview fields

= 2.8.2 =
* Fix: Legal document generation now sends websiteId and accountId from JavaScript to bypass server-side option caching issues
* Fix: PHP generate handler uses 3-tier ID resolution (POST params → options → fresh DB read) for reliable operation

= 2.8.1 =
* Fix: Email notification sync now uses configurable BISCOTTI_API_URL constant instead of hardcoded production URL

= 2.8.0 =
* New: Webhooks – Send real-time event notifications (consent, GDPR, billing) to external services via HTTP POST
* New: Audit Log – Track all account changes and actions with filtering and pagination
* Improved: Session limits updated – Starter 15,000 / Growth 60,000 / Agency 2,000,000
* Improved: Agency plan limited to 25 sub-accounts

= 2.7.0 =
* New: Data Processing (GDPR Art. 30) – Records of Processing Activities management
* New: White Label – Per-website branding, custom colors, logo, and CSS
* New: A/B Testing – Test banner variants with traffic splitting and conversion tracking
* New: 3 dedicated admin pages with full AJAX support
* New: 10 Cloud API-backed AJAX handlers for CRUD operations
* Improved: All new strings fully localized across 9 languages

= 2.6.0 =
* New: Smart banner language cascade — banner automatically displays in the page language (html lang attribute), with browser language fallback
* Fix: GeoIP language fallback no longer forces German — uses null fallback so client-side cascade detects the correct language
* Fix: Login page legal links (Documentation, Imprint) are now language-aware, pointing to the correct translated marketing page
* Fix: Registration page legal links (Terms, Privacy Policy) are now language-aware across all 9 supported languages
* Improved: Elementor widget 'auto' language mode works seamlessly with the new smart cascade

= 2.5.9 =
* New: Per-language privacy policy and imprint URLs in consent banner
* Banner designer now allows setting different legal page URLs for each language tab
* Config API returns language-specific legal links based on visitor's detected language
* Client scripts (banner-v2 and consent.js) check per-language URLs before falling back to website-level URLs

= 2.5.8 =
* Fix: Banner duplication bug — sync_banner_to_api no longer creates a new banner every time settings are saved when api_banner_id is missing
* Fix: Plugin now queries existing API banners before creating, preventing duplicate "Manage Consent" banners

= 2.5.7 =
* Fix: Sync from Dashboard now correctly sends websiteId as query parameter to API
* Fix: Document sync no longer requires accountId — works with websiteId alone

= 2.5.6 =
* New: Sync from Dashboard button — documents generated in the Biscotti Dashboard app can now be synced into WordPress with one click
* New: Complete Legal Suite UI overhaul with professional styling matching the rest of the plugin
* New: 2-column form grid layout for all wizard steps
* New: Styled wizard step indicators with numbered circles and active/completed states
* New: Document cards with status badges, hover effects, and responsive grid layout
* New: Info boxes, checkbox groups, and representative rows with polished styling
* New: Full responsive design for Legal Suite (breakpoints at 1100px, 900px, 600px)
* Improved: Navigation buttons with branded styling instead of plain WordPress buttons

= 2.5.5 =
* Feature: Bidirectional banner sync between WordPress Plugin and Dashboard App
* Banner settings are now pulled from the Dashboard API when the Banner admin page loads
* Banner changes saved in the plugin are automatically pushed back to the Dashboard API
* Graceful fallback to local settings if the API is unavailable
* Fix: Banner color sync now correctly selects the most recently updated active banner instead of the first one
* Fix: Umlauts and special characters (ä, ö, ü, etc.) are now preserved correctly in synced banner texts

= 2.5.4 =
* Fix: Plugin language switching now works — admin UI respects the language selected in Settings → Languages & Region instead of always using the WordPress site locale
* Fix: Textdomain loading overhauled — unload_textdomain() + load_textdomain() explicitly loads the correct .mo file for the selected language
* Fix: Website deletion in dashboard — replaced flashing window.confirm() with a stable custom React confirmation modal
* Fix: Stripe webhook delivery failures — corrected rawBody access path (request.raw.rawBody) in both billing and subscription webhook handlers

= 2.5.3 =
* Fix: Plugin translations now load correctly — missing .mo files generated, incorrect German msgids corrected
* Fix: German translation file (de_DE.po) completely regenerated with 392 correct msgid/msgstr pairs matching all PHP source strings
* Fix: Compiled .mo translation files generated for all 9 supported languages (DE, EN, ES, FR, IT, NL, PL, PT, DA)
* Fix: Duplicate entries removed from Danish (da_DK) and Portuguese (pt_PT) translation files
* Fix: BISCOTTI_VERSION constant now synchronized with plugin header version
* Improved: Added .pot template file for future translation contributions

= 2.5.2 =
* Fix: TikTok and other services no longer falsely detected from JSON-LD schema metadata (sameAs social profile URLs)
* Fix: Non-executable script types (application/ld+json, text/template) excluded from inline content scanning to prevent false positive service detection
* Fix: Banner attribution ("Powered by Biscotti") now correctly respects account plan — hidden for paid plans (Starter, Growth, Business, Agency, Lifetime)
* Fix: API config now properly stores plan, branding, and white-label settings for accurate banner rendering
* Fix: Scanner-detected services (Elementor, TranslatePress, etc.) correctly displayed in banner categories

= 2.5.1 =
* Fix: "Powered by Biscotti" attribution is now fully opt-in (off by default) per WordPress.org plugin guidelines — requires explicit admin checkbox to enable
* Fix: Added dedicated attribution toggle in Banner Settings → Behavior tab with clear description
* Fix: Late escaping applied to shortcode output — __() replaced with esc_html__() for legal document shortcodes
* Fix: Banner JavaScript (biscotti-banner-v2.js) now respects server-side showPoweredBy setting instead of defaulting to true
* Fix: New show_attribution option stored in plugin settings (default: false), passed to frontend via wp_localize_script

= 2.5.0 =
* New: Complete redesign of the Subscription admin page with premium UI
* New: Dark gradient header, 2-column card grid (Plan + Usage), animated usage bar
* New: 4-column quick action row with hover effects (Upgrade, Downgrade, Payment, Cancel)
* New: 5-column plan pricing tiles with Popular/Enterprise badges and hover-lift
* New: Styled invoice table replacing legacy WordPress widefat table
* New: Responsive grid layout (5→3→2→1 columns) for all screen sizes
* Updated: Default banner description text with new 2-paragraph GDPR-compliant format
* Updated: Banner texts for all 7 languages (DE, EN, FR, ES, IT, NL, PL) in banner.php, consent.js, and class-multiregion-banner.php
* Fix: JS selectors in admin-subscription.js aligned with redesigned HTML class names

= 2.4.2 =
* Fix: Version header and BISCOTTI_VERSION constant synchronization
* Fix: Removed unused useApiCall hook from dashboard codebase

= 2.4.1 =
* Fix: Synced biscotti.min.js with latest source file
* Fix: All hardcoded German navigation strings wrapped in WordPress i18n functions
* Fix: Wizard step titles (Besucher, Einwilligung, Dokumente, Fertigstellen, Konto) now use __() for translation
* Fix: Sidebar titles in Dashboard, Wizard, and Tools pages now use esc_html_e()
* Fix: Tab navigation across all 6 admin views now fully internationalized

= 2.4.0 =
* New: Full i18n support for Legal Suite — 122 hardcoded German strings replaced with translated text
* New: Native translations for all Legal Suite option values in 9 languages (DE, EN, FR, ES, IT, NL, PL, PT, DA)
* New: Marketplace deep-dive, contract, payment, delivery, liability, SaaS, withdrawal, dispute resolution, consumer rights, and subscription sections fully internationalized

= 2.3.1 =
* Fix: Legal Suite JS selectors corrected — payment_acquirer, regulated_profession_name, chamber_name, chamber_regulations now read from correct HTML elements
* Fix: PHP handler now saves all 15 additional fields (fax, taxNumber, economicId, customerService, DPO phone/address, regulated profession, payment integration)
* Fix: Added toggle handlers for payment integration and regulated profession conditional fields
* Fix: hasOwnPaymentIntegration and isRegulatedProfession checkboxes now included in form data

= 2.3.0 =
* New: Identity step – Fax, Tax Number, Economic ID fields
* New: Identity step – Customer Service Email, Phone, Business Hours
* New: Identity step – Regulated Profession details (name, chamber, regulations URL)
* New: Identity step – Data Protection Officer (DPO) fields moved here from Compliance step
* New: Contract step – Payment processing fields (Gateway, Acquirer)
* New: Contract step – "Auf Empfehlung des Anwalts" option for liability cap
* Improved: Compliance step simplified, DPO fields consolidated into Identity step
* Improved: Legal Suite form parity with dashboard LegalWizard

= 2.2.1 =
* Fix: API URL consistency audit — all embed codes and documentation now include /api/v1 path
* Fix: Client script _trackSession() normalized to match _syncConsentToBackend() URL construction
* Fix: BISCOTTI_VERSION constant now matches plugin header version
* Synced: Client script (biscotti.js) with latest source

= 2.2.0 =
* New: Full i18n localization of all admin JavaScript files (admin.js, admin-wizard.js, admin-subscription.js, admin-legal-suite.js)
* New: uninstall.php for proper plugin data cleanup on uninstallation
* New: wp_localize_script calls for subscription and legal suite pages
* Fix: Double API path bug (BG-001) in legal entity sync endpoint
* Improved: All hardcoded German UI strings replaced with translatable __() calls
* Improved: JavaScript fallback defaults use English instead of German

= 2.1.0 =
* Fix: Document generation from Legal Suite now works correctly (parameter name mismatch)
* Fix: Legal entity API sync uses consistent base URL pattern
* Fix: Client script (biscotti.min.js) synced with latest source
* Improved: All API endpoint URLs use BISCOTTI_API_URL constant consistently

= 2.0.0 =
* New: Full Legal Suite integration with 4-step wizard
* New: Legal entity data management (company, contact, DPO)
* New: AI-powered document generation (Impressum, Privacy Policy, Cookie Policy)
* New: Complete i18n support for Legal Suite (9 languages)
* Fix: Authentication header (X-Account-ID) for Legal Suite API calls
* Fix: JavaScript biscottiAdmin fallback for robustness
* Fix: Form field consistency (dpoDesignated)
* Improved: Save UX with loading states and inline notifications
* Improved: Replaced alerts with consistent showNotice() calls

= 1.8.0 =
* New: Cookie Scan directly from WordPress admin (Werkzeuge > Cookie-Scan)
* New: US State Privacy Laws support (CCPA, CPRA, VCDPA, CPA, CTDPA)
* New: AI Consistency Module for banner text optimization
* New: Scan Automation with scheduled scans
* New: Debug Mode for developers
* Fix: Scan trigger endpoint now properly connected to backend
* Fix: api_key validation relaxed (only website_id required)
* Improved: Better error handling for scan failures
* Improved: Fallback to cached scan results when live scan unavailable

= 1.7.0 =
* New: Legal Suite shortcodes for Impressum, Privacy Policy, and Cookie Policy
* New: [biscotti_impressum], [biscotti_privacy_policy], [biscotti_cookie_policy] shortcodes
* New: API-based document fetching with 1-hour WordPress transient caching
* Fix: Admin scripts now load correctly on all Biscotti plugin pages
* Fix: Tools page links no longer redirect to external dashboard login
* Improved: A/B-Tests marked as premium feature (Growth+)
* Improved: Translations link now points to local Banner settings

= 1.6.1 =
* Fix: Granular options checkbox now saves correctly
* Fix: Boolean values properly converted from string to true/false

= 1.5.0 =
* New: Zustimmungs-ID (Consent ID) display in banner footer for GDPR audit compliance
* New: Viewport-aware scrollable preferences panel for mobile devices
* New: Single-Source Synchronization between Core and WordPress scripts
* Improved: Banner UI refinements for DACH region parity with premium solutions

= 1.3.0 =
* New: Automatic storageDuration detection from scanned cookies
* New: Full GDPR fields display (vendor, location, legal basis, privacy policy)
* New: Agency and Lifetime plan support
* New: Internal account status for non-billable accounts
* Fix: Server path resolution for Docker environments
* Fix: Client script GDPR field mapping

= 1.2.0 =
* New: EU AI Act compliance with KI-Dienste category
* New: Branding footer with plan-based visibility
* New: Floating cookie button for settings access
* Improved: Toggle and button visibility on light backgrounds
* Improved: Details panel contrast and readability

= 1.1.0 =
* New: Granular consent options per service
* New: GPC (Global Privacy Control) detection
* New: Multi-region banner support
* New: Geo-IP based compliance rules
* Improved: Scanner accuracy for third-party scripts

= 1.0.0 =
* Initial release
* GDPR-compliant consent banner
* Google Consent Mode v2 integration
* AI-powered cookie scanner
* Multi-language support (8 languages)
* Setup wizard
* Customizable design
