Found a bug? Have a great feature idea? Get on GitHub and tell us about it and we'll get right on it: https://eshop-optimizer.com

= 11.1.5 - 2026-08-29 =
* **Search, consent and tracking scripts work again on cached pages.** One entry in the list Rooster One hands WP Rocket contained a slash, and WP Rocket's own pattern builder cannot escape one. The pattern failed to compile, and because a failed pattern is not told apart from "no match", every inline exclusion on the site stopped working: Rooster One's, and every other plugin's. Consent banner, Consent Mode, attribution, session tracking and the captcha token minter were all being delayed to page load despite being explicitly excluded, and on one shop it stopped the search autocomplete from starting at all for logged-out visitors. Only visitors saw it, never anyone signed in, because caching plugins leave signed-in pages alone.
* **Thumbnails under a product image now work on a phone.** Tapping a thumbnail moves the main image, and swiping the main image scrolls the strip to keep the current thumbnail in view. Neither did before on a narrow screen, where the strip scrolls sideways and the arrows are hidden, so the active thumbnail was usually off screen. The vertical strip on desktop follows the main image now too.
* **The lightbox filmstrip is centred.** When the thumbnails fit across the viewer they sit in the middle rather than pinned to the left edge, and when there are too many to fit the strip still scrolls from its true start.
* **Variation images no longer depend on which script loaded first.** Rooster One and WooCommerce both have a way to change product images when a variation is chosen, and which one won was decided by script load order, which differs between a cached page and a signed-in one. Ownership is now stated outright: with WooCommerce's variation gallery switched on it swaps the whole gallery, thumbnails included, and Rooster One stands aside; with it off Rooster One swaps the image in place. Rooster One also stops filling in a variation gallery for shops that have the feature switched off, which was quietly turning it on for them.
* **Private sign-in address and sign-up fixes.** The custom sign-in address no longer leaks through a second path, the "not found" page no longer names WordPress, sign-ups get their own answer instead of being closed by accident, and two-factor enrolment works again.
* **SEO redirects: fewer loops and a cleaner table.** Loops that travel through WordPress's own canonical hop are now caught, and the redirect table reconciles itself when the pinned category changes.

= 11.1.4 - 2026-08-27 =
* **Branch restock worksheets with a real approval flow (Pro).** Each branch gets a worksheet over the whole preorder-eligible catalogue with its own 30/60-day sales. Staff type shelf counts and requested quantities, press Submit requests, and the lines file under Supplier Orders as requests: approval, per-supplier order batches with XLSX and one-click email, receiving, and a read-only reconcile against the ERP's deliveries. Every step is written to an audit trail with the real user behind it. The old board's export quietly rewrote the sheet as a side effect; submitting is now an explicit, confirmed step that tells you how many lines it filed.
* **Branch staff see only their own branch.** Four assignable "Branch staff" roles, one per branch: each opens its own worksheet and nobody else's, including staff at the head branch. The approval queue across all branches stays with the administrator, so the business owner decides who manages requests.
* **A fullscreen product lightbox of Rooster One's own.** Built on the image viewer WooCommerce already ships, so nothing new loads and swipe, pinch zoom and the keyboard come free. Rooster One adds a scrollable thumbnail filmstrip along the bottom, pager dots, an image counter, autoplay with loop, a choice of arrow styles, and a backdrop that can be dark, light or frosted glass. One click or tap anywhere on the main product image opens it. Every setting lives on a new "Gallery thumbnails and lightbox" page in Storefront Display, together with the gallery thumbnail strip's own height and count.
* **The lightbox can open by itself when a visitor arrives.** Always, only when they come from a product listing, or from any internal page but never from a search engine or a typed address. The decision is made in the visitor's own browser, so it stays correct behind full-page caching, and it opens whichever viewer the shop uses, Rooster One's or the theme's.
* **Choose whose look the gallery, the lightbox and the recommendation rails wear.** The Theme compatibility screen now decides the product image gallery and the fullscreen lightbox as well, beside the variation swatches and the recommendation carousels it already covered: for each surface separately, keep the theme's rendering or hand it to Rooster One. Gallery and lightbox are independent choices, so the theme can lay out the product page while Rooster One owns the fullscreen view. Nothing changes until you choose, and a shop that never opens the screen behaves exactly as it does today.
* **Four ERP purchasing reports (Pro).** Purchases by supplier, VAT reconciliation, landed cost and margin, and supplier price drift, built from the invoice scanner's own data.
* **The first charts on reports.** A report can now carry a chart above its table, drawn from the current view and refreshed by the same filters and search as the rows.
* **Every admin data table now behaves the same way.** The long conversion completed: some 250 data tables across the whole admin share one component, with server-side sorting, filters, search, exports, bulk actions and honest empty states, and several reports that read retired third-party tables were rebuilt on the plugin's own data.
* **Who may see, edit, run and export reports is now yours to decide.** Four new capabilities and two assignable roles (Rooster Data Exporter, Rooster Data Editor), seeded so nothing changes on upgrade day, and revocable from day two, which the old gates could never be.
* **The event guest-list export now requires the export permission.** Names, emails and phones leave the site only with the same permission every report export carries, no longer with any account that can edit posts.
* **Stock Recovery honours the quantity you type.** The Qty box was posted and ignored; the number you type is now the restock quantity, and an empty box still uses the engine's suggestion.
* **Both recoverable-value readings, side by side.** The recovery queue shows the in-store-anchored recoverable figure and the sales predictor's modelled estimate as two labelled columns. They measure different things; now they can be compared instead of confused.
* **Customer records finally carry the shop's own latest-order date.** The eshop latest-transaction field had been empty since the day it was created. It now fills for every customer with an order and stays current as orders arrive.
* **A product mapped onto one of your subcategories now lands in the whole tree (Pro).** Matching a feed category to, say, "Portable fridges Igloo" filed the product under that leaf alone, so it appeared in neither "Portable fridges" nor "Camping". Every category attach in the import pipeline now carries the ancestors too, and "Refresh all details" repairs the products already imported without their tree.
* **The variable parent gets the SKU rule too (Pro).** A feed rule that prefixes SKUs reached every variation but left the parent with the bare vendor code. Parent and variations now go through the same rule by construction, and parents imported earlier with the bare code are found and re-keyed automatically on the next run instead of turning into duplicates.
* **Rule the category mapping in bulk (Pro).** The "Match the feed categories to yours" screen gains row selection with select and deselect all, Confirm selected, Reject selected, and "Set category on selected": pick five feed categories and file them under one of your categories in a single click. Every mass ruling still teaches the matcher the way a one-by-one ruling does, and the Suggest and Accept-at-threshold buttons are untouched.
* **A real import log (Pro).** Every finished feed run now lands in an Import run history kept for 30 days: which products were created and updated, by name, which rows errored and why, and the feed-quality notes. "27 errors" stops being the whole story.
* **The feed source tables join the house tables.** "Your feed sources" and the scheduler's "Feed imports on a clock" were still hand-rolled, with no sorting; both now render through the same component as every other table, sortable, with their row actions intact.

= 11.1.3 - 2026-08-23 =
* **Product pages with many variations open in a fraction of the time.** On a shop whose theme draws its own colour and image pickers, a product with dozens of variations could take fifteen seconds to appear whenever the page was not already cached. The list of variations was being rebuilt once for every swatch on the page, so a product with sixty shades built that list sixty times over instead of once. It is now built once and reused for the rest of the page. Nothing about the page changes, it is the same shop with the same pickers. Measured on a live shop: 15.5 seconds down to 1.2.
* **A new screen decides what Rooster One takes over from your theme.** Many themes draw their own variation swatches and product carousels, and where they do, both they and Rooster One were reaching for the same place on the page. Storefront Display now has a Theme compatibility screen that names the theme it found and lets you choose, for each part of the shop separately, whether the theme keeps it or Rooster One takes it over. Nothing changes until you choose, so a shop that never opens the screen behaves exactly as it does today.
* **Our variation pickers can wear your theme's design.** If you hand the pickers to Rooster One, you can still keep the appearance your theme gives them. The page gets the speed without being redesigned.
* **Rooster One now works out what your attributes are.** Every swatch plugin has its own name for what a colour or an image attribute is, so a picker built by your theme could be one we did not recognise. A one-off detection reads your own products and works it out, and it is not repeated when a page loads.
* **Variation swatches can be placed anywhere.** A new `[aieo_product_swatches]` shortcode, and a block that no longer has to sit inside a product loop, so the picker can be dropped into an Elementor or Bricks layout.
* **A Greek label read as a hardware term.** The wording for the recommendations surface was translated as a name for mounting rails rather than a part of a shop page. Corrected.

= 11.1.2 - 2026-08-22 =
* **Feeds can bring weight and box dimensions with them (Pro).** The catalogue importer could read a code, a name, a price, stock, pictures and categories, and nothing about the physical thing — so every imported product arrived with no weight and no box, and somebody typed them in a second time. Weight, Length, Width and Height can now be mapped, from three separate fields or from one field written as "10x20x30". Both simple products and each variation of a variable one get their own figures, which is what shipping actually reads.
* **When the feed's units are not your shop's (Pro).** A supplier publishing kilograms into a shop that works in grams is out by a factor of a thousand, and nothing would have told you. Make a field that multiplies the supplier's, and map that: the importer already had the arithmetic, and the four new fields explain it where you need it.
* **"Refresh all details" (Pro).** A repeating feed is deliberately allowed to change only price and stock, so a supplier cannot overwrite a name you rewrote. That also means products imported before today will never gain a weight. The new button beside a feed's "Run now" re-reads it once and rewrites everything onto the products that already exist, filling in what they were imported without. It does not change what that feed does on its schedule, and anything you pinned is still left alone.
* **Signing in by email works again.** Asking for a sign-in link answered with a bare "not found" error, on every site, for as long as the feature has existed. The address the form posts to was only ever created on ordinary page views, and never on the request that actually uses it. The message read like a permissions problem and was not one.
* **The sign-in form no longer stops accepting you.** After about ninety seconds on a page, the security check behind the login and registration forms quietly stopped renewing itself, and a submission was then rejected with nothing on screen to explain why. It now waits for that check to be ready, renews when you start typing, and paces itself so renewing cannot trip your host's rate limit.
* **The "connect by email" button and its window survive any theme.** In themes that rebuild the page as it is drawn, the button's icon appeared at full picture size and the window opened at the very bottom of the page instead of over it. Both now carry their own appearance, so a theme that discards the stylesheet can no longer break them.
* **Greek wording that said the wrong thing is corrected, across the whole admin.** Sixty-two labels were translated into a real Greek word with the wrong meaning, which is why nothing looked broken. A tracking event was called a social function, so "Raw events", "Total events" and the Meta and TikTok "Events API" fields all read as though they were about running an event. Reconciliation was called making peace, on the courier invoice screen, the promotions report and the marketplace importer alike. A rejection said a date where it meant a marketplace name. Every one was checked against the screen it appears on before it was changed.
* **"Event" now reads correctly in both of its meanings.** The word was one entry serving two unrelated things: the column heading over a log of tracking events, and the name of the Events feature itself. Greek has a separate word for each, so the two are now separate entries. Your Events section keeps its name and the log columns say what they are.

= 11.1.1 - 2026-08-22 =
* **Text that should have been in your language, and was not.** Wording you can override in the settings, such as the wishlist tooltips, the free-shipping progress messages, the SALE and NEW badges, the SKU and barcode labels, the add-to-cart button and the checkout invoicing fields, could get stuck in English. It happened whenever the built-in wording was saved into the database instead of being looked up when the page is drawn, and once it was saved it stayed, whatever language your shop was set to. All of it now resolves as the page is built, and an update repairs the values already stored. **Anything you typed yourself is left exactly as you typed it**, in any language.
* **Wording set by one language of admin no longer reaches everyone else.** If a shop has staff working in different languages, the person who pressed Save decided what every visitor saw. That is no longer possible for settings, and search engine descriptions are now built in the shop's language rather than in the language of whoever last edited the product.
* **Two hundred pieces of text can be translated for the first time.** The marketplace importer's messages were written against a translation file that was never loaded, so they were English everywhere no matter what you did. They are now part of the normal translation set.
* **Some text could not be translated at all.** The badge labels, the add-to-cart button text, the product code labels and the trust badges were fixed English wherever you were. They are translatable now, and Greek is included.
* **Your shop's own loyalty points are recorded again.** Rooster One awards points on an order, but the figure never reached the customer's record, so the "Eshop Loyalty Points" field on a customer sat empty next to the points your accounting system had sent. It is now filled from the points ledger and stays correct, including for shops that were already awarding points before this update. Points that are not whole numbers are kept as they are rather than rounded down.
* **A faster storefront on every page a visitor loads uncached.** Rooster One was asking your database the same questions on every single page view: whether its tables existed, and what character set they use. Neither answer changes between page views. They are now looked up once and remembered, which removes about 22 milliseconds of database work per page, and every table check and table creation from the storefront entirely. Cart, checkout and account pages, which are never served from cache, benefit most.
* **Table creation no longer runs on shopper page views.** Setting up the database is now done where it belongs, in the admin, on the command line, and on scheduled tasks. Nothing about your data changes; it simply stops happening on a page a customer is waiting for.

= 11.1.0 - 2026-08-20 =
This release also carries everything listed under 11.0.13 below, which was never published separately.

* **Pro features now carry their code in the Pro plugin.** The catalogue importer, product feeds between sites, duplicate products, and the Greek text matching all three rely on, have moved into Rooster One Pro. The free plugin keeps every screen, every description and the upgrade path, so you can still find these features, read what they do and see what they would give you — and a screen whose engine is not installed says exactly that instead of failing. **If you use any of them, update Rooster One Pro to 11.1.0 at the same time.**
* **Screens that were always Pro now say so.** "Import products from a marketplace" and "Product feeds between sites" have been Pro in the capability list since it was written, but neither screen showed it and the feeds screen never checked. Both now show what Pro gives you, and the Connections page marks every Pro service in its directory instead of listing them as though they were included.
* **The one-click marketplace import (eFood, Wolt, Etsy, Amazon) requires Pro.** It always counted as a Pro feature; it simply never enforced it.
* **Nothing changes on your storefront.** Recommendation rails, related content and audience filtering keep reading what was already computed for them, exactly as before, and keep working if the Pro plugin is ever deactivated. Behavioural data collection stays in the free plugin so a shop that upgrades later still has its history.

