== Changelog (older versions) ==

Entries up to 2.1.1. Newer versions are listed in readme.txt.

= 2.1.1 =
* Fixed: a period now covers exactly as many days as it says. The totals below the chart reached one day further back than the chart itself, so "30 days" added up 31 days and "7 days" added up 8. Impressions, clicks and CTR now always cover today plus the days before it, and the table finally matches the curve above it. Because of that, all reported numbers drop slightly once, by roughly one day's worth: around 3 % over 30 days, around an eighth over 7 days. No data was lost, the oldest day simply no longer belongs to the period. Give it five minutes after the update, the statistics are cached that long.
* Fixed: deleting an ad block no longer hands its number to the next one. A newly created or duplicated block could be given the number of a deleted block, and it started out with that block's impressions and clicks. Worse, every `[adjet_ad id="7"]` still sitting in an old post or a widget quietly began showing the new ad instead of showing nothing. Numbers are now handed out once and only ever count upwards. Existing blocks keep their numbers, existing shortcodes keep pointing where they always did.
* Fixed: importing ad blocks with "Replace existing ad blocks" no longer restarts the numbering at 1, so imported blocks no longer take over the statistics and the shortcodes of the blocks they replaced. Reading your own export back into the same site stays lossless: replacing keeps the numbers stored in the file, so the shortcodes in your content keep working. Blocks imported on top of your existing ones get fresh numbers, as they always did.
* Fixed: "Top block" in the weekly summary could be won by an ad block that has been deleted. The line then vanished completely, because the block list no longer holds that number, and the strongest block you still have was never named. Only blocks that still exist take part now, and the weekly summary is rebuilt the moment you add, rename, delete or import a block instead of waiting up to 15 minutes for its cache to expire.
* Fixed: the week card no longer shows a green upwards arrow for a decline. A drop of 0.04 % rounds to 0.0 % and got the arrow anyway. Unchanged is now a state of its own: an arrow to the side, in the muted colour of the card.
* Fixed: a click-through rate is no longer printed as 0.00 % when clicks were counted without a single impression, which happens when a blocker or a cache swallows the impression. Those cells now read "n/a", because an unknown rate is not a rate of zero. No clicks on no impressions stays 0.00 %.
* Fixed: the AI statistics summary was handed the click-through rate with a decimal point no matter what language your site speaks, and quoted it back that way in an otherwise German answer. Numbers now go into the briefing formatted for the site language.
* Importing ad blocks with "Append" now books all block numbers in a single write instead of one write per block. The numbers themselves, and the order they are handed out in, stay exactly the same.
* Ready for WordPress 7.1: the editor block now registers with block API version 3. WordPress 7.1 always runs the post editor inside an iframe and warned in the browser console that a block on version 2 could force the editor back into its old mode. The block's placeholder also gets its own editor stylesheet, because the admin stylesheet never reached the editor canvas and the placeholder was drawn there without any styling at all.
* The plugin now carries the same name in the plugin list as in the WordPress.org directory. The two had drifted apart, which the directory flags on every single update.
* The recorded impressions and clicks of a deleted block stay where they are, on purpose. They still count towards this week's totals and the milestones, because that traffic did happen and there is no way to bring it back once it is gone. Nothing is deleted unless you switch on "Delete all data on uninstall".

= 2.1.0 =
* Ad rotation is now cache-proof and works in automatic placements: blocks sharing a group name render into one rotation container and the browser picks the weighted winner before any impression is counted. Previously rotation only existed in the `[adjet_ad_group]` shortcode, froze behind page caches, and two grouped blocks with the same automatic placement appeared side by side instead of rotating.
* Without JavaScript the first variant of a group is shown, so rotation groups fail open.
* Impressions are now counted when an ad actually becomes visible (at least half of it inside the viewport) instead of as soon as the page loads, and ads hidden via CSS are never counted. Expect lower impression numbers and a correspondingly higher CTR than in 2.0.0 - that is intentional and makes rotation groups measure copy quality instead of layout position.
* AI suggestions now write the ad copy in your site language instead of defaulting to English.
* Overlays and popups now respect content targeting on single posts and pages: "show only in categories/tags", excluded terms and excluded posts apply there, and a filled "show only in" list keeps the overlay off home, archive and search pages. Empty targeting fields keep the overlay site-wide, exactly as before.
* Two new AdSense formats in the wizard and the AdSense template: In-article (fluid) and Multiplex (related content grid) - the native formats blogs use inside their content.
* New free template "Text link / paragraph ad": a sponsored short paragraph in your content's look with labelling and a `rel="sponsored"` text link.
* New free template "Coupon box": an inline deal box whose coupon code visitors copy with one click, plus a button to the offer.
* New Pro template in the gallery: review box with star rating and pros/cons list.
* Kit (formerly ConvertKit) joins as the fifth newsletter provider: connect your Kit v4 API key and the "Newsletter (Kit)" template subscribes visitors to the Kit form of your choice; Kit's own double opt-in applies.
* Claude Opus 5 is the new default AI model; Opus 4.8, Sonnet 5 and Haiku 4.5 stay available and stored model choices keep working. Answers that hit the token limit now show a clear "cut off, please retry" message instead of a JSON error.
* New "Test connection" button next to the stored Claude key, a status badge on the AI settings section, relative cost hints per model and a monthly usage counter (calls and tokens) with a link to your Anthropic console.
* Without a stored key, the assistant now shows a static example of how the AI would fill the fields - clearly labelled as an example, no API call involved. A one-time dismissible card points to the feature.
* New AI alt-text suggestion for the image banner: one click sends the ad image to Anthropic via your own key and returns an editable image description.
* New AI statistics summary on the statistics page: aggregated totals of the last 30 days go to Anthropic via your own key and come back as a short interpretation with one recommendation - including the context that 2.1.0 counts impressions on visibility, so lower numbers are expected.
* The settings and the readme now spell out per AI button what is transferred and what never is (no visitor data, no IP addresses, no e-mail addresses, no page content).
* Translated all new texts into German (informal and formal), Spanish, French, Italian, Dutch and Brazilian Portuguese. The formal German catalog also caught up on the strings it was missing from earlier releases.
* Choose your Cookie: the consent banner template can now show one of four bundled cookie images next to the text (flat, mascot, monoline, realistic) - picked from preview cards in the wizard. Decorative, lightweight WebP, and existing banners stay image-free until you pick a style. Five more styles plus a custom image option come with Pro.
* The cookie consent banner template moved up in the template gallery: it now sits right behind the core ad formats, so it lands in the wizard's first visible row. Pure catalog order, existing blocks are untouched.
* Fixed: overlays and popups ignore the lazy loading option. Lazy loading kept the markup in a `<template>` until the block's wrapper scrolled into view, but an overlay is fixed to the window edge while its wrapper sits in the content flow - so the cookie banner could appear late, at a random scroll position, or never at all on short pages. Overlays now render immediately; inline ads keep lazy loading unchanged.
* Fixed: overlay impressions are counted when the overlay itself becomes visible. The visibility check watched the block's wrapper, but an overlay's wrapper is an empty element left behind in the content flow - at the very end of the document for footer-rendered overlays - so overlay impressions were only booked once a visitor scrolled all the way down.
* The statistics page previews the Pro consent statistics as a locked row (consent rate of your cookie banner, daily aggregates per block, no cookies and no visitor data). The upgrade page now also mentions consent banner A/B tests measured by consent rate and geo-varied banners (e.g. strict for the EU, slim elsewhere).

= 2.0.0 =
* New free placement "Between posts": show an ad in blog, archive and search listings after every Nth post.
* New free templates: image banner, HTML/code tile, sticky bottom bar (anchor ad), countdown banner (hides itself after expiry), social follow bar and a sponsored-content disclosure note.
* New Pro templates in the gallery: slide-in box (default trigger 50% scroll depth), recommendation card in post style, product/affiliate box with mandatory labelling, Google Ad Manager slot, video ad, comparison box, ad slider and content locker light.
* New Pro targeting (in Hafenstudios Ads Pro): referrer targeting and weekday/hour time windows, both evaluated in the browser so they stay correct behind page caches.
* New targeting: "Show only in categories/tags" complements the existing exclusion list.
* Cache-proof statistics: tracking no longer requires an expiring nonce (protected by an event whitelist, block-ID checks and an IP rate limit instead), and impressions are bundled into one beacon per page.
* The AdSense loader now only loads on pages that actually serve an AdSense block - with consent gating, only after consent has been given. The frontend stylesheet is now inlined in the head (0.7 KB, no extra request).
* One shared guard for shortcode, widget and Gutenberg block: ended campaigns no longer keep running through the Gutenberg block.
* Honesty: removed the never-implemented template teasers "Spin-to-Win" and "Reader paywall", including their claims on the upgrade page.
* Correctness: UTF-8 word counting for the minimum word count, save errors now show as an error notice instead of "saved", and a new index on stat_date speeds up the statistics.
* Hardening: the Cloudflare IP header only counts for rate limiting when the ADJET_BEHIND_CF constant is set explicitly; the newsletter opt-in no longer requires a nonce that broke on page-cached sites.
* New in the plugins list: a "Manage ads" button and a rate-this-plugin link.