= 11.0.13 - 2026-08-20 =
* **Prefix or suffix the SKU a feed creates.** A supplier's code 101.55.3 can be stored as ELDIC0-101.55.3, so you can see at a glance which supplier a product came from. The rule is applied as each row is read, so the SKU the catalogue is searched on and the SKU a new product is created with are always the same value: a match updates, a miss creates, and the two can never disagree.
* **Filter an import by SKU or barcode.** Include or exclude by shape rather than by list: `101.55.3*` takes only codes starting with it, `*-EL` only those ending with it, and a plain `EL-` any code containing it. A dot stays a dot, so real codes behave the way you type them.
* **The same code patterns target offers.** Brand discounts, brand gifts and free shipping accept the same include and exclude patterns, matched against a product's SKU and barcode, so one supplier's whole range can be covered without listing it.
* **Products are identified by SKU only.** A barcode can no longer establish identity, because the same article carries different barcodes per country and per revision, which meant an import could update the wrong product or duplicate one. Barcodes remain imported, stored and filterable.
* **Give suppliers to products you already had.** A feed records the supplier on everything it imports, but products that were in the shop first had none, and showed as unresolved on the re-order boards. A feed source can now link the brands it carries to its supplier, which repairs those older products without touching them: a product finds its supplier through its brand when it has none of its own. It previews what it would change first, never overwrites a brand that already names a supplier, and modifies no products.
* **Supplier Orders: pick the orders first, then the supplier.** The supplier list is built from what those orders actually contain, several suppliers can be chosen at once through the usual product-picker modal, and the item list no longer stops at the first 500 orders.
* Fixes a multi-select on the feed filter screen that lost its styling once you picked something, and a colour-swatch help text that could never be translated.


= 11.0.12 - 2026-08-20 =
* **Feed rules: reshape a feed before it becomes products (Pro).** Five ways to make a field out of what a feed really sends: cut at a delimiter or by pattern (the parent code hiding inside a SKU, an attribute inside a specifications blob), multiply (a feed that carries only the wholesale price maps it to Cost price, and a made field "retail" — the same price times 1.5 — becomes the Price), sum per-warehouse quantities into one stock figure, or pick one named warehouse's value. "Set a value" rules watch feed fields and write stock, prices or a discontinued flag; conditions on repeating groups are paired within one entry, so "address is X AND it says available" is true only when that one warehouse says both.
* **Filter what a feed imports (Pro).** Per-feed include and exclude lists over the feed's own categories, brands and tags, combined with AND or OR; an exclusion always wins. The pickers read the whole feed even when a test cap is set.
* **Import at most N products (Pro).** Set it to 1 and watch how a single product of a new feed actually lands before letting the whole catalogue through.
* **The feed's categories are matched to yours, and your rulings teach it (Pro).** Vendor category paths are scored against your category tree, Greek-aware; a review panel shows every suggestion with its score, and you confirm, correct or reject each one. Rulings persist in a register that survives deleting the feed and transfers to other suppliers' feeds: an exact path applies at full confidence, and a near match raises a lexical half-match to a strong one. A category with no match can be created locally, skipped, or keep the product out entirely — your choice per feed.
* **Availability words teach a register too (Pro).** A rule that says what a feed's availability word means is remembered, and feeds that use the same word get the same reading without a rule.
* **New: a discontinued state.** A rule or availability word can mark a product discontinued: out of stock and hidden from the catalogue while its page stays live, and the state is sticky — a feed that simply stops mentioning the product never quietly revives it.
* **Wholesale prices land in WooCommerce Cost of Goods.** A new "Cost price (wholesale)" mapping target writes straight into Cost of Goods (WooCommerce 9.5+): set on create, moved on change, filled where empty on enrich — and never cleared by a feed that omits it.
* **Feed schedules got a real calendar.** Hourly, daily, weekly or monthly, with the hour, the weekday or the day of the month, all in the site's own timezone and self-correcting across daylight-saving changes. The Data preparation page gained an audit card showing every source's cadence and next run, and the Connections page now counts your own feed sources instead of reporting the importer "not set up".
* **New: Supplier Orders.** One page that answers "what should I order from each supplier": items from open orders or any period, aggregated per product, exportable to XLSX, and emailable to the supplier's order-taking address in one click. The staff pre-order boards moved here from the Branch Scanner, so purchasing lives in one place.
* **The supplier is part of product vitals.** Each product's supplier is resolved nightly into the vitals table (direct assignment first, then the ERP catalogue, then the brand's supplier), so order screens answer instantly instead of walking the chain per product.
* **Duplicate products reads more Greek (Pro).** Two colours in one variant ("Τιρκουάζ/Πράσινο") stay one colour value; litres keep their unit; dimensions such as "40 X 127 Cm" bind the unit to the numbers so a trailing code stays a code; and colourway groups — the same sizes repeated across colours — coalesce into one product with both size and colour axes.
* **Fix: a PHP 8 fatal on malformed text from imported catalogues.** Invalid UTF-8 bytes in a title could take down the Duplicate products screen and the content indexer's word count. Both now degrade instead of fataling, which is the resolver's contract with catalogue data.
* **Fix: editing a saved feed source no longer loses its address.** The file picker stopped clearing the URL, an empty address on save keeps the stored one (a single - removes it), and re-picking "One product is" during an edit works from the stored sample. The default for "one product" now prefers the row that carries SKU and price signals over an equally-nested warehouse row.
* **The whole new surface speaks Greek.** 149 new strings translated, and the match-score column header corrected — it read «Αγώνας», a sports match.
* **The update screen reads like an update screen again.** The upgrade notice is one short paragraph; the story stays here in the changelog.

= 11.0.11 - 2026-08-18 =
* **New: Duplicate products (Pro).** Suppliers often send the same product several times, once per colour or size, so the catalogue ends up carrying three separate products where it should carry one product with three variations. Tools & Settings → Duplicate products finds those groups, shows the evidence behind each one, and can merge a group into a single variable product. It reads the difference out of the product titles, so it can tell you these three differ by colour, or by size, or by both, and it refuses to merge any group whose difference it cannot name rather than guessing. Groups you know about yourself can be assembled by hand with checkboxes and a running selection that survives searching again.
* **Merging always shows you what it will do first, and can always be undone.** There is no one-click merge anywhere in the screen: you see the product that would be created, each variation with its SKU, price and stock, and the numbers behind it, including how many past order lines are involved. The originals are never deleted, only retired, so undo puts everything back exactly as it was. Past sales keep counting towards the merged product, because a register records which old product became which new variation and the reports follow it.
* **Related content can now appear on any kind of content, not just posts.** The automatic related-content section had exactly one content type built into it, so a site whose content is listings, courses or guides could switch the feature on and see it on its handful of blog posts and nowhere else. Internal Linking → Settings now asks which content you want it on, and lists every content type your site has.
* **The block and the shortcode can show one kind of content.** A listing page can now recommend other listings instead of products: set "Show only these content types" in the block, or `show="listing"` in the shortcode. Left empty, both behave exactly as before. The shortcode's old `type` attribute still means what it always meant, which is the source page rather than what gets shown, and it now has a clearer name: `source_type`.
* **The block is findable and shows what it will do.** It was called "Semantic Related Content", so searching the inserter for "AIEO" or "Rooster" never found it, and it drew an empty dashed box in the editor. It is now "AIEO Related content", it previews the real cards while you edit, it has controls for the heading, the number of cards and the content types, and it says so plainly when the engine has nothing related to that page.
* **Article and guide cards look like cards.** A card for content with no featured image used to render with no image area at all, so it collapsed while the cards beside it kept full height. It now keeps its place in the grid, carries a date for articles or the content type's name for everything else, and asks for the same image size products do instead of one that could be six times larger in the same row.
* **Views and clicks on content sites are recorded again.** The tracking script was loaded only when WooCommerce was active, so on a site with no shop the related-content section could be seen and clicked all day and nothing was ever logged. Where it did run, every view was recorded against "no page", because the code only knew how to identify a product page. Both are fixed, and each view and click now records what kind of content it showed and what kind of page it was shown on.
* **Fix: unticking a content type on the Internal Linking settings screen did nothing.** The three checkbox lists on that screen could grow but never shrink, because the save merged the new list into the old one item by item. Unticking now removes.
* **Fix: a supplier feed that lists each variant as its own row rewrote the parent product instead of the variant.** When a row's SKU or barcode belonged to a variation, the import walked up to the parent product and wrote the row there, so the product's name and description were replaced by whichever colour or size came last in the file, its barcode was overwritten by the variant's, and in "enrich" mode that one variant's categories were added to it. Rows now update the variation they actually name, and the parent is left alone. Worth checking any product whose name reads like one of its variants, because this release stops the damage but cannot undo what earlier imports wrote.
* **Fix: per-variant prices and stock never updated from a feed.** Because those rows were being applied to the parent, and a parent with variations carries no price or stock of its own, the price and stock on them were discarded without a word. A variant's price and stock could sit unchanged for months while the feed ran every night. They now land on the variant, and the parent's price range is recalculated afterwards.
* **Related content can be told who it is for (Pro).** The engine scores how alike two pieces of text are, and a men's cologne and the women's cologne of the same range are almost the same text, so each was the other's best recommendation. That is not a ranking fault: who a product is for simply is not something similarity can see. Internal Linking → Settings now carries a grid of which audiences may be recommended to which, and a scan that reads your categories, tags, brands and attributes and proposes what each one is for. The scan proposes and you confirm, always, because on our own reference shop it proposes the shop's own brand name as a women's audience, correctly by its own rules and wrongly in fact. Descriptions are deliberately never read: measured on a real catalogue, reading them classified almost half the products and was wrong more often than it was right. Ships switched off.
* **Related content can be told how old is too old (Pro).** A yearly announcement is nearly identical to last year's, so the best matches for this year's page were the same page from 2025, 2024 and 2023, and the reader was being sent to a superseded copy of what they were already reading. You can now set how old content may be before it stops being recommended, separately for each kind of content, and mark the kinds that never go stale as evergreen so they stay recommendable for ever. Evergreen matters more than the window does: today's news article pointing at your permanent guide is the single best recommendation on the board, and a plain "prefer recent" rule would suppress exactly that. Off by default, and never applied to products, where a two-year-old product is not a worse one.
* **Choose the language model the semantic engine runs on (Pro).** Internal Linking → Settings lets you pick the model, and change your mind: switching re-analyses the site in the background while related content carries on showing the previous results until the new ones are ready, so nothing goes blank while it works. Leaving it unset uses the default for your site, which is also the way back from a model that turned out to be no better.
* **Fix: cart-adds from the related-content rail were credited to WooCommerce Related Products.** The rail was never registered as a place of its own, so every product added to a basket from it was recorded against `product_related` instead. Anyone reading their attribution reports has been reading an over-credited WooCommerce Related Products and a semantic row that was always empty. Both halves are fixed, and the rail is now counted wherever it appears, including on articles and listings. Rows written before this release cannot be separated retrospectively, so treat the split between those two surfaces as unknown before today rather than as measured.
* **Fix: the semantic engine kept superseded rows after a model change.** Both artifact clean-ups only ever collected rows belonging to the model in use, so the day a site changed model the previous model's rows were never visited again and survived indefinitely, looking current. Measured on a live site: changing the model took the cluster table from 300 rows to 600, every cluster silently gaining a twin. Clean-up now collects every row older than the run that replaced it, whichever model produced it.
* **"What the import creates" is now "Import products from a marketplace",** which is what the page does; the old name described one section of it. That heading stays where it belongs, over the preview of what a feed would create.
* **Imported images are converted to WebP.** The importer used to store whatever the marketplace served, which is JPEG and PNG, and WordPress then generated a full set of thumbnails in the same format for every one of them. On a catalogue import that was the single largest cost in disk and in backups. Images are now converted before they are stored, so every thumbnail is made from the WebP and no JPEG is kept beside it, and the camera and location data that marketplace photography routinely carries is removed at the same time. Quality is yours to set and defaults to 82. Images already in WebP or AVIF are left alone, a conversion that would have made a file larger is discarded rather than kept, and a server that cannot write WebP imports the original format and says so instead of failing.
* **A supplier feed that lists a product once per colour or size can now build one product with variations.** Where a feed says which rows belong together, those rows become a single variable product instead of five near-identical ones. What they vary by is read from the feed where it names the field, and from the product names otherwise, using the same detection the Duplicate products screen uses. A group whose difference cannot be named is imported as separate products and the reason is reported, rather than guessed at: variations nobody can name are variations nobody can edit.
* **New: build your own import source from a sample feed, and put it on a schedule (Pro).** Paste a sample of any XML feed, or give its address, and Rooster One reads its structure: which node is one product, what fields hang off it, and how often each field is actually filled in, which is the number that tells you a barcode is present on 4% of rows. It proposes what each field means, you confirm every one of them, and you see the first products exactly as they would be created, including which rows become one product with variations, before anything is written. Name it and it is saved as a source you can import from like any marketplace, or leave to run on its own every hour, day or week. A scheduled run creates drafts and adds only new products unless you say otherwise, because nobody is watching it; a catalogue too large for one run records where it stopped and the next one carries on. What is stored is a mapping, never generated code: a pasted feed is untrusted, and turning it into something that then runs on every import is not a thing we will do.
* **The whole admin is in Greek again.** 1,210 pieces of wording had gone untranslated across several releases and were showing in English on a Greek shop: the entire sign-in security screen, the Semantic Internal Linking screens, every block name and description in the editor's inserter, and the SEO description and search-query screens. All of them are translated, and the wording follows the terms the rest of the plugin already uses.
* Tweak: the related-content settings are written in content language rather than product language, and the paragraphs explaining the block and the shortcode have moved out of the brand-limit row they were hidden in.
* **Feeds belong to suppliers now.** A feed source can be assigned to one of your suppliers — the same suppliers you manage at Products → Suppliers — or create one as you save it, and every product the feed creates or updates is placed under that supplier. That one assignment is what makes lead times, payment terms and the reorder lists cover feed-imported products, which they never could while the feed only wrote its name on the product. Which feed wrote a product is still recorded separately, because one supplier can send several feeds.
* **A supplier directory, with the ERP's own record on each supplier.** Tools & Settings → Supplier metadata now lists every supplier with its commercial terms and how much of the catalogue it reaches, and each supplier carries the fields the ERP keeps: ERP id and supplier code, VAT number, address, telephones and corporate contact. If the previous implementation's tables are still in your database, one button migrates all of it — supplier fields, the ERP master, per-product supplier names and the pre-order history — filling only blanks, never overwriting your edits, and safe to run twice.
* **The brand's supplier is a real field now,** not the read-only note it used to be, and a product's supplier is resolved in a stated order: the product's own supplier first, the ERP catalogue second, the brand's supplier third.
* **Build an import source from JSON and CSV feeds, not just XML.** The same reader recognises which of the three it was given, a JSON feed's nested rows can read fields from their parent exactly as XML variants do, and a CSV keeps your column headings as labels even when they are Greek.
* **The pin and the supplier are visible on products.** The products list gained a Supplier column that reads the supplier assignment itself, and the product editor gained the pin that protects a product from feed updates — both existed underneath, neither could be seen or set without code.
* **Fix: a flat feed row that matches a product with variations no longer overwrites it.** In the add-and-update modes, one such row would have rewritten the product's name from a single variant and stripped its categories. It is now refused and reported, with "price and stock only" named as the mode that updates it safely.
* **A store can take stock (Pro).** Each store on the Locations & Reviews screen has a "Stock-taking" box: tick it and everything the Branch Stock Scanner needs is created for you — the stock board, its audit and pre-order triggers, a first fill of your own catalogue so the first scan already finds its product, and a published scanner page locked to that store. Untick and the branch is disabled; nothing is deleted.
* **The scanner is the full phone tool, on its own path.** Scan barcodes with the phone camera on every major browser, switch cameras, save stock and requested quantity and go straight to the next scan; a barcode the board does not know is logged once, with the camera frame that saw it, so the buyer can see what was actually on the shelf. Scanner pages live under /scanners/, are served bare with no theme header or footer, require signing in, and are never indexed. A new role — Rooster Stock Scanner — lets floor staff use them without any shop-management rights.
* **Pre-orders have an approval flow.** What staff request from the shop floor can now be approved, marked ordered, received with a quantity, or rejected, each step recording who and when. Received and rejected lines leave the outstanding totals that drive the next purchase order.
* **Fix: "Confidence" was translated as self-confidence.** The statistical sense is now Βεβαιότητα everywhere.