= 1.9.0 =
* Added three more newsletter providers next to SendFox: MailerLite, Brevo and Mailchimp. Each one has its own template and its own connection, so you can run several at once and pick a different list per ad block.
* Every provider token is stored encrypted on its own, so connecting a new provider never touches an existing one. An already connected SendFox account keeps working unchanged after the update.
* Mailchimp signups are handed over with status "pending", which triggers Mailchimp's own confirmation mail.
* Uninstalling now also removes the tokens and cached lists of the new providers.
* Translated all new texts into German, Spanish, French, Dutch, Italian and Portuguese.
* Fixed: the click-through rate now follows the site language. On a German site it read "1.58" right next to "31.847" impressions, where the dot is a thousands separator - two different meanings for the same character.

= 1.8.7 =
* Added a small card above the content of the plugin's own pages that shows one thing at a time: a statistics milestone, a one-off review request, or a feature you may not have found yet. Never more than one card, never on other pages of the admin area, and every card can be dismissed for good.
* Replaced the four 30-day tiles above the ad block list with a weekly summary: impressions, clicks and CTR since Monday, compared with the same weekday span of the previous week, plus a 7-day chart and the strongest ad block of the week. Same local data as before, nothing new is collected.
* Added the setting "Delete all data on uninstall", switched off by default. Until you switch it on, removing the plugin leaves your ad blocks, settings and statistics untouched.
* The assistant now states plainly what your own Claude API key unlocks, and the settings explain that requests go from your site straight to Anthropic with your own account.
* Added complete Spanish, French and Dutch translations and completed the German one.
* Added a link to the manual on the plugin pages.

= 1.8.6 =
* Fixed: the Pro page named the wrong payment provider. Purchases are handled by Freemius, not Digistore24; the 30-day money-back guarantee is unchanged.

= 1.8.5 =
* The plugin now speaks English by default: all source strings were translated from German to English, and German ships as a complete translation that loads automatically on German sites. Before this release, every site saw the German interface regardless of its language.
* Removed the broken placeholder translation files (en/es/fr/nl) that did not actually translate anything; Spanish, French and Dutch will return via wordpress.org language packs.
* Added the six directory screenshots.

= 1.8.4 =
* Renamed the plugin to "Hafenstudios Ads – Ad Placement Manager" and changed the text domain to `hafenstudios-ads`, following the WordPress.org plugin review. No functional changes.
* Internal prefixes, option names, shortcodes and the REST namespace are deliberately unchanged, so existing ad blocks, settings and statistics keep working.

= 1.8.2 =
* Removed the legacy `[sai_ad]` and `[sai_ad_group]` shortcode aliases, so every public name this plugin registers now carries the `adjet` prefix. Use `[adjet_ad]` and `[adjet_ad_group]` instead.
* The Google AdSense loader is now enqueued with `wp_enqueue_script()` only; the custom script-tag filter that added the optional `crossorigin` attribute has been dropped.
* Documented the output escaping contract of the ad render pipeline directly at the shortcode, block and `the_content` callbacks.

= 1.8.1 =
* Fixed the Plugin URI and the SendFox terms link so all declared URLs resolve.
* Moved all frontend and admin JavaScript and CSS out of inline `<script>`/`<style>` tags into properly enqueued files: base styles now load as `public/frontend.css`, the AdSense unit call, the popup/exit-intent/cookie-consent behaviour and the admin live preview run from enqueued scripts. No behaviour change.
* Hardened and documented output escaping around the ad render pipeline (generated markup is escaped; raw custom code stays gated behind the `unfiltered_html` capability, like the core Custom HTML block).
* Switched all direct database access to prepared statements using the `%i` table-identifier placeholder, which raises the minimum WordPress version to 6.2.