= 11.0.10 - 2026-08-15 =
* New: third card design source, "Theme design" — hands your precomputed recommendations to the theme's own related-products and up-sells blocks, so the theme draws the cards with its own markup, CSS and carousel.
* Fix: "Display the product short description" and the rating toggle were enforced only by a stylesheet the two non-default card modes switch off, so both settings did nothing in Neutral and Theme modes. Enforcement moved to the card renderer and now behaves the same in all three modes.
* Fix: a global rating toggle set to off also hid ratings that a Product Slider block had explicitly enabled.
* Fix: recommendation rank is now preserved when the theme renders the cards (WooCommerce shuffles related products by default).
* Tweak: recommendation card type scale, alignment and star sizing brought into proportion with theme cards.

= 11.0.9 - 2026-08-14 =
* **Onboarding a big catalogue from the command line works again.** 11.0.8 introduced `wp aieo semantic onboard` for indexing large shops in chunks. It worked on our machines and failed in every released build: the packaged code has its comments removed, and the helper command the indexer calls was named by an instruction that lived in a comment. With that gone the helper had a different name, so every chunk was refused and the run gave up with "Three unparseable child responses in a row". The name no longer depends on a comment. Indexing from the admin card was never affected.
* **The panel's push notification buttons reach the plugin again.** Readiness, Go Live and Pause in the hosting panel call the same kind of comment-named command and had been silently failing for the same reason. Fixed by the same change.

= 11.0.8 - 2026-08-13 =
* **Link suggestions can now approve themselves.** Internal Linking gains a choice under Settings: keep reviewing every suggestion by hand, or let the engine's suggestions go live as they arrive. Auto-accepted links stay listed on the Suggestions tab so you can see exactly what was placed and reject anything you dislike — and a rejection is still forever, in either mode. Your own declared anchor rules always outrank a suggestion, and the cautious match types (sound-alike and embedding-only matches) are never auto-approved; those still wait for a human.
* **Indexing a large catalogue no longer runs out of memory.** The initial index used to run as one long job that grew until PHP killed it — on a five-thousand-product shop it died partway through and there was no way forward without a developer. It now works through the catalogue in chunks, each in a fresh process, resuming wherever it stopped: from the same admin card as before, or from the command line with `wp aieo semantic onboard`. Closing the browser tab pauses a card-driven run; reopening the page resumes it.
* **Approving vocabulary now actually does something.** Approving a multi-word term told you the affected pages were "queued for re-extraction on the next index pass" — and then no pass ever came, so nothing changed until someone reindexed by hand. Approvals now queue their pages for re-extraction immediately, the background queue works through them within minutes, and the next sync carries the result through. The message now says what really happens.
* **Two Greek word families untangled.** The identity layer treated διαφορά (difference) and διάφορος (various) as one word, and likewise συνήθως (usually) and συνήθης (usual) — so pages about one could be linked as if about the other. Both pairs are now kept apart. After updating, the semantic index re-derives itself on the next indexing pass; that is expected and needs no action.
* **The text cache now caches.** A table meant to hold each page's extracted text was created but never filled, so every indexing pass re-extracted every page from scratch. It fills as content is indexed and repairs its own table if missing.
* **Relevance is right from day one.** The term statistics that feed relevance rankings used to be filled in only by a nightly maintenance run, so a freshly onboarded shop spent its first day ranking on incomplete numbers. They are now refreshed the moment onboarding finishes.
* **A stuck compute job can no longer swallow your changes.** On the compute service, a job that had been queued for a while would absorb any newer request, so a just-pushed catalogue silently never recomputed. A queued job now re-arms itself when the content actually changed, and the observability page shows how long each job has been waiting.
* **A Security section, and the three things that actually keep people out of your shop.** Set Up now has a Security column holding two-factor authentication, a private address for signing in, and a pause on repeated password guessing — plus the bot protection that used to sit under Tracking & Consent, which is where nobody looked for it. All three read one list of roles: you say once which accounts run the shop, and everything applies to those. Your customers are not affected by any of it.
* **Two-factor authentication: a code from your phone as well as a password.** It is what stops a password that leaked somewhere else from being enough to get into your shop, and a leaked password is by far the most common way a WooCommerce site is taken. Staff scan a QR code with Google Authenticator, Microsoft Authenticator, 1Password, Authy or anything else that reads one, and get ten recovery codes in case a phone is lost. You choose how many days they have to set it up, counted from the first time each person is asked rather than from the day you switch it on, so somebody on holiday still gets their full run.
* **The QR code is drawn on your own server.** The usual way to put a QR code on a page is to ask Google or another service to draw it, which means sending them the secret the code contains — after which it is in somebody else's log and is no longer a second factor. Rooster One draws it locally. Nothing about setting up two-factor authentication leaves your site.
* **A private address for signing in.** Every WordPress site keeps its sign-in page at the same well-known address, and automated scanners hammer it around the clock. You can move yours. The point is less that it hides the page and more that it silences the noise, so a real attempt on your shop is visible in your log instead of buried under thousands of fake ones. Password-reset links, logout links and the "session expired" box carry on working at their published addresses, because recalling those would break emails your customers already have.
* **Moving the sign-in page now actually means something on a shop.** Your WooCommerce My Account page carries a second, fully working sign-in form, and it accepts an administrator's password exactly as the old page did — so on every other plugin that offers this, hiding one door leaves the other wide open. Accounts in your protected roles are refused at the customer form and pointed at the staff address instead. Customers sign in as normal, and shop integrations that connect over the API are untouched.
* **Sign-in pauses after repeated failures.** WordPress will let a script guess passwords as fast as your server can answer them. After a number of failures you choose, that address is refused for a while, and an address that keeps coming back gets a much longer pause. Nothing happens to the account itself: no password is reset, nothing is disabled, and the pause lifts on its own. The person whose account is being guessed at is emailed, at most once an hour however heavy the attack.
* **The failed sign-in log never records an attempted username that matches no account.** Far more often than an attack, that is somebody who typed their password into the username box — and writing those down would build a list of your staff's passwords, which is a worse thing to own than the problem it was meant to solve.
* **None of it can lock you out by accident.** Everything ships switched off and does nothing until you tick a box. The settings screen refuses to switch on a feature aimed at no roles, refuses a sign-in address that would hide one of your own pages, and refuses the ones a scanner tries first. If an address is ever lost, one line in wp-config.php switches the whole lot off. A copy of the shop pulled down for development never enforces any of this, so it cannot demand a phone that is not in the room.

= 11.0.7 - 2026-08-08 =
* **The cookie banner no longer talks about advertising on shops that do not advertise.** The wording was written for an ad-funded publisher: it told every visitor that we and our partners collect their data to "fund our services through advertising", and described the Marketing category as delivering ads to them. On a shop or a content site that carries no advertising that is simply untrue, and asking permission for things nobody is doing invites people to refuse the ordinary visitor statistics you do want. The banner now describes what it actually controls: keeping the site working, remembering your preferences, collecting usage statistics with your consent, and — only where such technologies are in use — showing or measuring content through third-party services. The count of third-party services found on your own site is still shown, because that number is real and it comes from your own cookie scan. You can still replace any of this wording from Cookies & Consent.
* **The Agree button is always reachable on a phone.** The buttons are now pinned to the bottom of the dialog with the text scrolling behind them, so consenting never requires scrolling first, in any language and at any length of wording you write. A consent dialog whose Agree button is off-screen measures patience rather than agreement.
* **"Decline all" no longer records that the visitor agreed to functional cookies.** It was recording a refusal as consent for that one category in the proof-of-consent log, and ticking the box when the visitor reopened the panel. Existing records are unchanged; new ones are correct.
* **The cookie banner is now served by the runtime plugin.** It keeps working on shops whose front end is trimmed down to runtime code only, where previously the banner, the Google Consent Mode signals and the consent answer the tracking gates depend on were all simply absent.
* **The banner survives more of WP Rocket.** It was already protected from delayed JavaScript; it is now also protected from Remove Unused CSS, which would strip the styling and leave an unreadable block of text, and from Lazy Render Content, which would show the banner only to visitors who scroll.
* **The Facebook and TikTok pixels no longer hold up the page on cheap phones.** The pixel code still records every event the moment it happens; only the download of Facebook's and TikTok's own scripts now waits for the browser to be free, or for the visitor's first tap. On a low-end Android these two were a large part of what made the site slow to respond to the first touch.
* **The checkout invoice script no longer loads on pages with no checkout on them.** It was running on every page of the shop looking for VAT and tax-office fields that cannot exist there.
* **Duplicate now appears on every content type that supports it**, including Stores and FAQ entries, which could be duplicated in principle but offered no way to do it.
* **The cookie scanner can now recognise third-party services again.** The list of known services was never reaching the storefront, so a scan could only ever find cookie names and never the service behind them — which is why the number of partners shown in the banner was usually missing.

= 11.0.6 - 2026-08-07 =
* **Popups: design an announcement in the block editor, then schedule it.** A new Popups screen replaces the old Exit-intent modal. Each popup is a post you build with blocks, so you lay it out the way you lay out any other page, and it translates the same way too. The supplied "Popup — split media" layout puts your text beside a full-bleed picture on a computer and stacks them on a phone, with a separate mobile picture so a wide photograph is not cropped down to nothing on a small screen. Set the dates it should run between, where in the shop it appears, who sees it, what makes it open and how often the same person may see it.
* **Exit intent is now one of four ways a popup can open**, alongside on load, after a delay, and after scrolling. Every one of them works on a phone: the old exit-intent modal listened for the mouse leaving the window, which never happens on a touchscreen, so on mobile it could not appear at all. On a touchscreen exit intent now reads a pause or a quick flick back to the top instead. Your existing exit-intent message and coupon are carried over automatically, keeping their reporting history intact.
* **A popup stops when you said it should stop.** Shops run pages from a cache, and a page saved while a promotion was still running would happily keep showing it for hours afterwards. The end time is now enforced three separate ways, so an announcement about being closed for the holidays disappears when the holidays end.
* **Test mode.** While you are working on the design, one checkbox makes a popup appear on every page every time you reload, instead of once and then not again. It carries a visible TEST MODE badge and records no statistics, so trying it out never distorts your figures.

= 11.0.5 - 2026-08-03 =
* **Cash-on-delivery orders are now always charged the delivery surcharge, and card orders never are.** The block checkout works out the surcharge before it knows which payment method the customer actually picked, so an order could be placed without the surcharge, and a card order could go through still carrying one. Rooster One now checks every order against the payment method it was really placed with and corrects it before the customer is charged. It works the same way on the classic checkout, and when someone returns to pay for an order whose card payment failed the first time. Every correction is written into the order notes, so you can see what happened and why. Orders your staff create by hand are deliberately left alone, so phone orders stay exactly as you entered them.

= 11.0.4 - 2026-08-03 =
* **The withdrawal link in an order email now opens the order itself.** It used to land on a form that already knew the order number but asked for the email address the message had just been sent to, and then sent a second email before anything could happen. The link is signed now, so it goes straight to the list of items with nothing to fill in. The signature is tied to that one order and expires on its own, and a tampered link opens nothing.
* **Fixed: the withdrawal button arrived in English on Greek emails.** Order emails are built after WordPress has switched language, and the plugin was not following that switch, so the one piece of wording the law is specific about came out in the wrong language while the text around it stayed Greek.
* **The withdrawal form now disappears once you have used it.** It used to come back under the "check your inbox" message, so customers assumed nothing had happened and submitted it two and three times. The confirmation stands on its own, with a single button for anyone who needs to try a different address.
* **An expired withdrawal link no longer costs you a retyped address.** If the link is genuinely ours but past its date, the page fills in the order's email address for you and asks only that you press Continue. The address is never carried in the link itself, so it stays out of server logs and browser history.
* **Signing in is offered, never required.** When an order was placed with an account, the withdrawal page says so and offers the shortcut, with the full form still working underneath. Customers who are already signed in are pointed to the one-click button in their own orders instead.
* **Fixed: the withdrawal form asked for a reason twice.** A drop-down and a free-text box both carried the label "Reason (optional)", with nothing to say which was meant. The box is now "Other comments", which is what it was always for.

= 11.0.3 - 2026-08-02 =
* **A welcome that explains what you have just installed.** The first time you open Rooster One it says thank you, admits there is a lot in here, and explains why it is one plugin rather than twelve: separate plugins each build their own consent prompt, their own pixel, their own copy of your catalogue, and then quietly disagree about your numbers. It also says plainly what is free and what is Pro. Reopen it any time from the lightbulb icon in the top bar.
* **Tell us about your shop, if you want to.** Four short questions sit folded away under the welcome. Answer them there, or skip and be asked once more a week later, when you have formed an opinion. Skipping costs nothing.
* **A feedback button on every Rooster One screen.** Send a bug, an idea, or something you liked, with an optional rating, without leaving wp-admin. Your address is prefilled so we can reply, and you can send anonymously instead.
* **A support group on Viber**, one click from the top bar.
* **Fixed: the site-profile badge described the maths wrongly.** Hovering "Shop" claimed Opportunities, cornerstone pages and demand scores were "all weighted on revenue". None of the three works that way: Opportunities never reads a money column at all and leans mostly on how often a view becomes a cart, cornerstone pages lead on units sold, and demand is how fast a product is actually selling. The explanation now says what each one really uses.
* **The badge stops shouting how it decided.** It printed AUTO beside the profile on almost every shop, and a label that reads the same everywhere tells you nothing. How the profile was chosen moved into the explanation you get on hover; what stays on the badge is a warning when a profile you set no longer matches what detection sees.
* **Fixed: Greek wording across the new screens.** Among others, the feedback form asked for "Απάντηση σε" — the literal translation of an email header rather than a request for your address.
* **Every button in the admin now has a name, and the manual says what it does.** 421 controls across 118 screens were catalogued and described in plain language: what changes when you press it, whether the customer is emailed, and the exact question it asks before doing anything irreversible. Eight of them contact a customer, and each now says so before you click rather than after.
* **Search finds actions, not just screens.** Ctrl+K and the assistant used to answer "where is X" only. Typing "disavow", "quarantine" or "never suggest" now takes you to the control itself, shown as the screen it lives on followed by the button.
* **A "?" beside a control explains it** and links to the matching page of the manual, resolving to the exact button where one is documented and to its screen otherwise.
* **Fixed: an English-speaking operator on a Greek shop saw a fully Greek admin.** The plugin asked for a translation before WordPress had worked out who was reading, which locked the whole page to the shop's language whatever that person's own account said. Each reader now gets their own language, and two people reading in different languages no longer evict each other's cached copy.
* **Fixed: settings text saved in whichever language you happened to be using.** Fields pre-filled with the built-in wording stored that wording permanently the moment anyone pressed Save, so the text stopped following the reader. Those fields now show the default as a placeholder: leave the box empty and the wording follows each reader's own language.
* **Fixed: in-page tabs are now declared by the screen that draws them.** A hand-maintained list had drifted, advertising a "Migrate from legacy" tab to shops that do not have one. Tabs a shop cannot see are no longer offered to it.
* **Usage sharing, off by default and Pro only.** A licensed shop can choose to tell us which admin controls its own staff use, so we can see which features are worth building on. It records a control name, a screen and a timestamp: never an order, a customer, or anything typed into a field. This is unrelated to your cookie banner and to your shoppers.

= 11.0.2 - 2026-08-01 =
* **Refusing a withdrawal request.** A shop manager can now decline a request that falls outside the right — sealed or pharmaceutical goods opened after delivery, or a request made after the deadline — citing one or more of your own published grounds. The customer is emailed exactly what was ticked. The grounds are yours to edit under the withdrawal settings, prepopulated from a standard returns policy. Refusing is blocked once a return voucher or label has been issued: cancel it with the courier first, so nobody is sent to collect a parcel you have just declined.
* **Every step now shows who did it.** Hover any date in the progress column to see which member of staff took that action.
* **The progress column reads as one clear path**: request received, acknowledgement sent, decision issued, return voucher raised, postage label sent, goods back, money returned. The voucher line appears only when a voucher exists or can still be raised, and says "not yet sent" only while that is still true.
* **Shop managers are guaranteed access to everything.** Access used to depend on the "edit pages" permission, which shop managers normally have but which some sites remove — and when it was removed the whole plugin vanished from their screen with no explanation. Rooster One now uses its own permission, granted to anyone who manages the shop or edits pages. Nobody loses access; shop managers gain it by right rather than by luck.
* The changelog link in the plugin header now points at rooster.systems.
= 11.0.1 - 2026-07-31 =
* **Fixed: the e-shop Manager and AI Chat pages showed programming text where their menu bar should be.** Both pages opened with a block of source code printed across the top and no navigation bar. Nothing was broken behind it and no data was affected, but the two pages could not be navigated from. Home and every other page were unaffected. Please update.

= 11.0 - 2026-07-31 =
**From this release our version follows WooCommerce**, so the number tells you which WooCommerce this was built and tested against. While WooCommerce is on 11.0 we ship 11.0, 11.0.1 and so on; when they move to 11.1 we go to 11.1.0.

* **The WordPress sidebar and the toolbar now list what the plugin does.** Both carry the twelve main sections instead of internal page names, each in its own colour, and each opens Home filtered to just that section. Clicking a section in the top pill bar does the same, so all three menus answer the same way. Tapping a section on a tablet now works too, where a hover menu never could.
* **Bot protection (reCAPTCHA v3) has its own page**, listed directly under Registration and sign-in rather than hidden at the bottom of the sign-in settings. It is findable by searching "reCAPTCHA", "captcha", "bots", "spam" or their Greek equivalents.
* **Ctrl+K no longer offers the same destination twice.** 47 duplicate or dead entries removed, including ten that pointed at cards which no longer exist.
* **The Analyses menu is in Greek.** Every report page in it rendered in English because its labels were translated before the language file had loaded.
* **Greek corrections**: "Slug" read as the animal, warehouse stock read as stock-market shares, Settings and Preferences had collapsed into one word, and "parent product" was said three different ways.
* Menu spacing, pinned shortcuts and section colours are consistent across all three admin pages, which now share one menu bar and one sticky header instead of separate copies.
* Removed the links inviting you to buy your own Anthropic and Voyage API keys. AI runs on Rooster One credits.


= 10.3 - 2026-07-30 =
* **Customers can see where a withdrawal stands.** Entering an order number and email now shows the stage — received, approved, items on their way back, refunded — and what happens next at each one.
* **How the refund was made is recorded and told to the customer**: back to the original payment method, bank transfer, store credit or cash.
* The guest withdrawal link now lasts **21 days** instead of an hour, and a lapsed one can always be re-requested from the form.
* **Greek admin fixes**: menu entries that rendered in English now translate, and five withdrawal strings that read "cancellation" say "withdrawal".
* Minimum WordPress is now 6.7.

= 10.2 - 2026-07-29 =
**Withdrawal declarations now notify the shop, plus mobile-navigation refinements and minor fixes.**

* A declaration that needs processing no longer ends at a private order note: an optional interim order status **"Withdrawal requested"** (off by default — mind anything that reads order statuses), and a **store email** built as a real WooCommerce email with its own recipients and overridable template. Both controlled from Operations → Withdrawal Right → Settings.
* Mobile navigator: **Categories | Filters** tabs at the top of the panel on archive pages, so categories are never buried below the filters.
* Subcategory chips: the mobile slider now shows the same progress affordance as the product carousels — an accent-coloured thumb over a thin black line running to the screen edge — and one theme override recolors both.
* Minor fixes: admin checkboxes that could be ticked but never unticked, Wide/Full block alignment on the chip strip, and dual-bundled class loading hardened against cold-opcache redeclare fatals.

= 10.1 — The withdrawal right, end to end, and ten international couriers (29/07/2026) =

* **The withdrawal button, as Directive (EU) 2023/2673 requires it.** In Greece that is Law 5317/2026, published 10 July 2026, which inserts article 3ζα into Law 2251/1994 — in force on publication, with no transition period. A permanent, prominent button; 14 calendar days counted from *receipt* rather than from the order; a separate confirmation step; partial withdrawal on multi-item orders; and an acknowledgement email on a durable medium recording the content, date and time of the declaration. It works for guests throughout: creating an account is offered afterwards and never required, because requiring one is precisely the obstacle the law prohibits. Ships disabled.

* **Cancellation and withdrawal are two flows, not one.** Cancelling an unshipped order is a commercial courtesy the shop grants on its own terms and may refuse once the parcel has left; withdrawal is a statutory right, for a window the shop does not set and cannot decline. Merging them is the commonest way a compliant-looking implementation fails. The instant-cancel courtesy is now settable in hours *and* minutes — 0 hours 15 minutes is a valid answer — or switched off entirely, for shops that would rather not advertise it.

* **An operator stands between the carrier and the customer.** Return vouchers pass through a new awaiting-approval stage: the operator picks the courier, issues the voucher, downloads the label, checks it, and only then approves — at which point it reaches the customer by email and in their account. Nothing carrier-generated reaches a customer unseen. The pickup address is editable (a customer can have the parcel collected from work); the destination is not, since carriers already know where the shop is. A label booked at a courier's own portal can be uploaded and shared exactly like an API-issued one.

* **Ten international courier drivers** — DHL Express, FedEx, UPS, USPS, Royal Mail, DHL Paket, Sendcloud, Colissimo, BRT (Bartolini) and Correos Express — each a real API integration on a shared transport layer handling OAuth, label decoding across PDF, PNG, JPEG, GIF and ZPL, and carrier-specific error extraction. Carriers that cannot bill a third party do not show a "who pays" control, because a choice that cannot change the outcome is worse than no choice. Three of them issue per-customer endpoints, so their base URL is a setting with a sensible default rather than something only a release can change.

* **Greek terminology corrected.** "Withdrawal" had been translated as «ανάληψη» — a *cash* withdrawal — on eighteen strings including the statutory button's own label, and elsewhere as «ακύρωση», which merged the two flows the law requires be kept apart. The term is «υπαναχώρηση».

* **The web-push opt-in banner no longer defaults to on**, and this update switches it off once. Turn it on in Operations → Web Push → Display if you want it; existing subscribers keep receiving campaigns either way.

* Fixed: admin submenus vanishing while the pointer crossed the gap towards them; withdrawal-queue stage counters losing their styling on empty tabs; courier settings being blanked rather than merged on save; and a DHL Paket billing number assembled from an incomplete customer number, which could be accepted against another account.

= 10.0 — Everything findable, everything described, everything correctly named (27/07/2026) =

* **Every screen in the plugin is now reachable from one keystroke.** All 617 addressable destinations — 157 pages, 25 in-page tabs and 435 anchored panels — answer to **Ctrl+K** from anywhere in the admin. Before this, a capability you had not personally been shown was a capability you did not know existed: the panels inside a page were reachable only by knowing which page held them and scrolling to find out.

* **Every page and tab says what you can DO there.** All 195 of them carry a written one-line description, shown on hover and in the navigator. This was not cosmetic work. "Attribution", "Operations" and "Set Up" are honest names that tell a new shop manager nothing, and the Home page lists 169 of them at once. Each line is written once and consumed four ways — the hover modal, the Home search index, the Ctrl+K keywords, and the Insights assistant — so a vague one costs four times and a wrong one teaches something false in four places.

* **Findable by the nouns you actually use.** Descriptions feed the search index, so a destination is reachable by what it deals in — «ΑΦΜ», "Omnibus", "packing slip", "disavow", "Skroutz" — and not only by its label. Measured before this existed: "redirects", "schema", "backlinks", "sitemap" and "swatches" all returned nothing on a Greek install, because the label was Greek and the index held nothing else. Worse, "swatches" surfaced Problematic Variations and "side cart" surfaced Checkout Layout — decoys outranking the namesake tab, which is worse than an empty result.

* **The plugin tells you what kind of site it thinks you are.** The admin header now shows your site profile — shop, catalogue or content — whether it was auto-detected or set by you, and warns when a manual setting no longer matches the evidence. One click to change it. Opportunity scoring, cornerstone selection, the demand score and which signals even apply all follow from that one choice, so having it invisible meant an operator could not tell why a number looked wrong.

* **The whole codebase now speaks English at source, and Greek to the reader.** 151 Greek literals across both plugins were converted to English source strings with the Greek preserved exactly as translations, so nothing a shopper reads changed. Duplicate translations of the same concept were merged onto single strings, and four checkout invoice fields in the runtime plugin had never been translatable at all — they are now. This is what makes any other language possible.

* **Fixes.** OpenAI was dropped as a provider in May, but the AI settings form could still store it: the dropdowns stopped offering it while the save handler kept accepting it, so a stale submission could switch a live site onto an unsupported provider. The AI credits check told operators the Content Connector was not configured even when it was — a message only reachable *after* the token authenticates; it now distinguishes "no Anthropic key on the account" from "AI service disabled", names the account and links to the portal. Admin hero copy no longer quotes one particular shop's catalogue size as if it were yours.

= 9.5 — Audience correctness, identity resolution, admin information architecture (26/07/2026) =

* **Segment and cohort exports no longer download empty files.** "Given an audience NAME, which customers are those" was answered in four places that disagreed. Both CSV exporters accepted segment names while filtering on the dormancy column alone, so exporting a segment produced a well-formed, successful, EMPTY file — 9 of 14 targetable audiences were silent no-ops, including every one named in the lookalike workflow's own instructions (VIP, Loyalist, Brand-Devoted). One resolver now answers for all callers, and it is the union of the four implementations rather than their intersection: exact JSON matching, bidirectional handling of the lapsed→overdue rename, a segment list derived from your data instead of a hardcoded one, and the `pure_intent` audience that previously existed in only one of them. Dormancy cohorts are unchanged.

* **Retroactive identity resolution.** Browsing sessions were only ever tied to a person as orders and logins happened, so a store installing the plugin started with a large backlog of past orders whose visitor was already known but never linked. A new backfill resolves those in one pass — from the admin (Data Pipeline → Identity resolution), from WP-CLI (`wp aieo identity backfill`), or on a daily schedule. It is safe to re-run at any frequency: it only looks at sessions that are not yet resolved, so it converges and then costs nothing. Three further resolution points were added (registration, WooCommerce customer creation, and checkout — a shopper who registers during checkout is auto-logged-in, so the login hook never fired for them).

* **Action Scheduler protection no longer corrupts unrelated queries.** The query guard matched the actions table by name ANYWHERE in the SQL, including as one value in a list. The plugin's own health probe lists every catalogued table in one lookup, so the guard truncated it to ten rows and the Signals screen reported 62 measurements as missing storage that demonstrably existed. It now matches the query's actual target table, and a bounded roll-up over a low-cardinality key is recognised as safe.