= 1.8.0 =
* Added a built-in Cookie-Consent banner template: visitors accept or decline, it sets the consent signal your consent-gated ads wait for, and it emits Google Consent Mode v2 updates. No separate consent plugin needed.
* Added a native SendFox newsletter integration (bring your own SendFox access token): the new "Newsletter (SendFox)" template subscribes visitors straight into your chosen list. SendFox handles the double opt-in.
* Added a public opt-in endpoint protected by nonce, honeypot and rate limiting; the token is stored encrypted and used server-side only.
* Generalised the assistant with a third mapper type (provider) so more email providers can hook in later.

= 1.7.0 =
* Added a template gallery with ready-to-use ads, banners, notification bars and popups (free), plus Pro-badged premium templates and a Free/Pro foundation with an upgrade page. Free stays fully functional.
* Added a guided assistant: pick a template and just fill in fields (title, text, link, colour) with a live preview instead of writing HTML. Field values are stored and stay editable; "HTML selbst bearbeiten" switches to the raw editor.
* Added a guided Google AdSense assistant: enter Publisher-ID, Slot-ID and format, and the plugin builds the responsive unit.
* Added optional AI copy suggestions in the assistant: bring your own Anthropic Claude API key and let the AI draft titles, texts and button labels as fully editable suggestions. One request per click, never automatic; the key is stored encrypted (libsodium) and used server-side only.
* Added a site-wide "overlay" placement so popups, exit-intent and notification bars display and self-trigger.
* Added a first-run onboarding panel that routes you to AdSense, a template or your own HTML.
* Refined the admin: save-in-editor confirmation, compact icon actions, a collapsible "Erweitert" section, and Hans, the hand-drawn hafenstudios captain mascot.

= 1.6.0 =
* Added a least-privilege capability `manage_adjet` (administrators keep access via a manage_options fallback) plus an optional editor-access toggle in Settings.
* Added a REST API under `adjet/v1` for ad blocks, statistics and settings (Application Password auth).
* Added a 30-day impressions/clicks chart to the statistics page, rendered inline as SVG without external libraries.

= 1.5.0 =
* Rebranded the plugin as part of the hafenstudios plugin family.
* Redesigned the admin interface in the clean hafenstudios/Linkjet design language (brand bar, tab navigation, teal accent, KPI and content cards).
* Migrated shortcodes to `[adjet_ad]` and `[adjet_ad_group]`; the legacy `[sai_ad]` / `[sai_ad_group]` shortcodes keep working as aliases.
* Automatic data migration from a previous AdEngine install (ad blocks, settings and statistics).
* Added object-cache-backed statistics totals to reduce database load.
* Fixed AdSense script enqueue to always carry a version and cleaned up Plugin Check notices.

= 1.4.3 =
* Renamed the public plugin title to comply with WordPress.org naming restrictions.

= 1.4.1 =
* Refined admin interface with cleaner hero, glass navigation, stronger cards and better responsive behavior.
* Improved backend usability, visual hierarchy and AdSense onboarding cues.

= 1.4.0 =
* Added AdSense Wizard for easy responsive AdSense ad units using Publisher-ID and Slot-ID.
* Added optional AdSense Auto Ads frontend loader.
* Added dynamic Gutenberg block for manual placements.
* Added weighted ad rotation groups.

= 1.3.1 =
* Added campaign scheduling with optional start/end time.
* Added one-click duplicate and pause/activate actions.
* Added automatic placement after the first image.
* Added WordPress code editor integration and a safer admin-side live preview.

= 1.3.0 =
* Added optional local statistics toggle.
* Hardened import flow with JSON extension and file-size validation.
* Improved security posture around raw ad code.

= 1.2.0 =
* Added backend styling with hero area and KPI cards.
* Added localization starter files.

= 1.1.0 =
* Added label, AMP exclusion, server-side device detection, minimum word count, max-per-post, exclusion lists, lazy loading, consent gating, stats, import/export and uninstall routine.

= 1.0.0 =
* Initial version.