* **Admin navigation.** Data prep schedule and Product/Orders analytics were two screens answering one question and are now one page, Data Pipeline. Signals — the catalogue of what this install measures — moved out of an unrelated tab onto its own page, with a Re-measure control.

= 9.4 — Correctness hardening for the 9.3 price fast path (23/07/2026) =

* **Multi-currency detection.** `variation_price_filters_are_safe()` inspected only `woocommerce_variation_prices_price/_regular_price/_sale_price`. WooCommerce Multilingual's multi-currency hooks none of them — it uses `woocommerce_variation_prices` (the aggregated array, applied after the walk) plus `woocommerce_get_variation_prices_hash`, converting at its own layer. The gate therefore found nothing registered on a multi-currency store, concluded "safe" and served unconverted prices. The array filter is now part of the inspected set; verified that a WCML-shaped callback declines the fast path with coverage otherwise unchanged (947/1124, 0 wrong prices).
* **Cross-check against the variations.** The fast path trusted `wc_product_meta_lookup.min_price/max_price`. A full-catalogue comparison against the live aggregation found two products where that aggregate is wrong and, in both cases, LOWER than the real price: a parent row left stale by an import that bypassed `WC_Product_Variable::sync()`, and a variation whose `_price` is the string "24,99" (SQL `CAST` truncates to 24.00 while PHP parses 24.99 — WooCommerce's own lookup row is wrong there too). `lookup_variable_price_range()` now compares the aggregate against the `_price` of the product's visible children and declines on any disagreement.
* **European decimal handling.** That comparison normalises through `wc_format_decimal()` rather than `str_replace(',', '.')`, which would corrupt `1.234,56` into `1.234.56` on a store using a comma decimal separator and a dot thousands separator.
* **Third-party pricing extensions.** The fast path reads raw stored prices, bypassing anything hooked into the price pipeline. It now identifies every registered callback by name (as `CallbackUtil::get_callback_signature()` does) and proceeds only when all are allowlisted; closures and invokables have no stable name to match, so their presence declines. Filter: `aieo_dto_price_filter_allowlist`.

* **Fixed a latent no-op on `delete_term`.** `AIEO_DMM_Swatches::invalidate_on_term_edit()` resolved the term with `get_term()`, but `delete_term` fires *after* the row is removed, so it returned null and the handler exited early — deleting an attribute term invalidated nothing, leaving every product that used it with a stale swatch cache until its next save. The hook now takes WordPress's 4th argument (the already-deleted `WP_Term`) and uses its slug; the variations' `attribute_pa_*` meta survives the delete, so the affected parents are still found. Verified on the live catalogue: a deleted term's slug correctly resolves the 69 products that used it, where the previous code resolved none.
* **Removed the per-variation N+1 in term invalidation.** The handler called `wp_get_post_parent_id()` once per matching variation. Parents are now resolved in one indexed JOIN — 10.47 ms to 0.51 ms (20x) on the busiest term, with an identical result set.
* **Removed silent truncation.** The variation lookup carried `LIMIT 2000`, unordered and unpaged, so a term used by more products than that would silently drop the remainder. The lookup is now keyset-paged and always drains, so nothing is missed; page size is memory hygiene, not a cap.
* **Bounded the rebuild fan-out.** Term edits are recorded at hook time (no queries) and flushed once on `shutdown`, deduplicated across every term edited in the request, then enqueued as one `$unique` Action Scheduler job per affected product. An import editing thousands of terms therefore costs one lookup per distinct term and at most one pending job per product, instead of work per edit. The global settings-hash bust that caused the June cache-rebuild outage remains removed, and this path never reintroduces it.

Upstream: this hardening came out of review feedback on our WooCommerce core contributions — woocommerce/woocommerce#66890 in particular.

= 9.3 — Variable-product pricing performance & role-price cache correctness (22/07/2026) =

* **Fixed: professionals could see stale prices for up to 30 days after a role-price import.** WooCommerce's variable-price cache key recorded the viewer's roles but not the version of the role-price dataset, so a CSV import did not supersede cached ranges until each product was next saved. The dataset version is now part of the key (`aieo_role_prices_v`, bumped from every role-price write path). Public/guest cache entries do not carry the factor and are left intact.
* **Cut needless variable-price cache entries for logged-in customers.** `modify_variation_hash()` keyed on every role a user held, while the price filter only acts for the configured professional roles — so ordinary `customer`, loyalty and newsletter accounts each minted a private cache entry byte-identical to the guest one, each costing a full aggregation of every variation. The key now intersects the viewer's roles with the enabled set and leaves the hash untouched when the intersection is empty, so those visitors share the guest entry.
* **Recommendation/side-cart card DTOs no longer aggregate every variation.** `build_clean_price_html()` now reads `min_price`/`max_price` from `wc_product_meta_lookup` (which WooCommerce already maintains and indexes) instead of walking the variations. Deliberately conservative: it falls back to the full aggregation for logged-in visitors (role pricing rewrites per-variation prices) and on tax-enabled stores (the stored values are raw). Filterable via `aieo_dto_use_meta_lookup_price_range`. Applied in both the admin and runtime plugins.
* **Product labels and Product structured data now use display-context prices.** Discount percentage/amount labels and the `AggregateOffer` `lowPrice`/`highPrice` were computed from raw variation prices; on a tax-inclusive store both understated the figures, and both requested the cache variant WooCommerce had not warmed, forcing a second full aggregation. They now pass `$for_display = true`.
* **`AIEO_DMM_Settings::get_all()` is memoised per request.** The defaults/stored merge ran on every `get()`; `current_user_is_pro()` calls it once per price-filter invocation, so a cold aggregation of a 187-variation product rebuilt the whole settings tree several hundred times. The memo is invalidated by `update()`, `replace()` and by the `update_option_`/`add_option_`/`delete_option_aieo_dmm_settings` hooks, so writes from outside the class are also covered.

Background: this work came out of our contributions to WooCommerce core on variable-product price performance — woocommerce/woocommerce#37629, PRs #66882 and #66889, issues #66883, #66890 and #66892.

= 9.2 - 2026-07-21 =
**Analytics and nightly jobs now repair their own database routines.**

* **Fixed: nightly jobs could fail forever after a site restore or migration.** Database backups exclude stored routines unless explicitly asked for, so restoring a site could silently drop the procedures that power surveys, web-push customer intelligence, product needs and session analytics — while the plugin still believed they were installed. The affected jobs then failed every night with "PROCEDURE … does not exist", and reinstalling or reactivating the plugin did not help, because the check was based on a stored version number rather than on whether the routines actually existed.
* **Now self-healing.** Each job detects a missing routine at the moment it needs it, reinstalls it and retries once. This costs nothing on normal page loads — there is no extra check on storefront requests — and it repairs itself inside the nightly job or an admin action instead of needing manual intervention.
* Covers the survey ingest/intelligence procedures, the web-push customer-intelligence recompute and segment assignment, product-needs population, and the session-analytics aggregation, ranking, summary and cleanup routines.

= 9.1 - 2026-07-20 =
**Stability release — three fixes worth updating for, one of them urgent.**

* **Fixed: a fatal error that could break logging in.** On sites using the identity/attribution layer, the `wp_login` handler called an internal helper it wasn't allowed to reach, which raised a PHP fatal during sign-in — affecting the standard login form, "My account", and passwordless/magic-link sign-in. If you were seeing 500s on login, this is the fix.
* **Fixed: AI credit balance over-reported when you held a top-up.** The remaining balance added your top-up on top of the monthly cap, but the cap already includes it — so the figure could read as much as double. This under-fired the low-balance alert and let the assistant hit its limit while the meter still showed credit. The credit meter now reads "incl. $X top-up" rather than "+ $X" to match how the cap is actually composed.
* **Removed a latent fatal on the paid-order path.** The Meta CAPI purchase-event builder contained an unreachable branch that would have raised the same class of fatal as the login bug if ever taken. Removed rather than left as a landmine.

= 9.0 - 2026-07-20 =
**The plugin is now Rooster One.**

* **New name.** "AI eShop Optimizer" is now **Rooster One** (the front-end companion is "Rooster One — Runtime"). Only the display name changes: your settings, data, shortcodes and block names are all untouched, and updating is a normal plugin update with nothing to reconfigure.
* **One checkout newsletter opt-in instead of two.** A single box now registers new subscribers, with per-list control for shop managers, and customers manage newsletter and offers independently from their account.
* **Tidier checkout birthday field** — day, month and year sit on one row with a full-width explanatory note and a configurable highlight colour.
* **Accessibility fixes** — corrected ARIA usage on rating stars and gave promo and product-card links proper accessible names.
* **Faster perceived loading on category, page and blog pages** — the above-the-fold hero image is now prioritised, improving Largest Contentful Paint. Below-the-fold heroes stay lazy.

= 8.7 - 2026-07-13 =
**Block-library discoverability, plus a clearer block name.**

* **More native blocks now surface in the block editor and on the plugin page.** Recommendations, Product Slider and Product Variations Gallery gained a block-editor registration (they were front-end-render-only before), so they now appear in the inserter with a labelled placeholder and are listed among the blocks this plugin provides. Their front-end output is unchanged.
* **The full block library is documented** in the plugin description — brand, product, local-business (Google Reviews, Store Locator / Map, Opening Hours, Business Info…), content and archive blocks, grouped by purpose.
* **"Post Related Categories" is now "Post Related Product Categories"** to make clear it lists related WooCommerce product categories, not blog categories.

= 8.6 - 2026-07-13 =
**Your admin chat now fixes SEO for you — plus Google Search Console, backlink & disavow tools, a sharper and more relevant storefront assistant, lower AI/embedding costs, and a faster engine that shrugs off aggressive crawlers.**

* **Sales predictor rebuilt to run in seconds, not minutes.** The backtest accuracy step and the per-batch variation build joined the full order-line history on a computed key (`COALESCE(variation_id, product_id)`) that no index could serve, forcing MariaDB into full×full block-nested-loop scans of ~2–5 billion row-pairs per run (300–500s each). Order-line analytics now carry an indexed `effective_pid` column, the backtest is pre-aggregated once instead of a correlated per-row subquery, and every freshly-rebuilt staging table is `ANALYZE`d before it is joined — so the optimiser always picks an indexed nested-loop. Output is byte-for-byte identical (verified across every history row on live data); the nightly job just stops churning the database into swap.
* **Same fix applied across the physical-recovery and monolithic predictor paths.** The five stock-recovery-outcome joins and the legacy monolithic predictor now use the indexed `effective_pid` too.
* **Customer cumulative order-stats no longer stall.** The per-customer lifetime spend/items rollup was a `customeremail` self-join that flipped to a quadratic multi-minute scan whenever the underlying order table had just been rebuilt (stale statistics). It is now a single pre-aggregate pass — structurally impossible to flip — with fresh statistics forced before it runs.
* **Product Data Prep (vitals) rebuilt for speed and stability.** The nightly Data Prep run that computes every product's vitals — the metrics behind recommendations, badges, swatches, archives and analytics — could flip into multi-hundred-second full-table scans right after a staging table was rebuilt, because stale table statistics made MariaDB mis-plan the joins (the classic "n_rows reads as ~1" trap). Every vitals stage now refreshes its statistics and pins the correct index with typed join keys before it joins, so it stays on a fast indexed path; a built-in circuit breaker caps any single query's temp-table size and run time, and a plan-flip detector flags a regressing stage instead of letting it churn the database into swap. Identical output, a fraction of the time — and no more midnight swap spikes on large catalogues.

* **Sitemap & robots.txt scheme fix.** The XML sitemap and managed robots.txt are built offline (cron / WP-CLI), where WordPress cannot detect that the live request is HTTPS. On a site served over https but whose stored WordPress Address is http:// (the classic Cloudflare "Flexible" SSL / http-origin proxy setup), every sitemap `<loc>`, image URL and the robots.txt `Sitemap:` line could be written as http:// — so search engines were handed http canonicals for an https site. Every same-site URL emitted by the sitemap and robots.txt is now normalised to https (overridable via the new `Rooster One_seo_sitemap_scheme` filter for a genuinely http-only origin).

* **SEO Auto-Fix Assistant, in your admin chat.** Ask the Rooster One Insights chat to "fix my top pages" and it now works in two tiers, free before paid. **Free (no AI credits):** it sets the canonical URL and fills the focus keyphrase from the page's own top-ranking Google keyword — structural fixes that need no copywriting, applied instantly and re-scored, with one-click undo; it can also clear an accidental noindex on request. **AI credits:** the content-writing fixes — a SERP-aware SEO title + meta description, descriptive alt text for images missing it, and richer body content (drafted for review; approving saves a WordPress revision so you can roll back) — are written by the AI, drafted for your review by default. The assistant always tries the free fixes first and tells you the credit cost before writing across more than a few pages. A nightly free Autopilot can keep the structural fixes flowing (off by default, opt-in).

* **Google Search Console — your true traffic (new).** Connect Search Console (SEO → Search Queries) and see the real queries and pages that bring you Google organic clicks, impressions, CTR and average position — straight from Google — beside the DataForSEO ranked-keyword view. High impressions with low CTR flag a title/description the AI can rewrite.

* **Backlinks & toxic-link disavow (new).** SEO → Backlinks pulls your referring domains and their spam score from DataForSEO, flags the toxic ones, and generates a ready-to-upload Google disavow file — kept in sync as new bad links appear. (Google provides no API to submit a disavow, so you upload the file once at the Search Console Disavow Tool; we link you straight there.)

* **Content ideas from your customers (new).** The questions shoppers ask your storefront AI that it answers poorly — no matching products, or a low rating — are distilled into a demand-ranked list of content gaps, so you know exactly what to write next. Your customer assistant only surfaces the signal; it never changes your site.

* **Storefront AI assistant — more relevant answers, fewer wrong turns.** The product cards shown alongside a chat answer are now always tied to what the shopper actually asked, with a relevant-product fallback so the assistant never pads a reply with off-topic links. It understands your own Table Rate Shipping rules when answering delivery questions, and a configurable recency boost lets newer arrivals win when a shopper asks "what's new" or browses a fast-moving category (set your "new product" window under Merchandising → Badges). Answers also run to full length by default for more complete replies.

* **Lower AI costs — the semantic index only embeds what changed.** Building your recommendation & search index now embeds only the products, posts and pages that are new or have changed since the last run, instead of re-embedding the whole catalogue every time — a large saving on embedding spend for big or frequently-updated stores. (Switching the embedding model still rebuilds everything, as it must.) Product text is read from the pre-computed product vitals first and only reaches into the full content for the description, so each run is faster too — and the automatic nightly re-embed schedule now runs reliably.

* **Faster and more resilient under heavy crawling.** A few storefront helpers used to make a small uncached background request on every page view — the swatch add-to-cart nonce refresh, recommendation-impression logging, and the free-shipping progress bar's cart check. Under an aggressive SEO crawler those could multiply into a needless server-load spike. They now fire only on genuine shopper interaction (or only when there is actually a cart to sync), and automated JS-rendering crawlers are skipped outright — no change to what shoppers see or do, just no wasted load from bots.

* **Cookie scanner: off by default, on demand instead.** The consent banner's live per-visitor cookie-discovery sweep is now off by default. Your cookie & third-party-service inventory is built from the built-in database plus the on-demand "Re-scan storefront now" button (Consent → Cookie inventory), which loads your key pages in the background whenever you add a new service — so nothing scans on every visitor. Continuous live discovery can be switched back on any time.

= 8.5 - 2026-07-05 =
**SEO grows beyond the shop: site profiles for content & catalogue sites, Google-data-driven AI titles, conversion goals, campaign & exit-page analytics, and complete attribution in your order and form emails.**

* **Site profiles (new).** Tell Rooster One what your site IS — shop, catalogue (products presented as content, without checkout), or content/publisher — or let it auto-detect. Every SEO metric adapts: opportunity scoring, pillar pages and demand now work properly on content and catalogue sites instead of assuming a shop.
* **Content vitals (new).** The per-product health metrics gain a content twin: every post, page and custom type gets daily reach, engagement, freshness and demand scores — including reader time-of-day and day-of-month consumption patterns, the same lens the shop side has for buyers.
* **DataForSEO integration (new).** Connect a DataForSEO account per site (SEO → General) and Rooster One pulls a weekly snapshot of every Google keyword you rank for, computes striking-distance opportunities (positions 8–20 by search volume) and shows them on the Opportunities card — under a monthly spend cap you control.
* **AI titles & descriptions with live SERP context (new).** A nightly queue drafts new SEO titles and descriptions for your highest-ROI pages using the actual Google results for the target keyword — written to win the click against the competitors really shown there. Drafts wait for approval on SEO → Health, or switch on Autopilot.
* **Conversion goals (new).** Choose which Gravity Forms (plus the Newsletter plugin) count as conversions per site. Rooster One's AI reads each form and derives its goal context, which feeds the SEO descriptions of the pages hosting that form — and goal conversions flow into content demand and the new campaign analytics.
* **Campaign attribution in form notification emails (new).** Gravity Forms staff notifications can include the full campaign story — channel, all five utm parameters, landing page and original referrer — with no hidden form fields or per-site code snippets. Visitor autoresponders are never touched, utm form fields auto-prefill, and the page URL sent to HubSpot is cleaned of tracking parameters.
* **Order attribution emails, completed.** The "Order origin & attribution" block in staff new-order emails now always lists all five utm parameters — and it now also appears on orders paid by cheque, bank transfer or COD, which previously missed it on speed-optimised checkouts.
* **Landing pages & campaigns, canonicalised (new).** Session analytics now parse every visitor's landing page into a clean path plus utm parameters (per-click junk such as fbclid is dropped), with a durable per-day campaign × landing-page rollup: sessions, cart adds and goal conversions per campaign.
* **Exit pages (new).** See where sessions end, split by whether the visitor converted first — separating natural end-of-journey pages from the pages that bleed visitors.
* **Ask the insights chat.** New chat skills: ranked keywords, the striking-distance list, the content-vitals leaderboard, landing-page campaigns and exit pages.
* **Redirect import safeguards.** Imports now verify that every redirect target exists on THIS site and refuse any rule that would create a redirect loop at any depth.
* **Cookie consent: position the floating button per device.** The floating "Cookie settings" button can now be placed Left, Centre or Right with exact pixel offsets from the edge and the bottom — separately for desktop and mobile — so it aligns cleanly with other floating widgets (chat bubbles, scroll-to-top). When the Side Cart bottom navigator is active, its coordinated styling automatically takes precedence on mobile.
* **Search Console fixes.** Paginated pages emit correct self-canonicals and sensible robots defaults, addressing "Alternative page with proper canonical tag" and "Duplicate without user-selected canonical" warnings on content sites.

= 8.4 - 2026-07-04 =
**A media-library cleanup tool, a per-user landing page that can resume where you left off, an admin usage dashboard, a managed robots.txt, and more resilient background jobs.**

* **Media Cleanup (new).** A Tools & Settings tool that reclaims disk space by finding and removing image files your site no longer needs — thumbnail sizes your current theme doesn't register, true orphans with no media-library record, and images left behind by products you deleted. It scans in resumable batches (built for libraries with hundreds of thousands of files), moves everything it flags to a recoverable quarantine first, and only deletes for good when you choose to empty the quarantine — with removing the matching media-library records kept as a separate, off-by-default opt-in. A file is only ever touched when nothing on the site references it anywhere — product galleries, variation swatches, brand logos, page/post content, widgets, menus and theme options are all checked, WebP and AVIF included — so an image still in use is never removed.
* **Choose where you land — or pick up where you left off.** A new My Settings card lets each user set which e-shop Manager card the page opens on, instead of always landing on the same default. Pick any card, or choose "continue where I left off" and Rooster One returns you to the last card you were working in.
* **Admin usage dashboard (new, administrators only).** A Tools & Settings panel, visible only to site administrators, that reports how the Rooster One admin is actually used — page views, active users, sessions and most-opened cards — in total and per user, over a time window you select. Lightweight, self-pruning (kept for 180 days), and never shown to non-admins.
* **Managed robots.txt (new).** The built-in SEO engine can now manage your robots.txt alongside the XML sitemap: it serves a correct, sitemap-referencing robots.txt (honouring "Discourage search engines"), lets you add your own custom rules from SEO → Redirects, and writes a static file at the web root so your server can serve it directly. It only ever acts when Rooster One SEO is switched on and no other SEO plugin (Yoast, Rank Math, AIOSEO) is handling robots — and it never overwrites a robots.txt it didn't create.
* **More reliable background jobs.** The protection that stops a mis-behaving recurring task from piling up thousands of duplicate scheduled actions now covers the full set of AutomateWoo jobs and WPForms' spam-purge and form-locator tasks — not just log cleanup — so low-traffic sites keep a clean, fast Action Scheduler queue.
* **Fixes & polish.** The image picker on the SEO → Schema and Social cards now opens reliably (it previously failed to attach on pages where the media library loaded late). Greek translations refreshed, including the Amazon Selling-Partner catalogue and order-import strings.

= 8.3 - 2026-06-26 =
**One-click product import from your marketplaces, an AI-chat analytics dashboard, and a single unified admin navigation.**

* **Catalogue Importer (new) — import your products from eFood, Etsy, Wolt and Amazon.** The mirror image of the marketplace catalogue exports: enter your marketplace API keys, choose a source, and Rooster One recreates the products in your store with one click — auto-building the category tree, product attributes and food option-groups each product references, sideloading its images, and matching on SKU so a re-run updates products in place instead of duplicating them. Imports as Draft + new-only by default (each a per-run toggle, alongside Create + Update and Update-only). A new **Enrich** mode fills gaps on existing products without overwriting anything you already have — so you can, for example, create products from Wolt (for their ingredient / topping option-groups) and then enrich from eFood or Amazon to fill in barcodes. Amazon reads your own Seller listings through the Selling Partner API (no extra AWS keys needed); eFood, Etsy and Wolt use their catalogue APIs.
* **AI Chat Analytics (new, Pro).** A Customer-Profiling dashboard that turns your AI chat history into insight: a searchable conversation log plus aggregate metrics — which page each conversation was opened from (read from the existing Session Tracker, not a second tracker), question volume and topics, the satisfaction ratings customers leave, and chat-assisted conversions (orders that followed a chat, precomputed and cached the same way Competition conversions are — never calculated live on page load). Conversation-history retention is configurable, 90 days by default.
* **Unified admin navigation.** The three Rooster One admin pages — Analytics & Shop Recommendations, e-shop Manager and AI Chat — now share a single menu bar that highlights the section you're in, replacing the old top tab-strip and the separate per-page tables of contents. New "Set Up" and "AI Chat" menu groups deep-link straight to each page's individual cards, so everything is one click away from anywhere.
* **Quick Navigator finds features by what they do.** The Ctrl/Cmd-K Quick Navigator now matches synonyms and capability terms as well as names — so "amazon", "barcodes" or "import" jump to the Catalogue Importer and "happiness" or "sentiment" to AI Chat Analytics — and cross-page results now show the correct section badge.
* **Fixes & polish.** The SEO → Redirects "Rebuild SEO index" card now remembers the object types you last rebuilt (Posts / Pages / Products / Attributes as well as the taxonomies) and pre-ticks them on your next visit, instead of always resetting to the terms-only default.

= 8.2 - 2026-06-25 =
**A built-in FAQ manager, a new "Custom Post Types" admin section with a unified Store Pages panel, scheduled Google-review refresh, Courier Center end-of-day reconciliation, more reliable controllable background jobs — plus an opt-in Food & Beverages product-options toolset and new marketplace connectors (catalogue export + order-taking) for eFood, Wolt, Etsy and Amazon.**

* **Built-in FAQ manager (new).** A self-contained FAQ system — an FAQ content type (title = question, answer in the block editor) with category-style FAQ groups, managed from a dedicated admin panel where you create / rename / delete groups, add and edit questions, and drag-and-drop to reorder or move a question into another group (everything saves instantly). Drop the `[faq]` / `[Rooster One_faq]` accordion shortcode on any page — optionally scoped to one or more groups (`group="shipping,returns"`), rendered as titled per-group sections (`sections="yes"`), or limited to specific entries; a bare `[faq]` lists every published Q&A. Self-contained, theme-agnostic markup with its own styling.
* **New "Custom Post Types" admin section.** The horizontal admin navigation gains a group that gathers the editorial content types in one place — FAQ, Events, Competitions, Locations & Reviews, and Store Pages. Events and Competitions moved here from Customer Profiling and Locations & Reviews from Catalog Sync (same panels, clearer home), and the store-location, store and FAQ post types no longer add their own clutter to the WordPress sidebar.
* **Store Pages panel.** A single screen listing every store / location page on the site — both the theme's store pages and the plugin's store pages — with type, bound Google location, status and Edit / View links. The store-page settings (enable, URL base, labels) moved here from SEO → Schema, so configuration and the page list now live together.
* **Scheduled Google-review refresh.** Reviews used to update only when you clicked "Fetch all", so they silently went stale; they now refresh automatically on the cadence and run-time you set (daily by default), keeping each location's rating and review snippets current. The refresh arms itself only when at least one location has a valid Google API key and place ID — a site that never configured reviews never makes a single Google API call — and it respects per-hour / per-day quota caps (defaults 20/hour, 500/day). The manual "Fetch all" button ignores the caps so an operator-initiated run always completes.
* **Courier Center end-of-day reconciliation.** Courier Center (Qualco "Deliverd") has no separate close API, so Rooster One now reconciles each day through its official `/Manifest` pickup list — the carrier's end-of-day close — and stores the returned manifest PDF as the closing document. "Close & print", the batch "close pending now", the daily auto-close, and "reprint last closing" now self-route by carrier: Geniki uses its close API, Courier Center uses the manifest, and carriers with no end-of-day API get a local manifest close — so every carrier is reconciled, not just Geniki (a per-carrier close on a non-Geniki courier previously failed with a "does not support close_pending" error).
* **Smarter background jobs.** Scheduled jobs no longer do pointless work on installs that haven't set the feature up yet: order-cancellation metrics recompute only on Pro, courier tracking and auto-close run only once you've created a shipment, the monthly AI-model refresh wakes only when your Rooster Content Connector token is set, and session-analytics rollups run only when session tracking is switched on. Fresh sites stay quiet until the relevant feature is actually in use.
* **More control over the always-on jobs.** A new "Automatically capture abandoned carts every hour" toggle lets you pause the hourly capture from the Saved & Abandoned Carts screen (it saves the moment you flip it), and session analytics honours the existing tracking switch — while the manual "Capture now" and "Rebuild now" buttons still run on demand even when the automatic job is off.
* **Self-healing schedules.** If a plugin update or a wiped cron table ever drops a scheduled job, Rooster One now quietly re-creates it — your automated marketplace product feeds and AI analytics / embeddings updates keep running instead of silently stopping, with no need to re-save settings. Only jobs you have already enabled are re-armed.
* **Much faster Competitions tab.** The Competitions admin page used to rebuild its entrant-revenue and Insights figures on every load — a heavy cross-table calculation that made the page take several seconds to open. Those figures are now precomputed in the background, so the page renders them instantly, with an **Auto-refresh** control (Off / Hourly / Daily / Weekly / Monthly, default Daily), a manual **Refresh** button, and an automatic background update shortly after each new entry.
* **Food & Beverages: product options & modifiers (new, opt-in).** A toolset for food and made-to-order products, off by default behind a single "Food & Beverages" switch (Custom Post Types → Food & Beverages) so shops that don't sell food never see any of it. Build reusable option groups — sizes, toppings, with / without extras — and attach several to one product at once from its new "Options" tab. Each group is single- or multi-select with min / max counts (so a shopper can pick more than one in a single go, the way the colour-variation multi-add works); each option carries its own price, with support for a free allowance ("first 3 toppings free"), "always charged" premium options, and prices that vary by size or per product. Shoppers choose on the product page with a live running total and sold-out options flagged immediately; the selection carries through to the cart, checkout and the saved order.
* **Catalogue & menu exports for eFood, Wolt and Etsy.** Three new templates on the Marketplace Feeds page export your catalogue in each platform's own JSON shape — eFood products, Wolt menu items, and Etsy listings (with per-shop who-made / when-made / taxonomy / shipping-profile defaults you set once) — alongside the existing Skroutz, Shopflix and generic CSV / XML feeds.
* **Order-taking from eFood, Wolt, Etsy and Amazon.** Four new connectors in the Marketplace Order Importer pull orders straight into WooCommerce — Etsy (Shop Receipts) and Amazon (Selling Partner API) by polling, eFood and Wolt by signed webhook. Every marketplace has its own on / off switch, and the 5-minute background poll only runs while at least one polling marketplace is actually connected, so it never burdens a store that isn't using it.
* **Fixes & polish.** The account "Communication" page no longer rewrites every main-menu label to "Communication"; the My Account login / register page no longer shows the social sign-in buttons twice (they render once, inside each form); the Brand Story block's opening heading reads "About the Brand"; the redundant "Products" sub-heading was removed from the brand and category archive layouts; and the Navigator's category highlights (the middle column shown when you select a category) now order the Brands and Needs blocks to match your Navigator → Group order, instead of always listing Needs first.

= 8.1 — Events (Beta) & Competitions, a richer storefront block library, storefront polish (20/06/2026) =

* **Events (Beta).** A complete event-management module modelled on Competitions: an Events CPT with a participants table, XLSX/CSV participant import (update-vs-overwrite), tokenised one-click RSVP confirm/decline, parametric newsletter invitations through The Newsletter Plugin ({salutation}/{title} merge, BUILD + dry-run only — never auto-sent), front-end self-registration via a new `aieo/event-registration` block and `[aieo_event_register]` shortcode, a Google Maps location URL, quick-create JS, and an attendance stats panel with XLSX export. Admin-plugin only.
* **Competitions.** A competitions / giveaway module sitting alongside Events.
* **AIEO Product Slider block.** A horizontal product slider with the exact look of the product-page recommendation rails (thin uppercase title + underline, then cards) — manual title, filtered by category, tag and/or a specific set of product IDs. Replaces the legacy OM `[responsive_product_slider]` shortcodes.
* **AIEO Category Footer Hero block.** Lays a product category's SEO footer text beside the category image (image left, small rich text right) with the hero divider line cutting across the image — the horizontal counterpart to the category hero, for the bottom of the archive.
* **AIEO Trust Badges block.** A configurable trust strip of icon + title + subtitle columns (shipping / payment / returns / quality), with four bundled icons.
* **"Feature heading" opt-in for blocks.** The Brand Slider, Google Reviews and Latest Posts Overlay blocks gained a toggle to render their heading in the big thin-uppercase + full-width-underline style the product sliders use — inherited from one source so it never drifts, with the underline contained to the body width on the constrained blocks.
* **AIEO Latest Posts Overlay.** Now supports an optional section heading (with the feature-heading style) and a "glass" card style — tall portrait cards with a frosted-glass info bar — reproducing the legacy live blog layout.
* **Blog category archives.** A redesigned blog (sub)category header: a featured latest-post hero with the category-hero look, a subcategories dropdown, and a category lead, applied across every blog category and subcategory.
* **Product archive cards.** Evened the QUICK VIEW ↔ ΑΓΟΡΑ spacing between variable and simple products (the WC variation-form chrome no longer adds height above the buy button on image/colour-swatch products), tightened the variation swatches, and centred the buy-button label.
* **Side-cart.** The "You might like" recommendations sidebar now hides itself entirely when there are no recommendations (or no seed product) instead of showing an empty pane — the cart takes the full panel width.
* **Greek translations.** Customer-facing recommendation rail titles reworded from back-office jargon ("cross-sells / up-sells") into natural shopper language, plus footer trust-badge copy.

= 8.0.1 — Promo Surface full-width + single consent control (16/06/2026) =

* **Promo Surface "Full width" now bleeds edge-to-edge when nested.** The `aieo/promo-surface` block already forced a full-bleed when it was a direct child of the theme's constrained root, but on a *programmable* front page (where the page is rendered through `wp:post-content`) the surface sat one level deeper and kept its parent's root-padding-inset width, leaving a side gap. The full-bleed rule now sets an explicit `width:100vw` (not just `max-width`), so the surface fills the viewport regardless of nesting depth.
* **One cookie button, not two (consent control de-duplicated).** The first-party CMP rendered its own floating "reopen" button AND the side-cart bottom bar rendered a separate bespoke cookie item — two controls opening the same modal (a duplicate on mobile, and the bar one leaked onto desktop). They are now a single control: the side-cart bar's consent item reuses the CMP's `.aieo-cmp-reopen` styling (the themed floating circle on desktop, via `AIEO_Consent::reopen_button_vars()`) and flattens into the bar on mobile, while `AIEO_Consent::render_reopen()` suppresses its standalone button whenever the bar carries it (`AIEO_Side_Cart_Display::consent_button_active()`). No duplicated markup; styled per breakpoint. (Shared `class-aieo-side-cart-display.php` updated in both the admin plugin and `ai-eshop-optimizer-runtime`.)

= 8.0 "Fable / Mythos 5" - marketplace-feed URLs + an attribute-driven product-colour pipeline for SEO and feeds (14/06/2026) =

* **Absolute marketplace-feed links.** Products with an SEO canonical were being exported with a relative `<g:link>` (no domain), so Google / Skroutz silently dropped them. The catalogue build now absolutises the canonical into the core-vitals product URL, with a defensive absolutise at feed output; the sitemap and the on-page canonical tag were already correct.
* **Product colour from your attributes.** A new "AIEO colour context" flag (Products -> Attributes, with a one-click bulk action) marks which swatch attributes represent a colour; the catalogue build reads the term NAME (e.g. "French Caramel") into the product colour used by the JSON-LD schema and the marketplace feeds, as real per-variant values rather than slugs. Completes the 7.4 colour follow-up (parents stay clean: Google carries colour on each variant plus variesBy:color).
* **Platform.** Asset-minification system (minified CSS/JS served automatically), an SEO opportunities scorer + module, session analytics, the Courier Center fulfilment driver, and SQL runtime helpers. Requires-PHP floor set to 7.4.

= 7.4 Merchant-listing & Local SEO schema completeness — clear Google Search Console's "missing field" warnings across Product and LocalBusiness, plus a clean Channel view for attribution. Now requires WordPress 7.0+ (11–12/06/2026) =

* **Product `color` on every product.** The Product / ProductGroup schema now ALWAYS emits a `color` property, read straight from the core vitals colour (the single source of truth). Variable products carry real per-variant colours (and `variesBy`); simple products emit the field present-but-empty until you designate a colour-denoting attribute to populate vitals — a guided follow-up documented in `documentation/SEO_SCHEMA_NEXT_OPTIMIZATIONS.md`. No data keys were faked: the empty value is honest, and fills in automatically once vitals colour is populated, with no further code change.
* **Shipping & returns on every product Offer.** A new "Returns & shipping" card in the SEO → Schema tab emits `OfferShippingDetails` + `MerchantReturnPolicy` on each Offer — return window (days) + free-returns flag + country, and shipping rate + handling/transit-time range + country. Both are OFF until you enter values, because wrong terms trigger Merchant Center issues; once on, they apply site-wide to every product and variation.
* **`productGroupID` "invalid string length" fixed.** A long slug-style parent SKU exceeded Google's ~50-char item-group-id limit and was rejected; the group id now falls back to the stable numeric product ID when the SKU is too long.
* **LocalBusiness `priceRange` + `image` per store.** The Locations & Reviews location editor (Schema extras) now offers a Google-recognised price-tier selector (€ / €€ / €€€ / €€€€) and a WordPress media-gallery image picker with preview. Both persist per location and feed each store's `HealthAndBeautyBusiness` node, clearing the "missing image / priceRange" Local-SEO warnings across every page that carries the store graph.
* **UTM Explorer — new "Channel" dimension.** A top-level breakdown by the RESOLVED marketing channel, with consolidated friendly labels — paid Google cleanly separated from Organic Search (organic + organic_search merged), plus Facebook – Paid, Direct, Skroutz, Instagram, Phone, etc. The Source view additionally relabels paid traffic so Google Ads no longer hides inside the organic `google` bucket; raw `utm_source` is preserved underneath. Labels are display-only — the stored channel keys other modules match on (ROI Wizard, ad connectors, Enhanced Conversions) are untouched.
* **Attribution accuracy.** Order analytics (`wp_aieo_order_stats`) now bucket by the site's configured WordPress timezone instead of UTC, so AIEO's daily order counts/revenue match WooCommerce Analytics day-for-day; and the self-referral "internal" channel folds into the standard "(direct)" bucket.

= 7.3 In-house SEO engine — replace AIOSEO/Yoast with a faster, Greek-aware engine: rich commerce + local-business + image schema, redirects, a static sitemap, demand-aware scoring + Greek AI Assist, and an AIOSEO migration with a pre-cutover verify — all served from materialized data at ~0 ms (09–10/06/2026) =

* **A complete on-page SEO engine, folded into AIEO.** Title, meta description, canonical, robots, Open Graph and Twitter cards plus the JSON-LD `@graph` are now AIEO's to emit — built ONCE into a head cache and served by the thin runtime in a single cached read. No per-request assembly, no Vue admin SPA, no telemetry, no licence calls: ~0 ms added TTFB versus the 100–300 ms AIOSEO/Yoast/RankMath add per render. It runs **augment-only behind your current SEO plugin** (changes nothing on the page) until you turn it on, so 7.3 is safe to deploy ahead of any cut-over.
* **Canonical category, one resolver.** Pin one canonical category per product (the AIOSEO "primary category" mechanism) and a single resolver drives the **permalink, the `<link rel=canonical>` and the sitemap entry** in lock-step — they can never drift apart. A "SEO" editor metabox offers the canonical-category picker (plus title / meta / canonical override / robots), and a chunked bulk tool sets it across a whole catalogue.
* **Rich, correct commerce schema — entirely from the core vitals table (no live WooCommerce queries on the build path).** Variable products now emit a `ProductGroup` with `hasVariant` / `variesBy` so Google surfaces the variants instead of collapsing them into a phantom single item; on-sale products carry a `UnitPriceSpecification` strikethrough (the "was" price); plus `gtin`, `Brand`, and operator-configured `MerchantReturnPolicy` + `OfferShippingDetails` (off unless they reflect your real terms). Category, brand, attribute and tag archives are first-class landing pages too — `CollectionPage` + a product `ItemList` + `BreadcrumbList` + `inLanguage` — and their canonicals are the pretty `/product-category/…` URLs (a query-string-canonical bug from early builds is fixed).
* **Local SEO for physical stores.** A `LocalBusiness` node per shop, built from your Locations & Reviews Google locations — address, opening hours, geo, "Open in Maps" / "Rate us" links — linked under the site Organization, with one location pinned as HQ. Three new storefront blocks (**Store Locator**, **Opening Hours**, **Business Info**) render the same data anywhere, and an optional **store-page post type** lets each shop have its own page; you choose its URL base and label (nothing is hardcoded), and store pages get their own sitemap entries. Stars are never self-emitted (they stay on your Google Business Profile, per Google's policy).
* **Image SEO (no AI, no credits).** Empty product featured-image alt text is auto-filled from the product title (or your imported format) — only empties, so any alt you or AIOSEO already set is preserved — and one `<image:image>` per product is added to the sitemap for Google Images.
* **Redirects with an automatic slug-change monitor.** Rename a product or post and the old URL keeps working (an automatic 301), so the canonical/permalink changes at cut-over never 404. A clean unique-404 list (deduplicated, with hit counts) offers a one-click "Create redirect". Served at the nginx edge before PHP where available, with a PHP `template_redirect` fallback for portability.
* **A static, outage-proof sitemap.** Cron-built to static `.xml` files — chunked into shards of ≤2,000 URLs plus a sitemap index — and served as files. It keeps serving even if the plugin is deactivated or broken (no repeat of the "two weeks with no sitemap" failure mode).
* **Demand-aware SEO score, health dashboard & Greek AI Assist (day one).** A live 0–100 score with a checklist in the editor, a "SEO Health" dashboard that lists the **highest-demand** low-scoring pages first (not alphabetical), and one-click Claude **AI Assist** — generate/improve title, meta description, summary and keyphrases — fluent in Greek from the start.
* **AIOSEO → AIEO migration engine + a pre-cutover verify.** A read-only importer copies your **entire** AIOSEO configuration — per-object meta, global settings (separator, Organization, social, robots, per-type/taxonomy formats), the image-alt format, and redirects — into AIEO storage, with a mapping report that lists every field as mapped / transformed / dropped (deprecated **FAQPage/HowTo schema is stripped**, not migrated as zombie markup). Then a one-click **"Pre-cutover verify"** compares the head AIEO would emit against your LIVE head over a sample of products, categories and pages, flagging any title / canonical / robots drift and confirming every canonical equals its permalink — so you flip the switch with confidence and keep AIOSEO installed-but-inactive as a rollback.
* **EU Omnibus reference price.** A 30-day-lowest-price history feeds the legally-required "lowest price of the last 30 days" reference on reductions.
* **Multilingual.** Per-language head + `hreflang` for WPML/Polylang (including Polylang, which AIOSEO never localised), and a TranslatePress-aware sitemap that emits the per-language prefixed URLs.
* **Core vitals carry the canonical.** Each product's `product_url` in the core vitals table is now its SEO canonical where one exists (otherwise the existing reference is left untouched); the column was widened to 300 characters and the write is bounded, so a stray over-long canonical can never overflow or fail the catalogue rebuild — our most load-bearing table.
* **Editor blocks discoverable again.** All AIEO blocks now appear in the inserter regardless of a block theme's curated block-palette (e.g. roosterX), and every block title is prefixed "AIEO" so they're findable by search; product-context blocks (swatches, PDP messages…) stay correctly scoped to single-product templates.
* **Fixes & runtime.** Fixed a 404 / MIME-type console error from a retired product-labels stylesheet that was still being enqueued. The thin front-end **runtime plugin moves to its own 1.0 version track** and now also serves the SEO head, the sitemap and the redirects with the core plugin trimmed from the request.

= 7.2 TikTok conversions, consent carried on every event, runtime-owned measurement, in-request event delivery — and the searchable ROI / UTM reports (08–09/06/2026) =

* **TikTok added to Pixels & Conversions.** TikTok joins Meta and Google as a fully-owned platform — a browser **TikTok Pixel** and the server-side **Events API**, deduplicated on a shared event id, with the click id (`ttclid`) recovered from the captured attribution session. Same consent-gated architecture as the Facebook CAPI; spec-verified against TikTok's current docs.
* **Consent travels with every event (in-band signalling).** Beyond the consent gate, AIEO now tells the ad platforms the consent state *on each event*: region-aware **Google Consent Mode v2** defaults (deny in the EEA, grant elsewhere, emitted before any tag fires), GA4 Measurement-Protocol top-level **consent**, Google Ads per-conversion **consent**, and Meta CAPI **Data Processing Options** — so a declined category down-scopes the conversion at Google / Meta / TikTok too, not just in the browser. Consent is snapshotted onto the order at checkout so the asynchronous server sends carry the right state.
* **The runtime owns measurement when present.** When the premium runtime plugin is active it serves all three browser pixels (Meta / TikTok / Google tag) **and** the three server senders itself — even on requests where the core plugin is trimmed — by lazy-loading the core send logic by path. The core plugin stays fully functional on its own; each surface is gated so exactly one of the two serves it.
* **Event delivery rebuilt for scale.** Server-side conversions no longer queue through Action Scheduler. They flush the page response to the shopper first (`fastcgi_finish_request`, LiteSpeed too) and send in the *same* request — zero checkout latency, no WP-Cron dependency, and nothing to back up at thousands of events a minute. A **"Fire-and-forget"** toggle in Pixels & Conversions (on by default) frees the worker the instant each event is sent; switch it off to wait for and log every platform's reply (sent / failed) while validating a setup.
* **Order origin in staff e-mails.** New-order staff notifications now show where the order came from — the marketplace (Skroutz / ShopFlix / eMAG …) or the site with its marketing channel and campaign — read from the one canonical order-facts source. (This replaces the PixelYourSite origin line that frequently failed to render.)
* **Ad-connector setup links.** The Google / Meta / TikTok credential wizards now link straight to where each value is found, so the "what to ask your agency" guidance is actionable.
* **UTM Explorer (new report).** A cascading drill-down across the five campaign tags — Source → Medium → Campaign → Term → Content — where each level is a searchable multi-select that narrows the lists below it to what actually co-occurred under your choices. It lives on the ROI Wizard page (and is findable from the admin command palette by searching "utm"). The table breaks the current selection down by the next level, with orders, revenue and average order value.
* **Searchable multi-select across the whole cockpit.** Every selector in the ROI Wizard and UTM Explorer — the **Type**, the exact detected **value**, the five UTM levels and the ad-spend **ledger value** — became the same fast searchable picker used for product selection (search, tick several, Done). Choosing from hundreds of creatives or campaigns now takes seconds instead of scrolling a giant dropdown. **Type** accepts several types at once and stacks one results table per type; **Exact** offers the union of the selected dimensions' values.
* **Reco lift per channel.** A new ROI type that shows the recommendation engine's incremental orders and € *over and above* each traffic channel — of the orders Google / Meta / organic brought, what share the recommendations assisted and what they added.
* **Period comparison (both reports).** Compare any result against the **previous period** or the **same period last year**, with a green / red Δ% shown inline on every metric. Off by default.
* **Report polish.** The UTM Explorer is consolidated onto the ROI Wizard page (ROI → UTM → ledger order); From / To dates lead each row; rows are top-aligned so selecting a value no longer nudges the controls; the Ad-spend connectors moved into a renamed **Tools & Settings** group; the default date range is the last 30 days.

= 7.1 First-party measurement & consent, then the ROI Wizard & ad-spend connectors (Google · Meta · TikTok), one canonical order revenue/channel (06–08/06/2026) =

* **Pixels & Conversions (server-side measurement).** AIEO can now own the Facebook (Meta) Pixel and Google Analytics 4 end to end — a thin browser pixel **and** server-side Conversions API / GA4 Measurement Protocol, deduplicated on a shared event id, sent after the order is saved (zero checkout latency). Each platform is selectable, with master off-switches so you can keep PixelYourSite; Facebook credentials can be read from PixelYourSite if present.
* **Advertiser-grade match quality (PixelYourSite-Pro parity).** Facebook browser **Advanced Matching** (hashed client-side, consent-gated) on top of the full server-side set; **complete GA4 user-data** (name + address, Unicode-correct for Greek); a built-in **Google tag** loader (gtag.js or a GTM container, wired to Consent Mode v2); and **Google Ads Enhanced Conversions** both in-browser and **server-side via the Google Ads API** (keyed on the captured gclid).
* **Consent / GDPR — first-party CMP.** A lightweight first-party cookie-consent banner (no external CMP, no IAB TCF bloat): rich-text message, three actions (Accept all / Decline / Customize) with a per-category preferences panel, per-button theming with an equal-prominence guard, two layouts (bar or centre glass pop-up), a floating "Cookie settings" re-open button, and a policy-version re-prompt. Drives Google Consent Mode v2 and gates AIEO analytics plus the Facebook / GA4 senders, with a built-in cookie scanner.
* **Checkout timing.** Active vs idle time is measured for each successful checkout and aggregated per product, so offer timing (exit-intent, etc.) can be set from real deliberation data; coupon-completed orders are tagged and excludable from the baseline.
* **Custom Tabs + Product Tab Ordering (Product Display).** Up to five extra renamable product tabs (each with its own rich text per product, auto-hidden when empty; one-click migration from the legacy Pods usage/specification fields), plus a drag-and-drop card that orders the WooCommerce tabs together with the Custom Tabs on both block and classic themes.
* **ROI Wizard — one cockpit for "what actually drove the sale".** A new **Attribution → ROI Wizard**: pick a **type** (⭐ Recommendation engine, Offer, Channel, Campaign, Creative), then the exact detected value, then a period — and see orders, revenue, cost, net, **ROAS** and **ROI %**. AIEO's own contribution is front and centre: recommendation-assisted sales and offer effectiveness (free shipping, gifts, discounts) are quantified right next to marketing channels and campaigns. Revenue is the order AIEO computes; offers carry their *real* cost automatically (free-shipping uses the courier rate already computed in Fulfillment), and channels/campaigns use the ad spend you enter or sync.
* **Ad-spend ledger + bulk CSV.** A per-period, per-dimension ad-spend ledger backs channel / campaign / creative ROI. Enter spend inline, or **export a CSV, fill a whole quarter in a spreadsheet, and re-import** — manual entry, CSV and the connectors all land in one place, summed over your reporting window.
* **Ad-spend connectors — Google Ads, Meta, TikTok.** Connect each platform once through a guided credential wizard (with exactly *what to ask your ad agency for*); AIEO then pulls campaign spend nightly into the ledger, so ROI becomes automatic and you never leave AIEO to reconcile spend against revenue — one cockpit for spend AND your own recommendation / offer / channel attribution. Credentials are stored encrypted; the pull is read-only.
* **One canonical order revenue + channel.** A single source of truth for each order's revenue and marketing channel (`AIEO_Order_Facts`), reading from the order AIEO computes, with a documented map of where every order fact lives — including why the WooCommerce total and the ERP invoice total are *intentionally* different (the reconciliation behind pickup-store invoicing). The offer-effectiveness report now reads the canonical channel, so per-channel offer ROI no longer mislabels orders as "unattributed" when they were placed before attribution caught up.
* **Accurate engagement & attribution.** The engagement score is now one canonical formula everywhere (it previously differed between the AI-chat context and the analytics reports). Re-delivered tracking beacons can no longer double-count add-to-cart totals (idempotent, deduplicated server-side). The Attribution dashboard no longer writes thousands of rows on every page view — the channel back-fill moved to an hourly background job — and all of its channel cards now use one consistent 7-day attribution window.
* **Storefront & block editor.** Carousels (Brand Slider, recently-viewed and the like) gained an optional **"Recos carousel" slider style** — the pink scroll-progress bar matching the recommendations carousel — selectable per block; the **Google Reviews** block gained a true manual swipe-scroll mode. Fixed block-editor errors ("Invalid parameter(s): attributes") that appeared on AIEO blocks under WordPress 7 by registering the shared responsive / section-header attributes server-side.
* **Housekeeping.** The server-side conversion log now self-prunes (30-day retention). Fixed a stored-procedure installer that, because of a `DELIMITER` directive, had silently never updated the session-analytics procedures.

= 7.0 Product-page layout builder (block + classic themes), reusable product/variation picker, master Button Styles (padding/sizing/glass), side-cart mobile bar styling, wishlist & brand in the layout builder, sticky admin navigation (05/06/2026) =

* **Side-cart mobile bottom bar — fully theme-independent + styleable.** The mobile sticky bottom bar (Navigator / Search / AI Chat / Cart) gets its own controls on the Side Cart tab: bar background colour + height, icon colour, and the cart-count number + circle colours (each can differ from desktop). A new **glass effect** renders the bar semi-transparent with a frosted backdrop-blur (opacity + blur configurable) so the page shows through. New **per-option "selected" colours** (background / text / border for each of Navigator, Search, AI Chat, Cart) highlight an option while its panel is open. When ONLY the cart icon is enabled you can show it as a **floating bubble** (like the desktop cart) instead of a full-width bar, and a lone cart icon is now centred with its count badge correctly anchored.
* **Side-cart display owned by the runtime.** The whole side-cart surface (the floating shell, the mobile bar, the slide-out panel and its assets) moved into one shared class bundled in both plugins, so the runtime renders it even when the admin plugin is trimmed from a request; the admin module remains the standalone fallback.
* **Product Needs — new "Pills" display.** Alongside the stacked list, the product-needs taxonomy can now render as tag-style pills laid out side by side, with their own text size, corner radius, vertical / horizontal padding, gap, background, border (width + colour) and text colour.
* **Master Button Styles — padding, sizing and glass.** Every master button group (Primary / Secondary / Archive) now also controls **Top / Right / Bottom / Left text padding** and **min / max width + height** (0 = no constraint), plus an opt-in **glass effect** (transparent background via `color-mix` + backdrop blur, opacity + blur per group). The Secondary group gained full hover parity — a base border plus hover background / text / border colours — to match Primary and Archive.
* **Wishlist + Brand in the product-page layout builder.** "Add to Wishlist" is now a positionable element in the Display Order list with a **Beside image / Beside the product title / Under image** choice (the heart sits inline next to the title by default). Brand Display gained the same **"Beside the product title"** option, and the builder's placement is now authoritative over the per-module Brand position setting.
* **Admin polish.** Fixed the "AI Optimizer" toolbar icon occasionally stacking above its label (a theme `img{display:block}` rule). Reordered the AI Optimizer sidebar submenu to **Data Prep → e-shop Manager → AI Chat → Interactivity (IAPI) → Rec Cache** and removed the one-off Session Schema Migrator from the menu. The AIEO admin **top bar and the e-shop Manager / Analytics sub-tab navigation are now sticky**, staying visible as you scroll long settings pages.

* **Product-page layout builder — one list orders the whole product info column.** Storefront Display → Single Product Page → Dashboard now has a single drag-and-drop "Display Order" card that governs the vertical order of EVERYTHING in the product info column: the core elements (title, rating, short description, sale-eligibility and free-shipping messages, price, add-to-cart) AND the info modules (product meta, categories, brand, payment logos, trust badges, back-in-stock waitlist, free brand gifts, complementary products, product needs, role-pricing login). Each module also gets a per-item "Beside image / Under image" choice, so it can be moved beneath the gallery. The same saved order is honoured on BOTH the block (FSE) theme — via CSS `order` on the info-column flex children — and on classic themes (Botiga / FemmeFatale-V2), by re-prioritising the matching WooCommerce single-product-summary hooks (WC core templates + AIEO modules), so the two render identically. Core blocks that ignore `className` (rating, short description, add-to-cart) are now wrapped so their order always applies, and Brand Display's before/under-title placements follow the list instead of being pinned above the title.
* **Restored the product "ΚΑΤΗΓΟΡΙΕΣ" (Categories) line as its own positionable module.** When AIEO replaced WooCommerce's native product-meta block (SKU + categories + tags) with its SKU/barcode strip, the categories line was dropped. It is back as its own orderable module — a clickable comma-separated list, styled to match the SKU/Barcode rows (label and list flow as one inline stream, with uniform vertical spacing) — placeable before or after Product Meta. The Role-pricing "log in to see your price" message is likewise positionable within the info column. (Product Categories was also moved to the "Single Product Page" section of the Storefront Display menu, next to Product Meta.)
* **Reusable product / variation picker on every product-criteria field.** A shared modal browser — search by name, SKU or brand; pick simple products, whole variable products, or specific variations; drafts and deleted items shown greyed, variations badged — replaces the free-text ID/SKU/barcode boxes on the offer forms: **Product discounts, Free gifts and Free shipping** (both the "specific products" and the "excluded products" lists), plus the **Product Exchanger** feed builder and the **marketplace feed exporter**. Picked variations roll up to their parent so cart matching is unchanged, and existing saved selections migrate automatically. One shared implementation (`AIEO_DMM_Product_Picker`) drives them all.
* **Master "Button Styles" control (Tools).** A single card now sets the default background, text colour, border (colour + width), corner radius and hover state for every AIEO storefront button — with three masters: **Primary** (add-to-cart, checkout, "notify me", "add selected"…), **Secondary / neutral** (view cart, save / share cart, apply coupon) and **Archive** product-card buttons. Each button that already had its own colour picker — Back-in-Stock Waitlist, Complementary, Sticky Add-to-Cart, Side-Cart — now inherits the master by default and overrides it only when you set a value (a CSS custom-property fallback chain, so nothing you previously customised changes). The side-cart action buttons, the storefront "Save / share my cart" button, the coupon-Apply button and the variable-product multi-select "update cart" button were aligned to the same tokens. Also fixes a long-standing admin bug where the colour-picker swatches rendered blank on the DMM settings pages (a global rule forced the swatch background transparent) — every DMM colour picker now shows its colour.
* **Admin discoverability.** The Ctrl / ⌘-K quick navigator now indexes the product-page layout/positioning card and the Saved & Abandoned Carts report; the AI Insights assistant gained an abandoned-carts analysis playbook plus "where does this live" pointers for the layout builder, the carts report and the product picker.
* **Auto-restock — e-shop sales no longer counted as physical-store demand.** The "Stock Recovery" auto-restock signal mines physical-store sales from the ERP to find products that are out-of-stock online but still selling in shops. The e-shop's own sales — invoiced through the head-office branch with a distinct ERP invoice-type code — are excluded from that signal so they cannot inflate in-store demand and trigger false restocks. The contract is documented for multi-store setups (give the e-shop's ERP sales a distinct invoice type).
* **Stock Recovery — out-of-stock cancellations surfaced as a "failed restock" signal.** When an order line is cancelled with the reason "Out of stock", the affected products are reconciled against the restock activity and shown in the Failures KPI, so restocks that didn't actually prevent a lost sale are visible.
