VendMizer — release history (archived)
======================================

The changelog for the current release line lives in readme.txt. This file keeps
the entries for earlier releases so the readme stays within the size the
WordPress.org plugin directory displays in full.

= 1.47.0 =

* Performance: the shop's search box is roughly a hundred times faster to suggest as you type. The suggestion list fires on every keystroke, and it was reading every published product each time — about 287ms per character at 49,000 products. It now uses the same search index the full search results page has used since 1.35.0. Measured at that catalogue size: a typical word goes from 287ms to 3ms.
* What this means in practice: suggestions now come from whole words and from the start of a SKU, the same way the search results page already worked. A fragment from the middle of a word (typing "shirt" and expecting "Sweatshirt") is only suggested when nothing else matches — which is also how the results page has always behaved, so the suggestions and the results now agree with each other. Developers can switch the index off with the `vendm_suggest_fulltext` filter, or the slow scan with `vendm_suggest_substring_fallback`.
* Note: no database changes in this release.

= 1.46.0 =

* Performance: searching the Customers screen is around a hundred times faster on a large store. The search asked the database six "contains this text" questions no index could answer, so every search read every customer row — about 290ms at 100,000 customers, whatever you typed. Names, email addresses and phone numbers are now found through a search index instead. Measured at 100,013 customers: a first or last name 289ms to 2.9ms, a full email address 286ms to 7.7ms, a phone number 288ms to 2.9ms, and the start of an email address 284ms to 3.1ms.
* Performance: the Customers screen's "Deleted" filter went from 179ms to 1.3ms. It asks for customers that have been deleted, which the existing index could not answer, so it read the whole table to find them — on the ordinary store, which has none, that meant reading every row to return nothing.
* Fixed: customers created at the same moment had no defined order between them, so in principle the same customer could appear on two pages of the list while another was skipped. On the test store 99,911 of 100,013 customers shared a timestamp with at least one other. The list now orders by date and then by customer id.
* What this means in practice: searching for a fragment from the middle of a name or phone number still works, but only when the faster searches find nothing first — so searching "2656" now finds the eleven customers whose number starts with it rather than the twenty-one that contain it anywhere. Searching by whole name, whole email, email prefix or phone is unaffected and much faster. On stores above 200,000 customers the mid-fragment search is skipped entirely, as it already is for products. Developers can adjust this with the `vendm_customer_search_scan_max`, `vendm_customer_search_substring_fallback` and `vendm_customer_search_fulltext` filters.
* Note: this release adds one column and two indexes to the customers table on upgrade. It took about 12 seconds on the 100,000-customer test store and grows that table by roughly 60%.

= 1.45.0 =

* Performance: the Customers screen loads about eight times faster on a large store. Almost all of its time went on counting the customers for the pager, which had to read every row: at 100,000 customers that was 26ms of a 29ms response, while fetching the twenty rows you actually see took half a millisecond. The count is now remembered between visits and recalculated only when a customer is added, removed, suspended, reactivated or restored. Measured at 100,013 customers: the screen goes from 26.2ms to 3.3ms, page 50 from 22.3ms to 4.1ms, and the "all statuses" view from 10.3ms to 2.2ms.
* Performance: when a search or filter returns less than one full page, the count is not calculated at all — the page already knows the answer. That makes a search that finds nothing cost one database query instead of two.
* Note: searching the Customers screen is still slow on a large store (around 220ms at 100,000 customers) because of how the search itself is written. That is a separate fix and is not in this release.

= 1.44.0 =

* Performance: the Analytics dashboard is roughly twice as fast to rebuild on a large store. Its revenue figures are converted to your base currency as they are added up, and the index the dashboard relies on did not include the currency column — so the database could not answer those sums from the index and read every matching order row instead. The index now includes it. On a test store with 900,000 orders the monthly revenue chart went from 8.6 seconds to 0.34 seconds, the headline figures from 1.8 seconds to 0.18 seconds, and a full dashboard rebuild from 18.0 seconds to 8.5 seconds, with identical numbers on screen. Smaller stores see the same change in proportion: at 300,000 orders the monthly chart goes from 204ms to 112ms.
* Note: this release rebuilds one database index on upgrade. It took under two seconds on the 900,000-order test store, and the index grows by about 1.4%.

Earlier releases are listed in changelog.txt, which ships with the plugin.
The WordPress.org plugin page displays only the first 5,000 words of this
section, so keeping the full history here would silently cut the oldest
entries from the listing.

= 1.56.0 =
* Improved: checking a payment's status with Moyasar, Tabby or Tamara used to wait up to 30 seconds for a reply. On a busy store, a slow gateway could tie up the server that way. These checks now give up after 10 seconds and are simply retried, which is safe because asking for a status changes nothing.
* Unchanged, deliberately: captures, authorisations and refunds keep the longer 30-second wait. A timeout on one of those does not mean it did not happen — the gateway may have taken the payment and only the reply went missing — so cutting the wait short would make that outcome more likely, not less. Both limits can now be set in wp-config.php if your gateway needs different ones.
* Internal: a new browser-level test suite loads all 47 admin and storefront screens at two screen sizes and fails on a rendering fault — a stringified object, a value shown as "undefined", a screen stuck loading, a failed request or a script error. This is the class of fault that a data-only test cannot see.
* Note: no database changes in this release.

= 1.55.0 =
* Security hardening: the click-tracking rewriter that runs over outgoing email escaped each rewritten link with the general attribute escaper rather than the URL one. With click tracking switched off the link passes through untouched, so a link written into a merchant's own email template kept whatever scheme it carried. Now escaped as a URL, which drops anything that is not an allowed protocol.
* Fixed: the storefront deal-slider image, the contact-card telephone link and the WhatsApp link in the email footer were escaped the same way; all now use the URL escaper.
* Internal: the test suite now fails on any link or image in the plugin that is escaped with the wrong function for its position.
* Note: no database changes in this release.

= 1.54.0 =
* Fixed: the eight allowlists that decide an announcement's status, display type, schedule, audience, page target, dismiss behaviour, frequency and popup trigger compared loosely. On PHP 7.4 a numeric value passed the check and was stored in place of the intended default. Now compared strictly, on every supported PHP version.
* Fixed: the staff profile-change audit entry dropped the wrong field from its list whenever the record it was trying to drop was not present.
* Internal: the plugin was run through PHP_CodeSniffer with the full WordPress Coding Standards ruleset. The findings that affect behaviour are the two above; the rest are indentation and house style, and are recorded in the release notes.
* Note: no functional changes to the storefront or checkout, and no database changes, in this release.

= 1.53.0 =
* Documentation: every place the plugin prints a file, a document or a data block whole — a PDF receipt, a CSV export, the product feed, a download — carries a note in the source explaining why that output is already safe. Four of those notes were checked line by line against the code this release: two were too terse to verify and one described a different piece of code entirely. All now say what the code does.
* Internal: the full set of those notes is now covered by an automated check, so one cannot be added, removed or left unexplained without the test suite saying so.
* Note: no functional changes and no database changes in this release.

= 1.43.0 =

* Fixed: on a very large catalogue, a mistyped product search could hang the screen. Searching for something that matches nothing falls back to checking every product one by one, and at 500,000 products that took 22 seconds — long enough to exceed the time limit most hosts allow and return an error. That last-resort check is now skipped above 200,000 products, so the search answers immediately instead: measured 22.1 seconds down to under 30ms.
* What this means in practice: on catalogues above 200,000 products, searching for a fragment from the middle of a product name or SKU no longer matches. Searching by whole words and by SKU is unaffected, and so is every catalogue below that size. Developers can change the limit, or remove it, with the `vendm_product_search_scan_max_products` filter.

= 1.42.0 =

* Fixed: sorting the Products screen by price had no index behind it and sorted the whole catalogue every time — around 130ms at 100,000 products, now under half a millisecond.
* Fixed: sorting products by name was fast in English and slow in Arabic. Only the English name had an index; the Arabic one sorted the whole catalogue on every visit, on both the admin screen and the shop. Arabic now has the same index English has, and both are equally fast.
* Improved: two database indexes that another index already covered have been removed — one on order lines and one on product names. Fewer indexes means slightly faster writes and a smaller database, with no change to how anything reads.
* Note: this release runs a one-time index rebuild on upgrade, which can take a few moments on a very large store.

= 1.41.0 =

* Performance: searching the Products screen no longer reads the whole catalogue. Looking up a product that matches only a handful of rows went from 173-204ms to 4ms on a store with 100,000 products, and searching by a full SKU from 347ms to 3ms. The screen used to ask the database one question that it could not answer with any index; it now asks the indexed questions first and falls back to a slow scan only when those find nothing.
* Performance: the Products screen itself is faster to load — 13.4ms to 3.2ms, and page 50 from 17.1ms to 5.0ms — because the "how many products" figure behind the pager is no longer recalculated on every visit. When a search returns less than a full page the count is not calculated at all, since the page already knows the answer. That also means a search that finds nothing costs one lookup instead of two.
* Fixed: products sharing the exact same creation time had no defined order between them (3,883 such timestamps on the test store), so in principle the same product could appear on two pages while another was skipped. Products are now ordered by the sort you chose and then by product id.
* Note for large catalogues: a search term that matches nothing still has to check every product before it can say so. Developers can switch that last fallback off with the `vendm_product_search_substring_fallback` filter, at the cost of no longer matching a fragment from the middle of a name or SKU.

= 1.40.0 =

* Performance: the Orders screen no longer slows down the further into it you go. Measured on a store with 300,000 orders: page 500 went from 397ms to 8ms, page 5,000 from 965ms to 163ms, and page 15,000 from 1.4 seconds to 146ms. The screen now picks which orders belong on the page before it fetches their details, instead of assembling every skipped row on the way.
* New: the orders API can be walked with a cursor instead of page numbers. Each response includes a `next_cursor`; pass it back as `cursor` to get the following batch. Unlike page numbers, a cursor costs the same at any depth — about 4ms per batch whether you are at the start of the list or 300,000 orders in — so a tool that syncs your whole order history finishes in about a minute rather than grinding through progressively slower pages. Page numbers keep working exactly as before.
* Fixed: orders sharing the exact same timestamp had no defined order between them, so in principle the same order could appear on two pages while another was skipped. Orders are now ordered by time and then by order id, which is a guarantee rather than a coincidence.
* Performance: the "how many orders" figure behind the pager is now remembered between requests instead of being recounted every time — it was 35ms of every single Orders request, about 87% of the work once everything else was fixed. It refreshes immediately whenever an order is created, edited, cancelled or refunded.

= 1.39.0 =

* Fixed: on multi-branch stores, the low-stock API returned every low item in the branch in one response, with no limit of any kind — 1,884 items and 488KB on the test store, and unbounded in principle. It is now paged like every other list. The single-location version was already capped at 500 items but gave no way to ask for the rest, or any hint that there were more; it now reports the total and accepts a page size. The default page is unchanged at 500 items, so nothing that already uses this changes behaviour.
* Performance: assigning barcodes to a catalogue used to write them one product at a time — 5,005 database calls and about 3 seconds per batch of 5,000. Barcodes are now written in batches, and because that is so much cheaper each run does more: on the test store a run went from 5,000 barcodes in 2.9 seconds to 20,000 in 1.3 seconds. A 200,000-item catalogue that needed 42 separate runs now needs 11. The generated codes are unchanged, and existing barcodes are still never overwritten.
* Improved: bulk stock updates are about a third faster and issue a quarter fewer database calls (8 per item down to 6). The stock history they record is identical — same rows, same values — it is just written in one go instead of one line at a time.
* Fixed: a barcode saved as an empty value (rather than left unset) was treated as "has a barcode" by the assign step and skipped forever. Those are now recognised as missing and filled.

= 1.38.0 =

* Performance: the Dashboard is about three times faster to build on a large store. Measured on 100,000 products, 300,000 orders and 745,000 order lines: 4.19 seconds down to 1.41 seconds on a cold cache. Every figure on the screen is unchanged — this release only changes how those figures are calculated, and each one was checked against the old calculation over four different date ranges before shipping.
* Performance: working out the period's best-selling product used to read the full record of every product sold in the window just to print one product's name — around 11,000 lookups to fill in a single line. The name is now looked up once, for the product that actually wins: 1.73 seconds down to 0.26. Sales for products you have since deleted are still excluded, exactly as before.
* Performance: the revenue-by-category chart now totals each product once and then attributes that total to the categories it belongs to, instead of carrying every sale through the category list separately. On this store, where nearly 10,000 products sit in more than one category, that is 165ms down to 91ms.
* Performance: the "new customers" figure — customers whose first order falls in the period — is now worked out with a direct check for an earlier order rather than by matching every order against every other order and discarding the matches. 182ms down to 112ms.

= 1.37.0 =

* Performance: low-stock screens no longer read through the whole catalogue to work out what is running low. The answer is kept on the product row and maintained by the database itself, so it cannot drift out of step with the stock figures. Measured on 100,000 products: the Inventory screen went from 270ms to 41ms, the Low Stock view answers in 3.5ms, and the Dashboard's low-stock figure — a 181ms scan on every cold load — is gone entirely.
* Performance: importing a product CSV asked the database the same questions over and over, once per row: does this SKU exist, what is this brand, what is each of these categories. They are now answered once for the whole file. A 500-row import went from 2.1 seconds and 6,020 queries to 0.8 seconds and 1,510, and the saving grows with the file — a 3,000-row import holds at three queries per row instead of twelve.
* Fixed: products imported with a sale price did not show on the shop's "On Sale" page until the next day's maintenance run. They appear straight away now.
* Fixed: a CSV row that listed the same category twice produced a database error during the import. Repeated categories are now ignored.
* Performance: on multi-branch stores the Inventory screen's page count no longer joins against branch stock when no stock filter is applied — 763ms to 485ms at 100,000 products, with identical figures shown.
* Improved: a product import used to recount every brand in the store one brand at a time. It is now a single step, and a brand the import emptied has its count corrected as well.
* New: a weekly integrity check counts stranded rows across nine table relationships and records anything that moves in the activity log. It only reports — it never deletes — so if a future change starts leaving rows behind, it shows up the week it happens rather than years later.

= 1.36.0 =

* Fixed: deleting a product left rows behind forever. Six related tables — including multi-branch stock — were never cleaned up when a deleted product was finally removed, so every store has been accumulating dead rows in proportion to how many products it has ever deleted. Upgrading sweeps the backlog in the background: the benchmark store had 95,217 stranded rows across four tables, all cleared. Multi-branch stores are affected most, because each deleted product left one row per branch.
* Fixed: emptying the trash was capped at 100 products a day. A merchant who deleted 10,000 discontinued products waited a hundred days to get the space back, and every one of those rows was still being counted and scanned in the meantime. The cleanup now works in batches of 1,000 and continues by itself until it is done.
* Performance: the shop's "On Sale" page no longer searches every product variation in the store to decide what is discounted. Measured on 100,000 products: 380ms to 4ms, and at 500,000 products it was 4.7 seconds. This matters most exactly when a promotion is driving traffic to that page.
* Performance: the Products screen's counter tiles, the Reviews screen's totals and star average, and the sales report are now cached instead of recalculated on every visit — 90ms to 2ms, 167ms to 9ms, and the sales report no longer grows with your order history. All three refresh immediately when you change the underlying data.
* Performance: sorting the shop by product name is now instant (298ms to 4ms at 100,000 products); the same fix landed for the admin Products screen in 1.35.0.
* Improved: bulk-cancelling orders is about twice as fast, and claims the whole batch in one step instead of one order at a time. The protection against restoring the same order's stock twice is unchanged and re-verified against the single-order cancel path.

= 1.35.0 =

* Performance: large catalogues and long order histories are dramatically faster. Benchmarked on a store with 100,000 products, 300,000 orders and 745,000 order lines, on ordinary shared-hosting settings: the Products screen went from 206ms to 22ms, sorting products by name from 273ms to 23ms, filtering orders by status from 352ms to 35ms, searching orders from 2.6 seconds to 55ms, storefront search from 1.06 seconds to 58ms, and sorting the shop by name from 298ms to 4ms.
* Fixed: exporting products could run the site out of memory. The export used to build the whole catalogue in memory at once — around 9MB of PHP memory per 1,000 products — so on a 256MB host it failed silently somewhere above 18,000 products. It is now written out in batches: peak memory stayed under 15MB while exporting 100,000 products, and the exported file is unchanged.
* Fixed: the twice-daily low-stock check did not scale. On a large catalogue it loaded every low product and variation at once, looked each one up separately, and left one database row per product behind every run — a store with 500,000 products saw it take over six minutes, issue 226,000 queries, and fill the options table with 90,000 rows. It is now bounded, batched, and keeps its state in a single row: the same job on 100,000 products went from 21.8 seconds and 45,196 queries to 0.8 seconds and 405.
* Fixed: the Dashboard could rebuild its figures on every single visit on a busy store, because ordinary activity such as a new order kept clearing its cache. Several admins opening it together each triggered a separate rebuild. The cache now survives normal activity, and while a rebuild is in progress everyone else is shown the last figures instead of starting another one.
* Improved: database indexes were rebuilt around the queries the admin screens actually run, and several redundant ones removed — including two that were quietly steering MySQL away from a better index it already had. Upgrading runs a one-time index rebuild, which can take a few moments on a very large store.
* Improved: storefront and order search now use the site's full-text indexes. Searching for part of a word still works exactly as before.
* Improved: turning off the Inventory or Tracking module now also stops its scheduled background job instead of leaving it running.

= 1.34.0 =

* Fixed: after updating the plugin, opening a console page could show "Failed to load page module." until you refreshed — and the refresh sometimes showed a "Checking your browser…" security page first. This happens when a CDN or firewall in front of your site (for example Cloudflare) shows a browser-check to the background request that loads a page, which a background request cannot complete on its own. The console now detects that case and reloads itself once automatically — the same refresh you were doing by hand — so the page loads without the error. If the whole console is held on the loading spinner for the same reason, it now recovers by itself too. A note for site owners is in the FAQ ("Console pages fail to load behind Cloudflare").

= 1.33.0 =

* Fixed: Stripe refunds issued from the Stripe Dashboard in a zero-decimal currency (Japanese yen, Korean won, Vietnamese dong and others) were recorded at one hundredth of their real value — a ¥5,000 refund showed as ¥50 and the order stayed "partially refunded". Refund creation, refund reconciliation and the original charge now share one currency table, so they can never disagree; it also follows Stripe's current rules for the Ugandan shilling and Icelandic króna.
* Added: if Stripe later reports that a refund failed or was cancelled (for example the customer's card was closed), VendMizer now reverses the refund in your books, sets the order's payment status back to what was really refunded, and alerts admins so the refund can be reissued. Add the `refund.updated` event to your Stripe webhook; the Stripe settings panel now lists every event to enable.
* Changed: the map address picker no longer searches as you type. Address lookups are sent only when you press Enter or the new Search button, which is what OpenStreetMap's Nominatim usage policy requires for its free geocoding service — search-as-you-type is expressly disallowed and could get a store's map blocked. Results now also credit OpenStreetMap contributors.
* Security: login rate limiting and the account lockout now treat every letter-case spelling of an e-mail address as the same account (alice@x.com and Alice@x.com share one attempt budget), closing a way to sidestep the lockout. The same applies to the magic-link and forgot-password limits.
* Security: backup downloads now require the admin's own signed-in session as well as the link's token; the token is bound to the admin who created it, works once, and expires after 15 minutes instead of an hour. A backup link copied out of a browser history or a server log can no longer fetch the file.

= 1.32.0 =

* Added: a camera "Scan" button beside the Barcode field when adding or editing a product, so you can scan a product's barcode with your device camera instead of typing it. The scanned code fills the field and runs the same live preview, check-digit fix, and duplicate detection as manual entry. The button appears only where camera scanning is available (it uses VendMizer Pro's scanning engine); a hardware barcode scanner still fills the field directly when focused.

= 1.31.0 =

* Improved: camera scanning now reads a full-resolution band across the centre of the frame — where the on-screen aiming box sits and where a barcode is actually held — and checks the whole frame on alternate passes, so each scan attempt is faster and better targeted. Pairs with VendMizer Pro 1.11.65's new scanning engine.

= 1.30.0 =

* Improved: further camera-scanning reliability, especially on iPhone and iPad. The scanner now waits for the camera to deliver a fully-rendered frame before reading it, paces itself off the device's real frame updates, and sets the video attributes iOS Safari needs to play the camera inline — addressing cases where the camera opened but never read a barcode. While the camera is open a small readout shows the live resolution and scanner status to help pinpoint any device-specific issue. Benefits VendMizer Pro's camera scanning.

= 1.29.0 =

* Improved: camera barcode scanning is far more reliable. The camera now requests a high-resolution feed with continuous autofocus, and the scanner reads a sharp, zoomed-in view of the centre of the frame (where you aim the barcode) instead of a shrunken whole-frame image — so real product barcodes actually decode. Benefits VendMizer Pro's camera scanning.

= 1.28.0 =

* Improved: the camera-scanning engine can now use a bundled JavaScript barcode decoder as a fallback when the browser's built-in barcode reader is missing or unreliable (as it is on iPhones, Firefox, and many desktop and Android browsers). VendMizer Pro supplies that decoder, so camera scanning in Pro's POS and inventory tools works on essentially any device with a camera. No change for stores running the free plugin on its own.

= 1.27.0 =

* Fixed: adjusting stock by hand in a multi-branch store now works for every user. The stock-adjust window shows a branch selector and always records which branch the change applies to, so staff limited to one branch — and admins viewing "All branches" — no longer hit a "branch required" error when saving an adjustment.

= 1.26.0 =

* Fixed: filtering the inventory overview by several products at once no longer triggers a PHP notice — the product-id list is now accepted whether it arrives as an array or a comma-separated string. Housekeeping release from a full audit of the barcode, POS printing and tax-invoice features; no changes to stored data or storefront output.

= 1.25.0 =

* Improved: tax-invoice fields on receipts and PDF invoices. The tax registration labels ("Tax ID", "Reg. No.") now display in the receipt's language (including Arabic) instead of always in English, and Settings → Invoice & Receipts adds two optional labels — "Tax label" (e.g. VAT 15%, GST) for the tax line, and "Tax number label" (e.g. VAT No., TRN) for the registration number — so invoices match your country's tax terminology.

= 1.24.0 =

* Improved: the inventory list can now be fetched for a specific set of products, letting scan-driven tools (such as the Pro extension's "Stock count") read the current on-hand quantity for exactly the scanned items in the branch you're viewing. Additive; no change to the inventory screen on its own.

= 1.23.0 =

* Improved: groundwork for scan-first stock receiving — the Inventory screen can now host add-on tools (used by the Pro extension's "Scan to receive"), and a barcode lookup now returns the product id so a scan can target the right item. No visible change on its own.

= 1.22.0 =

* New: camera barcode-scanning support in the core scanner utility. On devices and browsers that support it, VendMizer can now read barcodes from a device camera — powering the "Scan with camera" feature at the POS. No configuration; nothing changes unless a feature uses it.

= 1.21.0 =

* New: scan to find a product. On the Products screen, scan a barcode with any USB/Bluetooth barcode scanner and VendMizer opens the matching product instantly — no clicking or typing. Works with the barcodes you assign under Tools → Barcodes.

= 1.20.0 =

* New: show a scannable QR code on order receipts. Under Settings → Invoice & Receipts, set a "Receipt QR link" (a review page, order lookup or your store) and an optional caption, and a QR is printed on the receipt — on A4 and on the 58/80 mm thermal roll. Leave it blank for none; receipts without a QR are unchanged.

= 1.19.0 =

* New: print order receipts on thermal paper. The printable receipt now has a format switch — A4 invoice (the default, unchanged) or a crisp 58 mm / 80 mm thermal roll that prints to any receipt printer with no page margins, in a tidy monospace layout with your logo on top. The A4 invoice and every receipt link already sent are unchanged.

= 1.18.0 =

* New: a built-in QR code generator (a core utility, `vmzUI.qr`) — no external service or library. It powers scannable QR codes on receipts and labels (for example a review link or order lookup) and is available to the whole platform.

= 1.17.0 =

* New: barcode data quality tools. The product editor now checks a barcode as you type — it flags a UPC/EAN check digit that doesn't match with a one-tap "Fix", and warns if another product or variation already uses the same code. And under Tools → Barcodes, a one-click "Assign internal barcodes" fills every product and variation that has no barcode (from its SKU, or an internal EAN-13 number), so your whole catalog can be scanned at the register and printed on labels. Existing barcodes are never changed.

= 1.16.0 =

* New: the product editor now shows a live barcode preview beneath the Barcode field. As you type a UPC, EAN or Code 128 value it renders the real, scannable barcode on a printable white swatch, names the symbology it detected, and flags a UPC/EAN check digit that doesn't match — so you can see your code is right before you save.

= 1.15.3 =

* Fixed: checkboxes across the admin now each sit on their own line with proper spacing — some pairs (such as "Sold individually" and "Allow backorders" on a product) were crowding onto one row — and the check mark now uses the dashboard theme's accent-contrast colour so it stays clearly visible on every theme.
* Fixed: the tag and category suggestion lists in the product editor are no longer cut off by the surrounding card; they now appear in full below the field.

= 1.15.2 =

* Fixed: customers who sign in with a connected account (such as Google) and never chose a password can now add one. The "Set a password" button opens a secure email-a-link flow instead of a Change Password form that asked for a current password they never had — and once the password is set, they can change it and disconnect providers like any other account.

= 1.15.1 =

* Fixed: on phones, the list cards (Products, Orders, Customers, Attributes, and Bundles) now use the full screen width instead of sitting cramped inside an extra container, so they're easier to read and tap.
* Fixed: the Attributes screen no longer scrolls sideways on phones when the toolbar is crowded — the action buttons wrap onto the next line and the summary tiles stay fully in view.

= 1.15.0 =

* New: the Categories and Attributes screens now match the rest of the admin — a shared search-and-filter bar with clickable count tiles that double as one-tap filters (All · Active · Inactive for categories; All · Select · Color · Image for attributes).
* New: the Attributes list has a table/cards toggle and restacks into cards on phones; the Categories list keeps its drag-and-drop and keyboard reordering (available when no filter is applied) and now filters by search and status.
* Improved: on phones, the add/edit form for a category or attribute has a fixed Save bar so those actions stay in reach on longer forms, and the category reorder controls are always visible on touch.
* Improved: full keyboard, screen-reader, right-to-left (Arabic), and reduced-motion support across the redesigned Categories and Attributes screens.

= 1.14.0 =

* New: the Products and Customers list screens now use the same filter bar as the rest of the admin (search + filters), and their count tiles are one-tap filters — click "Out of Stock" or "Suspended" to filter instantly.
* New: the Customer detail page has been redesigned to match the order detail — on phones it's now a native app with a section switcher (Overview · Orders · Profile · Settings · Email) and a quick-action bar, while the desktop layout is unchanged.
* Improved: the Customers overview tiles now show real, live counts per status (active, pending, suspended) instead of estimates.
* Improved: full keyboard, screen-reader, right-to-left (Arabic), and reduced-motion support across the redesigned list and detail screens.

= 1.13.0 =

* New: the Settings screen has been redesigned to match the rest of the admin — panels are now grouped into clear sections (Setup, Store, Commerce, Governance, System) on desktop, and on phones it becomes a native app-style list you tap into one panel at a time, with a back button in the title bar.
* Improved: every on/off control in the Access & Policies and Security panels is now a modern switch instead of a plain checkbox, and each settings card carries a tidy section icon so panels are easier to scan.
* Improved: the Access & Policies Save button now stays disabled until you change something, matching the Security panel — so it's always clear when there's something to save.
* Improved: full keyboard and screen-reader support for the new switches, right-to-left (Arabic) layout, and reduced-motion support throughout the Settings screens.

= 1.12.0 =

* New: the Orders area has been redesigned to match the product editor — a cleaner, faster experience on desktop and a genuine native-app feel on phones and tablets.
* New: the Orders list now has one unified filter bar with a date-range filter, and the status stat cards are tappable one-tap filters; on phones it shows an easy-to-scan card list.
* New: the order detail page gets an at-a-glance Summary card and a unified Activity timeline (order placed, payments, edits and completion in one feed); on phones it becomes a focused section switcher (Summary · Items · Customer · Fulfillment · Activity) with a sticky action bar and an options sheet.
* New: the Create/Edit order screen restacks its line items into clean, labeled cards on phones so nothing is cut off, and keeps a tidy aligned grid on desktop.
* Fixed: the order detail's admin note now loads the saved note and its Save button works (it previously did nothing).
* Improved: full keyboard and screen-reader support for the new order-section navigation, right-to-left (Arabic) layout, and reduced-motion support throughout the Orders screens.

= 1.11.1 =

* Improved: in the product editor, variation attribute terms now appear as compact, tappable pills that wrap neatly across the row instead of a long vertical checklist — easier to scan and quicker to select on phones.
* Improved: the Shipping section now pairs its fields into two columns (weight/unit, dimensions, shipping/tax class) on phones and tablets, so there is far less scrolling.
* Fixed: the editor's "View on store" button now opens the correct product URL (/p/…).

= 1.11.0 =

* New: the Add/Edit Product screen has been completely redesigned — a clearer sectioned layout with a summary sidebar (status, organization, and a live "readiness" checklist) on desktop, and a fast, native-app experience on phones and tablets with a section switcher, a sticky Save/Publish bar, and slide-up pickers.
* New: mark a product as Featured, add a product video URL, and set quantity/tier pricing (volume discounts) — all directly from the editor.
* New: create a category or tag inline from the product editor, duplicate a product or view it on your storefront from within the editor, and press Ctrl/Cmd+S to save.
* Fixed: a product's visibility setting is now preserved when you edit and save it (it could previously reset to "Visible").
* Improved: at-a-glance section summaries, clearer validation, full keyboard and screen-reader support, right-to-left (Arabic) layout, and reduced-motion support throughout the editor.

Older releases (1.0.x through 1.10.22) are listed in the changelog.txt file that ships in the plugin folder.

= 1.10.22 =
* Security: accounts awaiting approval or email confirmation can no longer sign in through the standard WordPress login form — the same checks the storefront login uses now apply everywhere.
* Security: when "Hide prices from guests" is on, the Google product feed is no longer served publicly (it would otherwise have exposed prices), and a private ("members-only") store no longer publishes a sitemap that lists your catalog URLs.
* Fixed: the "sell to / do not sell to" country rule now also applies to digital orders (using the billing country) and to each destination of a split shipment, so it can't be skipped.
* Fixed: editing an order that contains a stock-tracked bundle now adjusts the bundle's own stock correctly, and order edits save atomically — a database hiccup mid-save no longer leaves an order half-updated.
* Fixed: checkout now declines an item that is below its minimum order quantity with a clear message, instead of quietly increasing the quantity (and the charge); per-line tax on an edited order is apportioned correctly across discounted lines.
* Fixed: a maximum order total accidentally set below the minimum no longer blocks every checkout.

= 1.10.21 =
* Fixed: when you edit a placed order to reduce a quantity or remove an item, each product's "units sold" total is now lowered to match, so your best-seller and sales figures stay accurate. (Previously an edit that returned items to stock left the sold count too high.) Only affects stores using order editing; refunds and cancellations are unchanged.

= 1.10.20 =
* Improved order editing: editing an order that contains a bundle now keeps the bundle and its components together and adjusts the component stock when you change the bundle quantity. A new "Recalculate tax" button in the edit screen works out tax from your store's tax rules and the order's address, instead of typing it in by hand.

= 1.10.19 =
* Added: order editing. Staff can now edit a placed order — change item quantities, add or remove products, update the billing and shipping address, and adjust shipping, discount and tax — while it is still pending, on hold, or processing. The order subtotal and grand total recalculate as you go, and stock is adjusted to match your changes. Off by default: turn on "Allow editing placed orders" under Settings → Access & Policies, and grant the new "Edit Placed Orders" permission to the staff roles that should have it. Fulfilled, completed, cancelled and refunded orders stay locked.

= 1.10.18 =
* Added: per-product backorder policy. Turn on "Allow backorders" in a product's Inventory tab and customers can order it even when it is out of stock — the product stays visible in your catalog, shows as "on backorder" with a working add-to-cart button, and the order goes through. Leave it off (the default) and out-of-stock products behave exactly as before: unpurchasable. Applies per product, so you choose which items accept backorders.

= 1.10.17 =
* Added: an option to make the whole storefront private ("members-only store"). Turn on "Require login to view the store" under Settings → Access & Policies and logged-out visitors are sent to the login page for every store page, with the catalog refused to them — while the login, sign-up and password-reset pages stay open so customers can get in. Signed-in customers browse normally. Off by default; when off, nothing changes.

= 1.10.16 =
* Added: an option to hide prices from logged-out visitors ("catalog mode"). Turn on "Hide prices from guests" under Settings → Access & Policies and guests see "Log in to see price" with no add-to-cart button; signed-in customers see prices and shop as normal. Prices are hidden on the server — they never reach a guest's browser, page source, or structured data — so it is safe for wholesale or members-only pricing. Off by default; when off, nothing changes.

= 1.10.15 =
* Improved: when email verification is on, the storefront login page now guides customers through it. Confirming an email shows a "you can now sign in" banner, an expired or already-used confirmation link shows a clear message with a one-tap "Resend confirmation email", and trying to log in with an unconfirmed account offers the same resend option inline. No settings change; only visible when "Require email verification for new accounts" is enabled.

= 1.10.14 =
* Added: optional per-product order-quantity limits. Each product's Inventory tab now has "Min order quantity" and "Max order quantity" fields — set a minimum to require customers to buy at least that many, a maximum to cap how many they can order, or leave either blank for no limit. The limits are applied when adding to the cart, when changing a cart quantity, and again at checkout. Existing products have no limits set and behave exactly as before.

= 1.10.13 =
* Added: two account-security actions on a customer's detail page. "Send password reset" emails the customer a link to choose a new password, and "Sign out everywhere" ends all of their active sessions so they must log in again. Both are available for customers with an account (not guest records), and neither can be used on your own record or on staff/administrator accounts.

= 1.10.12 =
* Added: optional email verification for new customer accounts. Turn on "Require email verification for new accounts" under Settings → Access & Policies, and each new sign-up must confirm its email address — by clicking a link we send — before it can log in. A "Confirm Email" message is added to the email templates, a resend option is available, and existing accounts are never affected. When the setting is off, nothing changes.

= 1.10.11 =
* Added: optional admin approval for new customer accounts. Turn on "Require admin approval for new accounts" under Settings → Access & Policies, and each new sign-up is created in a pending state and cannot log in until you approve it. A new "Pending" filter on the Customers screen shows everyone awaiting review, where you can Approve (the account becomes active) or Reject (the account is blocked). When the setting is off, accounts remain active immediately, exactly as before.

= 1.10.10 =
* Added: account-security controls on the Settings → Security tab. You can now set a password policy for storefront accounts — a minimum length, and optional requirements for mixed-case letters and a symbol — which applies when customers register, reset, or change their password.
* Added: an optional login lockout. When enabled, an account is temporarily locked after too many failed sign-in attempts, with the number of attempts, the time window, and the lock duration all configurable. When it's left off, the built-in limit of five attempts per fifteen minutes still applies, and sign-in throttling by IP address is always on.
* Every new option defaults to the store's existing behaviour, so this release changes nothing until you choose to tighten a setting.


= 1.10.9 =
* Added: a new "Access & Policies" tab under Settings that gives the store owner direct control over who can sign up and how orders are accepted. From one screen you can set customer registration to open, invite-only or closed; set a minimum and/or maximum order amount; choose which countries you sell to (an allow or a deny list of country codes); and pause ordering during a holiday or stock freeze while the store stays browsable, with an optional message for shoppers.
* Added: you can now name who receives the new-order and low-stock alert emails, as one or more addresses, instead of the alerts always going to the single site admin email. Leaving a field blank keeps the previous behaviour.
* Added: two access switches, previously available only to developers, now have a simple on/off control here — using the branded storefront login in place of the default WordPress screen, and sending shop staff to the VendMizer admin instead of the WordPress dashboard. Full site administrators are never redirected.
* Every option on the new tab starts at your store's current behaviour, so this release changes nothing until you choose to turn something on.

= 1.10.8 =
* Fixed: the plugin's page on WordPress.org was cutting off part of the readme. The directory folds custom sections into the description and shows at most 2,500 words of it, so the end of the "External services" disclosure and the whole "Third-party libraries" note were being truncated on the public listing. The libraries note and the two explanatory paragraphs now live in the FAQ (which has its own, larger allowance), and every section is within the directory's display limits.
* Changed: the changelog in the readme now covers the current 1.10 line only; the full history back to 1.0 is in the new changelog.txt in the plugin folder, as the plugin handbook recommends.
* Added: a LICENSE.txt with the full GPL v2 text, and the terser build script (bin/build-min.mjs) now ships in the package as the readme has always said it does, so anyone can regenerate the minified files from the readable sources that ship alongside them.
* No functional change — every fix in 1.10.6 and 1.10.7 is carried forward unchanged.

= 1.10.7 =

* Housekeeping: every link in the External services list was re-checked against the providers' current sites, and four more that had moved were updated (PayPal's legal hub, Meta's and Vimeo's terms pages, Shippo's privacy notice). Documentation only — no functional change.

= 1.10.6 =

* Security: a blog post marked private could still be listed as a "related post" on a public post, and its view counter could be nudged by a visitor who knew its address. Both now honour the post's visibility, like every other public blog query already did.
* Security: the customer download routes (signed link and delivery history) now confirm the signed-in shopper owns the download at the permission layer, not only inside the handler, and the other "my account" routes (wishlist, saved address, my reviews, session, language) declare a named, documented permission tier instead of a bare logged-in check. Behaviour is unchanged for legitimate use; the trust boundary is simply stated on each route.
* Improved: the one remaining hand-written inline script in the storefront renderer (the screen-size pruner for hidden sections) is now printed through WordPress's own inline-script pipeline, so site security policies that add a script nonce apply to it too.
* Housekeeping: "Tested up to" set to WordPress 7.1, and four links in the External services list refreshed to their providers' current legal pages (Tabby, 2Checkout, Square, EasyPost).

= 1.10.5 =

* Improved: on the shop, category and tag pages the filter sidebar (categories, brands, tags and attributes) now loads in a single request instead of five separate ones, so the filters appear sooner and the store makes fewer round-trips to the server.
* Improved: the storefront's critical styles are now cached instead of being re-read and re-processed on every page view, so pages are served with less work per request.
* Improved: the active announcements shown across the storefront are now cached for a short time and refreshed immediately whenever you add, edit or remove a campaign, so busy stores no longer repeat the same lookup on every page view.

= 1.10.4 =

* Fixed: on load, the storefront briefly showed default colours and then switched to your theme (the "flash"). Your palette — accent, hero, header, footer, page and card colours, sale badge, corner radius and fonts — now paints correctly on the very first frame, in both light and dark mode.
* Fixed: product pages were quietly re-downloading the product data the page already contained, so every product view made a redundant request; they now use the data the server already sent, which loads the product faster and removes the brief loading flash.
* Fixed: on some themes the structured data (JSON-LD) that search engines read was emitted in a form strict parsers reject, which could cost the store its rich results. It is now always valid, so Product, Breadcrumb and Article markup is reliably readable.

= 1.10.3 =

* Added: a "Show in navigation" switch on each custom page (Page settings). Turn it on and the page gets a link in the storefront header and the mobile menu automatically — no need to edit the menu by hand. Pages you already added to the menu yourself are left as they are, never doubled. The link is also present in the server-rendered navigation, so search engines discover the page from anywhere on the store.

= 1.10.2 =

* Fixed: after a storefront page finished loading in the browser, the tab title for custom pages, the About page and the Contact page briefly read "Page Not Found" (and the product page flashed it too) before settling. Each now shows its own title from first paint through load.
* Fixed: the app was clearing the search-engine description on category, brand, tag, product and blog pages once it took over from the server. Those descriptions now stay intact, so a store's category and product pages keep the summary search engines read.
* Improved: on Arabic storefronts the default About and Contact titles and descriptions now read in Arabic instead of English.

= 1.10.1 =

* Fixed: when the storefront runs at your site root ("Use the site home page for the store"), custom pages you build in the Pages workspace now have a clean root address (yoursite.com/your-page) just like the built-in About and Contact pages, instead of only working under the /shop/ path. The address list refreshes automatically whenever you add, rename, enable or disable a page.

= 1.10.0 =

Storefront pages are now always rendered on the server.
* Removed the "Server-Side Rendering" card from Settings → Advanced, together with its switch and per-section list. Server rendering is how the storefront works, not a preference: the home page, product, category, shop, brand, tag, blog and custom pages always arrive as complete HTML for search engines and for a fast first paint, and the app takes over in place. Stores that had the switch off are moved to server rendering automatically on update; the old settings are cleaned up and any page cache is refreshed. Developers keep an emergency switch — see the FAQ.
* Fixed: on the server-rendered home page the interactive layer never attached. Hero carousels did not rotate, prices stayed in the base currency for shoppers who had chosen another one, wishlist and compare states were missing, statistics did not count up, the newsletter form reloaded the page instead of subscribing, and store managers did not see the empty-section prompts. The app now loads its home-page module after the first paint and brings the server-rendered page to life, with guards so a language or currency switch or a sign-in during that moment can never act on a page that is no longer on screen.
* Fixed: a rendering error inside one home-page section could take the whole home page down. The server now falls back — to the simpler renderer, and failing that to the app — so the storefront always stays up.
* Fixed: the shop listing (/products) was rendered on the server only while the SEO module was on; it now is regardless.
* Fixed: a custom page with no sections yet showed a loading spinner instead of its title.
* Improved: server-rendered product rows and testimonials on the home page refresh immediately after a product, category or review change on hosts with a persistent object cache, instead of up to five minutes later.
* Improved: the home-page module is preloaded at low priority so it never competes with the largest image on the page, and the shop listing now preloads its module too.
* Fixed: activating the plugin on WordPress Playground (SQLite) logged a database error from a one-time customer-name backfill that used a MySQL-only function; the statement is now written portably and activation there is silent.

= 1.9.92 =

* Set "Tested up to" to the current stable WordPress version. No code changes.

= 1.9.91 =

Cleared every error reported by the official Plugin Check tool.
* File deletions now use `wp_delete_file()` instead of `unlink()` throughout the backup, restore and email-attachment paths — a drop-in that routes through WordPress's own deletion filter.
* The Backup & Restore feature's direct filesystem calls are now explained and scoped. Building a backup streams each table to a temp file, and restoring streams it back, one batch at a time so a large store never has to sit in memory at once; reading a ZIP entry and serving a multi-gigabyte backup download work on native stream handles that WP_Filesystem has no API for. Each such call now carries a specific, justified annotation rather than being flagged as an oversight, and the removal of the plugin's own temp work-directory is annotated the same way.
* The store's own storefront-loader diagnostic comment now escapes its two interpolated values, even though both were already restricted to `[a-z0-9-]` upstream.
* Two custom-table admin queries (clearing a customer's default address, exporting attributes to CSV) were rewritten or annotated so the table and column identifiers — which are the plugin's own internal names, never request input, and cannot be bound as SQL placeholders — no longer read as unescaped parameters.
* Removed the duplicate "Tested up to" line from the main plugin header; it now lives only in this readme, as the directory requires.
* Version-only bump otherwise; no storefront output, data, or behavior changes.

= 1.9.90 =

WordPress.org plugin review fixes.
* Restore uploads now go through WordPress's own uploader. The backup restore screen staged the uploaded archive with `move_uploaded_file()`, which sidesteps core's own file-type and extension checks. It now hands the file to `wp_handle_upload()`, with a scoped `upload_dir` filter that keeps the staged copy inside the plugin's existing deny-all backups directory and a filename callback that preserves the name the cleanup sweep looks for. The archive is still parsed and validated by the restore checker before anything is applied, and nothing else about restoring changed.
* Blog tags and categories no longer count unpublished posts. The public tag and category lists showed any term whose stored post_count was above zero, and that count was taken over every post that had not been deleted — drafts, scheduled posts, archived posts and private posts included. A tag carried only by an unpublished draft therefore appeared on the storefront, and its number disclosed how many unpublished posts carried it. Both endpoints now derive the count as they answer, over posts a visitor can actually open (published, not deleted, not private), so a term with none of those drops out of the list entirely. The stored count is kept to the same rule, which is what the XML sitemap's blog-category entries read, and it is now refreshed when a post's status or visibility changes and when the scheduler publishes a post — both of which used to leave it stale.
* Documented that several addresses in the source are format identifiers, not services. The SOAP envelopes built for Aramex and Naqel Express, the XML sitemap, the Google product feed and every inline SVG icon have to name their format's XML namespace, which is spelled like a web address but is never contacted. "External services" now says so, and names them, so the distinction is on the record. No service was missing from that list and no request the plugin makes has changed.

= 1.9.89 =

Added: the storefront products API can now be filtered by product type (for example type=bundle), and bundle list rows carry their mode (fixed or Mix & Match). This is what powers the new Bundles and Mix & Match storefront sections in VendMizer Pro; on its own it changes nothing about existing pages — every current request is byte-for-byte identical unless it opts into the new filter.

= 1.9.88 =

Fixed: in the Home editor, a Hero Banner color (Hero Background, Heading, or Subheading Color) appeared to reset to the default after saving and refreshing. The value was saved correctly, but the editor's live preview refreshed the in-memory copy of the section while the sections list was on screen — reading the color inputs before they existed and blanking them — so the field looked empty the moment the section was reopened, and the next save then wrote the blank back. The editor now only reads a section's fields while that section's editor is actually open, so colors (and every other field) persist exactly as saved. The same fix is applied to the content page builder (About, Contact, and custom pages), which shared the identical behavior. No storefront output or saved data changes.

= 1.9.87 =

Fixed: renaming an image in the Media Library no longer appears to revert on the next page load. A media item's name is a display label, but it was being run through WordPress's filename sanitizer — which turns spaces into hyphens and drops accents — so a rename like "My Photo.jpg" was quietly stored as "My-Photo.jpg" and, when your edit differed only in those characters, looked like it had snapped back to the old name after a refresh. Names now keep spaces and Unicode (including Arabic) exactly as typed, and the editor reflects the saved value straight from the server, so what you see immediately after saving is exactly what a refresh shows. Newly uploaded files keep their original readable names too (spaces intact), and the physical file on disk is unaffected either way.

= 1.9.86 =

SEO: the XML sitemap is now a sitemap index with paginated child sitemaps — one set per content type (products, categories, brands, product tags, blog posts, blog categories, and the static pages). The previous single file quietly stopped at 30,000 products (with similar caps on the other types), so a large catalog was partly invisible to search engines; every published URL is now listed, each child file kept to a safe size and carrying a real "last modified" date drawn from the newest item it contains. Nothing changes about which address you submit to Google — /sitemap.xml is still the one — only how it is structured, and small stores see the same URLs as before.

= 1.9.85 =

Fixed: on a store with a persistent object cache, category names in a non-default language (for example Arabic) could show as raw {"en":…,"ar":…} text in the "Shop by category" section. The server-side render and the storefront's data API shared one cache entry but stored it in two different shapes; both now agree, and any stale entry heals itself.
SEO: WordPress's "Discourage search engines from indexing this site" (Settings → Reading) is now honored. With it on, every storefront page emits noindex and the store stops pinging IndexNow, so a pre-launch or staging store is no longer silently indexable.
SEO: product, category and blog-post pages now always carry a meta description — when an item has none of its own, a sensible one is generated instead of leaving the tag empty.
SEO: the About, Contact and custom pages now ship structured data (AboutPage / ContactPage / WebPage plus a breadcrumb), matching every other page, and breadcrumb "Home"/"Blog" labels are translated to the page's language.
SEO: a store-wide Twitter-card setting no longer downgrades the rich image card on product pages; per-page share images on About/Contact are normalized to absolute URLs; each page now advertises its other-language versions to social platforms (og:locale:alternate); and a custom page whose slug begins with "search" is no longer mistaken for the search-results page.

= 1.9.84 =

Improved: clearer signposting between your store-wide SEO settings and each page's own settings, so nothing is hard to find. Settings → SEO now opens with a one-line note of what it covers (store-wide search and social defaults) and a button to jump straight to a page's editor. Each page editor's "Search & social" panel gets a matching link back to those store-wide defaults. Wording and links only — nothing on your storefront changes. This completes the pass that gives every piece of content one clear place to live.

= 1.9.83 =

Improved: your business email, phone and address now have one home. The "Contact Details" card in Settings → SEO is now "Business information" — the single source for these facts, which both feed your store's structured data (Organization / Local Business) and fill in the contact card on your Contact page by default. When a field on the Contact page's contact block is left blank, it now shows the matching business detail automatically; anything you type on the block itself still takes precedence. Nothing changes unless you have entered business details.

= 1.9.82 =

New: the home page now has its own "Search & social" panel inside the Home editor — the same per-page search title, meta description and social-share image controls the About and Contact editors already have, so every page type is edited in one consistent place. The old "Landing Page SEO" box in Settings → SEO is replaced with a link that opens the Home editor; your existing home title, description and image carry over and appear there ready to edit, and nothing on your storefront changes until you do.
Fixed: the per-page Search & social settings on the About and Contact editors did not reappear in the editor after a reload — they were saved and applied to your storefront correctly, but the fields looked blank when you came back. They now show your saved values.

= 1.9.81 =

Cleanup: the "About Page Content" and "Contact Page Content" boxes in Settings → SEO have been retired. They were a second, hidden place to write page text that never actually appeared on the storefront — the About and Contact page editors are where a page's content belongs, and now that is the only place it lives. On update, any text still in those two boxes is moved into a section at the top of the matching page so it finally becomes visible, but only when that page is still on its shipped default layout; a page you have already built is left exactly as it is, and nothing is ever deleted. No visible change for the vast majority of stores, which never used those boxes.

= 1.9.80 =

New: the About and Contact page editors gained a "Search & social" panel, so each page can carry its own search-result title, meta description and social-share image, plus an optional "hide this page from search engines" switch — set right where you edit the page, instead of in a separate settings screen. Every field is optional: left blank, a page keeps the same smart defaults it has today (its name followed by the store name, and an auto-written description), so existing About and Contact pages are unchanged. This is the first step toward giving every piece of storefront content one clear place to live.

= 1.9.79 =

SEO: product structured data (schema.org Product) now reports each product's real brand instead of always naming the store, so rich results and shopping listings show the correct brand. Product offers now include a price-valid-until date, which clears a Google Merchant listings warning. Social and search preview images now carry descriptive alt text (og:image:alt and twitter:image:alt). No visible storefront change.

= 1.9.78 =

Fixed: the arrows on carousel sections (Featured, New Arrivals, On Sale, Deal Spotlight) did nothing on a freshly loaded home page — they only started working after navigating to another page or switching languages. They now respond on first load.
Performance: the storefront's core script now ships the cookie-consent, analytics, WhatsApp and CAPTCHA pieces only to stores that actually use those features, instead of downloading and parsing them on every page of every store. A store with none of them loads about 28 KB less JavaScript per page.
Performance: add-ons can now attach their storefront scripts to the specific page that needs them (for example, a checkout add-on loads only when a shopper reaches checkout) rather than on every storefront page.
SEO: Arabic meta descriptions are no longer cut off mid-word (they were being trimmed by bytes, which halved Arabic text and could leave a broken final character). The Shop and Blog page titles now follow the store's language, blog category and tag pages get their own distinct titles and descriptions instead of all sharing one, and each blog post now has a single top-level heading.

= 1.9.77 =

Fixed: the storefront theme switcher could get stuck on dark. On a store whose default appearance is set to Dark, a visitor whose device is also in dark mode could tap the light/dark/system button with nothing happening — the toggle read the store default as if it were the visitor's own choice, so the cycle never reached light. The switcher now tracks the visitor's explicit choice separately from the store default, so the first tap always flips away from what is on screen and the light/dark/system cycle is always reachable. "System" is now remembered as a real follow-your-device setting.
Fixed: the Deal Spotlight ("Special Deals") homepage section could stay on its loading placeholder and never show its products. On server-rendered home pages the section is now filled with its on-sale products up front, exactly like the Featured, New Arrivals and On Sale sections, instead of waiting for a script that does not run on first load. When there are no products on sale the section is hidden rather than left showing empty placeholders.

= 1.9.76 =

Accessibility: on-accent text (the labels on accent-coloured buttons, the Call-to-Action banner heading, badges and the accent section) now adjusts automatically to stay legible against whatever accent colour you choose. If your set text colour already meets the WCAG AA contrast minimum against the accent it is kept as-is; otherwise the storefront picks black or white by whichever is more readable. This fixes low-contrast labels that could appear with lighter accent colours, and affects only the on-accent text colour — no section, layout or default changes.

= 1.9.75 =

Accessibility: the Testimonials carousel (homepage and content pages) is now keyboard-focusable and labelled, so keyboard users can scroll it (WCAG 2.1 scrollable-region). This is part of a QA pass across the section enrichments — focus outlines on the new interactive controls, responsive collapse at mobile widths, and dark-mode rendering were all verified.

= 1.9.74 =

New: the homepage FAQ section gained the same four controls the content pages have — a search box, a two-column layout, an open behavior (keep several answers open, or one at a time using the browser's native exclusive accordion), and an optional "still need help?" block with a message and button. All off by default, so existing homepage FAQ sections are unchanged. This completes bringing the homepage section builder to parity with the About, Contact and custom pages.

= 1.9.73 =

New: the homepage Testimonials section gained the carousel layout added to the content pages earlier — a swipeable row instead of a grid. (The homepage review cards already show author-initial avatars.) Off by default, so existing homepage testimonial sections are unchanged. Continues homepage parity with the content pages.

= 1.9.72 =

New: the homepage Features section now has the same two controls the About, Contact and custom pages gained earlier — a card style (bordered / plain / shadow) and an optional per-item link that turns each feature into a clickable card. Both are off by default, so existing homepage feature sections are unchanged. Begins bringing the homepage section builder to parity with the content pages.

= 1.9.71 =

New: the Call-to-Action Banner on the About, Contact and custom pages gained an optional secondary (outline) button beside the main one, and a background image with an adjustable overlay so the text stays readable over it. Both are off by default, so existing banners are unchanged. Sixth of the per-section enrichment updates.

= 1.9.70 =

New: the Testimonials section on the About, Contact and custom pages gained a carousel layout (a swipeable row instead of a grid) and optional author-initial avatars on each review card. Both are off by default, so existing testimonial sections are unchanged. Fifth of the per-section enrichment updates.

= 1.9.69 =

New: the FAQ section on the About, Contact and custom pages gained a search box that filters the questions as you type (with a "no matching questions" message when nothing matches), and a two-column layout for longer lists. Both are off by default, so existing FAQ sections are unchanged. Fourth of the per-section enrichment updates.

= 1.9.68 =

New: the FAQ section on the About, Contact and custom pages gained two controls — an open behavior (keep several answers open at once, or switch to one-at-a-time), and an optional "still need help?" block below the questions with a short message and a button that links wherever you choose. Existing FAQ sections are unchanged until you set these. Third of the per-section enrichment updates.

= 1.9.67 =

New: the Features section on the About, Contact and custom pages gained two controls — each feature card can now link somewhere, turning the row into clickable cards, and a card style (bordered / plain / shadow) sets how the cards look. Existing feature sections are unchanged until you set these. Second of the per-section enrichment updates.

= 1.9.66 =

New: the page hero on the About, Contact and custom pages gained real controls — a call-to-action button (with a solid or outline style), a height setting (compact / standard / tall), and an image-overlay strength (none through strong) for legible text over a background image — turning a plain title block into a proper hero. Existing heroes are unchanged until you set these. This is the first of the per-section enrichment updates.

= 1.9.65 =

Improved: the page builder's section library now marks Pro sections with a small "Pro" badge, on the homepage and on the About, Contact and custom-page builders. Where a free section and a Pro section cover related ground — for example Featured Products and the Pro Product Carousel — the library now reads as clear tiers instead of lookalike duplicates.

= 1.9.64 =

New: universal Section Style. The About, Contact and custom pages now have the same per-section presentation controls the homepage has — a background band (muted or accent), spacing (compact or spacious), and a hairline divider (top / bottom / both) — and a new max-width control (default / narrow / wide) is added to every section on every page, so a section's content can be tightened for readability or opened up wider. Sections with no style set render exactly as before. This completes Phase 2 (Section Style + the audience/schedule/device Conditional Visibility shipped in 1.9.61–1.9.63).

= 1.9.63 =

New: Conditional Visibility is now complete with screen-size targeting — each section can be hidden on desktop, tablet and/or mobile ("Hide on devices"), alongside the audience control (1.9.61) and the schedule (1.9.62). Because the server cannot know a visitor's device, a device-hidden section is sent in the page but removed from the DOM before it paints and before any of its JavaScript runs — never hidden with CSS — and on in-app navigation it is never rendered at all. Every part of Conditional Visibility (audience, schedule, devices) now sits in one group on every section, on the homepage and on the About, Contact and custom pages, and applies to Pro sections too.

= 1.9.62 =

New: Conditional Visibility now includes scheduling — each section can be shown only within a date-and-time window ("Show from" / "Show until", in the store's timezone), on top of the audience control added in 1.9.61. Outside its window a section is omitted from the page entirely (server- and app-rendered), not merely hidden, and it works on Pro sections too. Anonymous pages that contain a scheduled section use a shorter 60-second shared cache so a start or end time takes effect within about a minute, while every other page keeps the full 5-minute cache.

= 1.9.61 =

New: Conditional Visibility — every storefront section (on the homepage and on the About, Contact and custom pages) can now be shown to everyone, only to logged-in shoppers, or only to logged-out visitors, through a new "Visible to" control in the page builders. Hidden sections are omitted from the page entirely — not merely hidden with CSS — so they add no markup and run no code, on both the server-rendered and app-rendered storefront, and the choice works on Pro sections too. This is the first of the conditional-visibility controls; date/time scheduling and screen-size targeting follow.

= 1.9.60 =

Fixed: on stores that use the Pro "Recently Viewed" section, the homepage no longer shows two Recently Viewed blocks. The built-in Recently Viewed that is appended automatically at the bottom of the homepage is now suppressed whenever a Pro Recently Viewed section is placed in the layout, so a returning shopper sees their viewing history only once. Stores without the Pro section are unchanged — the built-in block still appears. Applies to both the server-rendered and app-rendered homepage.

= 1.9.59 =

Improved: the storefront now renders — and fully wires up — every content section on every content page. After 1.9.58 unified the page builders, the About Us, Contact Us and custom Pages storefronts share one set of section renderers, so a contact form added to the About page actually submits, a features or text-and-image block added to the Contact page displays with the full design system, and licensed Pro sections work on all of them. Disabled sections are skipped entirely rather than hidden with CSS, so a turned-off section adds no markup and runs no code.

= 1.9.58 =

Improved: the About Us and Contact Us page builders now offer the full section library — every content section plus every licensed Pro section — exactly like custom pages, instead of a small fixed subset. Previously About showed only 10 sections, Contact only 6, and neither could add any Pro section. All content pages now share one unified section catalog, so the "Add section" library is the same everywhere.

= 1.9.57 =

Improved: the page builder's section field engine gained a comparison-table editor — a 2‑D grid where the products you pick become columns and each feature row holds a value per product (values are keyed to the product, so they survive adding, removing or reordering columns). Shared groundwork for premium comparison sections.

= 1.9.56 =

Improved: the page builder's section field engine gained a searchable product picker — type to find products by name (with thumbnails) and pick one or many — complementing the category picker added in 1.9.55. The storefront search-suggestions endpoint now also returns each product's id. Shared groundwork so premium storefront sections can feature hand-picked products.

= 1.9.55 =

Improved: the storefront page builder's section field engine now supports a searchable multi-category picker and conditional "show-when" fields, so section editors can offer richer, context-aware controls — a field can appear only when it's relevant. This is shared groundwork that lets premium storefront sections present cleaner, smarter editors.

= 1.9.54 =

Improved: every storefront page now lives in one place. The admin sidebar's standalone Homepage, About Us and Contact Us items have been folded into the Pages screen, which already lists them together with any custom pages — so there is a single, unambiguous home for editing pages and no more duplicated menu entries. The Design hub's "Content & pages" shortcuts and the Page Builder's Exit button now return you to the Pages screen. The individual page editors are unchanged and open from the Pages list; existing links and bookmarks to those pages still work.

= 1.9.53 =

Improved: the Page Builder's live preview now offers three responsive widths — Desktop, Tablet and Mobile — and shows the narrower ones as a properly centred device (rounded frame and shadow) on a neutral canvas. Previously only Desktop and Mobile were available and the mobile preview sat pinned to one side against an empty strip; now every width is centred with no wasted space. Applies to every storefront-page editor (Homepage, About, Contact and custom pages), in light and dark themes.

= 1.9.52 =

Improved: the Homepage builder now uses the same focused, two-panel editing screen introduced for the other storefront pages. Editing the homepage sets the dashboard chrome aside and splits into a searchable section rail and a persistent live preview of your store; picking a section slides in its settings with a clear way back, the Add-section library is searchable in-panel, and each row carries its heading, an enable switch and a move / duplicate / delete menu. All the homepage's specialised controls — hero colour pickers, product and category pickers, sliders and per-section styling — and the bilingual fields work exactly as before. Every storefront-page editor now shares one consistent builder. Verified in light and dark themes, with the live preview updating as you edit and section changes saving correctly.

= 1.9.51 =

Improved: the storefront Page Builder for About, Contact and custom pages has a redesigned, focused editing screen. The dashboard chrome steps aside so the builder fills the screen, split into two panels — a section rail on one side and a persistent live preview of your store on the other, so you always edit against what you'll ship. The rail lets you search a page's sections, and picking one slides in its settings with a clear way back; a searchable Add-section library replaces the old popup. Each section row now shows its heading beneath its type, a quick enable switch, and a menu to move, duplicate or delete it. Every field keeps exactly the same bilingual editing as before. The Homepage builder is unchanged for now. Verified across About, Contact and custom pages in light and dark themes, with the live preview updating as you edit.

= 1.9.50 =

Improved: the Section SDK's data-defined editors now include an image field. An add-on section can declare an image field — on its own or inside a repeating row — and the page builders render it with the familiar media picker (choose or replace an image from your library), save it, and pass it through to both the live storefront renderer and the server-side renderer. As before this is additive groundwork behind the scenes: the free sections and their editors are unchanged, and a store without the add-on sees nothing new. It's what lets the Pro Section Library add image-rich sections.

= 1.9.49 =

Improved: the Section SDK now lets an add-on define a section's editor as data. A section can declare its fields — including repeating rows (add, remove, edit) plus dropdowns, icon pickers and toggles — as a schema, and the page builders render and save that editor generically, with no bespoke code per section. This is additive groundwork behind the scenes: the free sections and their editors are unchanged, and a store without the add-on sees nothing new. It's what lets the Pro Section Library grow quickly.

= 1.9.48 =

Added: a Section SDK so the VendMizer Pro add-on can bring its own storefront sections into the page builders. The section catalog (the "Add Section" grid), the live app renderer, the server-side renderer, and the save step each gained a small extension point, so an add-on section shows up in the grid, edits with the same fields, renders on the storefront with full server-side rendering for SEO and first paint, and saves without being dropped. This is entirely additive: the free sections are untouched, unknown section types are still skipped safely everywhere, and a store without the add-on sees no change. It's the groundwork for the Pro Section Library.

= 1.9.47 =

Improved: the Homepage editor now has Live preview — the same docked storefront preview the About, Contact and custom-page editors already have — so every page in the Pages workspace previews the same way. Open it beside the editor and the store's home page updates as you work: change a heading, reorder or toggle a section, and see it live before you Save, with no page reload. Behind the scenes all four editors now share one live-preview engine, so behavior is identical everywhere and future improvements land in one place. Your homepage content and the Save flow are unchanged. Verified across the homepage, About, Contact and custom-page editors, with edits reflected live and zero console errors.

= 1.9.46 =

Improved: custom pages, About and Contact now render their full content on the server. Previously only a custom page's title and description reached search engines while the body was drawn after JavaScript loaded; now every section — hero, text-and-image, features, stats, team, timeline, FAQ, rich text, calls-to-action and contact details — is present in the initial HTML, so crawlers index the real content and shoppers see the page straight away instead of a brief skeleton. About and Contact also switched to rendering the very same builder sections you edit (they previously used a separate legacy body), so what search engines and first-time visitors see now matches what you build in the editor and what the live app shows. The app still takes over on load exactly as before, and your existing pages are untouched. Verified across custom, About and Contact pages in light and dark mode and right-to-left layouts, with the app cleanly replacing the server-rendered page and zero console errors. This completes the server-side-rendering step promised in 1.9.45.

= 1.9.45 =

Improved: custom pages are now first-class. You can add any custom page to your storefront navigation menu with a new destination picker (built-in routes plus your own pages), so shoppers can reach them from the menu. Each custom page also gets its own SEO title and description and is listed in your sitemap.xml, so search engines can find and index it. (Full server-side rendering of custom-page content, which also improves first paint, is the next step.)

= 1.9.44 =

Added: custom pages + the Pages workspace. A new Pages screen in the admin lists every storefront page in one place — the built-in Home, About and Contact, plus pages you create yourself. Add a page, give it a title and URL (e.g. /shop/shipping), and build it with the same section builder and live preview the other pages use. Custom pages render on the storefront at their own address, can be toggled on/off, renamed, re-slugged and deleted, and their slugs are protected from clashing with store routes. This is the headline step of the unified Pages workspace; server-side rendering for SEO and sitemap listing for custom pages follow next.

= 1.9.43 =

Added: live preview in the page builder. The About and Contact editors now have a Live preview button that docks the real storefront page beside your editor and updates as you type — change a heading, add or reorder a section, and see it instantly, before you Save. Same-origin and read-only; nothing is published until you hit Save. This is the second step toward the unified Pages workspace, and the preview mechanism new merchant-created pages will use too.

= 1.9.42 =

Improved: unified the page-builder engine behind the scenes. The About and Contact editors now run on one shared section-builder core instead of separate near-identical copies, so both stay perfectly consistent and future page features land in one place. No change to how the editors look or work, and your existing About/Contact content is untouched — this is groundwork for an upcoming unified Pages workspace with live preview and merchant-created pages.

= 1.9.41 =

Added: per-section spacing and dividers. Alongside the background band from 1.9.40, each homepage section can now set its own vertical spacing — Default, Compact (tighter) or Spacious (roomier) — and an optional hairline divider on its top, bottom or both edges, all in the Design hub (Home page → the section). Together they let you control the rhythm and separation of the page instead of every section sharing one fixed spacing. Each option is independent and optional; leave them at Default/None and the section renders exactly as before. Applied on both the server-rendered page and the live app (including the pre-paint critical CSS, so above-the-fold sections don't shift on load). Verified byte-for-byte unchanged at default, and in light and dark mode and right-to-left layouts.

= 1.9.40 =

Added: per-section backgrounds. Each homepage section can now be given a background band — Default, Muted (a subtle shade for alternating rhythm) or your brand Accent — in the Design hub (Home page → the section → Background), so your homepage no longer reads as one flat wall of identical sections. Set it per section; leave it Default and everything looks exactly as before. It is applied on both the server-rendered page and the live app, so there is no flash on load. Verified in light and dark mode and in right-to-left layouts.

= 1.9.39 =

Added: text size. A new Text size control in the Design hub (Brand → Typography) scales all of your storefront's text together — Small, Default or Large — so you can dial the whole store's type up or down to suit your brand voice or audience. Default keeps your current sizes exactly as they are. The size is applied before the page paints, so changing it never makes the page jump on load. It previews live. Verified in light and dark mode and in right-to-left layouts. (Note: the transactional checkout screens keep their own fixed sizing.)

= 1.9.38 =

Added: accent gradient. Set an optional second color in the Design hub (Brand → Accent gradient) and your primary buttons become a smooth two-stop brand gradient (from your accent to the second color) — a quick way to a more premium, modern look. It is scoped to the main call-to-action buttons; the accent's other uses (icons, badges, progress bars, outline buttons) stay solid so nothing looks off. Leave it blank for a solid accent, exactly as before. It previews live and holds through hover. Verified in light and dark mode and in right-to-left layouts.

= 1.9.37 =

Added: header style controls. You can now set your header's height (Compact / Regular / Tall) and the line under it (Border / Shadow / None) in the Design hub (Brand → Header style), right alongside your header colors. The height is applied before the page paints, so changing it never makes the page jump on load. It previews live; leave the defaults and your header looks exactly as before. Verified in light and dark mode and in right-to-left layouts.

= 1.9.36 =

Added: status & rating colors. You can now set the colors used for stock states and review stars — in-stock / success, low-stock warnings, errors & out-of-stock, and the rating stars — instead of being limited to the built-in green, amber, red and gold. They live in the Design hub (Brand → Status & rating colors), apply in both light and dark mode, and preview live; leave any blank to keep its default. With this, every color on your storefront is now yours to set. Verified in light and dark mode and in right-to-left layouts.

= 1.9.35 =

Added: corner roundness. One control in the Design hub (Brand → Corner roundness) now sets how round your whole storefront is — buttons, inputs, cards and images together — so the store reads coherently sharp or soft instead of only the buttons being adjustable. Drag the slider, or pick Sharp / Rounded / Round, and watch it update in the live preview; it applies across the shop, product and content pages. Existing stores are left exactly as they are until you move it. Verified in light and dark mode and in right-to-left layouts.

= 1.9.34 =

Added: storefront surface & text colors. You can now set your store's page background, card, text and border colors instead of being limited to the built-in white and near-black — the single biggest step away from a store that still looks like the default. Each has a separate light-mode and dark-mode value in the Design hub (Brand → Surface & text), so both appearances stay on-brand; leave any blank to keep that default. It previews live and applies across the shop, product and content pages. Verified in light and dark mode and in right-to-left layouts.

= 1.9.33 =

Added: custom web font. You can now use your own brand typeface on the storefront, not just the bundled and system fonts. In the Design hub (Brand → Typography → Custom font) give your font a name and the URL of a .woff2 hosted on your own site, then choose that name as your Body or Heading font. For privacy and performance the font must be **self-hosted on your own domain** — third-party font-CDN links are rejected, so no visitor data leaks to another service and there is no extra cross-origin request on your pages (the same reason the built-in fonts are bundled locally). It previews live, and it is loaded only when you actually select it. Verified in light and dark mode and in right-to-left layouts.

= 1.9.32 =

Added: dark-mode logo. A new **Dark mode logo** field in the Design hub (Brand → Store Identity) lets you set a second logo that the storefront shows when it is in dark mode, so a logo made for a light background doesn't disappear on a dark one. Leave it empty and your main logo is used everywhere, exactly as before. The right logo is chosen before the page paints and swaps instantly with the theme, so it previews live alongside the default-appearance setting. Verified in light and dark mode and in right-to-left layouts.

= 1.9.31 =

Added: second image on hover. A new **Second image on hover** option in the Design hub (Brand → Product cards) cross-fades a product card to the product's second photo when a shopper hovers it — a familiar catalog touch that previews a back, detail or in-use shot without a click. It applies across the shop, category and search grids, works alongside any card style and hover effect, and previews live. Products need at least two images; those with only one simply don't swap. It is off by default and honours reduced-motion (an instant swap, no fade). The storefront only loads the second image when the option is on, so stores that leave it off carry no extra page weight. Verified in light and dark mode and in right-to-left layouts.

= 1.9.30 =

Added: product grid columns. A new **Columns** control in the Design hub (Brand → Product cards) sets how many products appear per row on desktop — 3, 4 or 5 — across the shop, category and search pages, and it previews live. This also fixes the shop grid, which previously ignored its column setting and always showed four. Tablet and mobile column counts adjust automatically so the grid never crowds, and any homepage section that sets its own column count keeps it. Four columns is the default, so existing stores are unchanged until you choose otherwise. Verified in light and dark mode and in right-to-left layouts.

= 1.9.29 =

Added: sale badge colour. A new **Sale badge color** control in the Design hub (Brand → Storefront Colors) sets the colour of SALE and discount badges across your store, so they can match your brand instead of the default red. The badge text colour adjusts automatically for readable contrast whatever colour you pick, so there is nothing to get wrong, and it previews live. Leave it blank to keep the default red. Verified in light and dark mode and in right-to-left layouts.

= 1.9.28 =

Added: product grid spacing. A new **Grid spacing** control in the Design hub (Brand → Product cards) sets how much room sits between products in the shop grid — Comfortable (the current airy spacing), Cozy, or Compact for a denser layout that shows more products at once. It applies across the shop, category, search and homepage product grids, at every column count and on mobile, and previews live as you change it. Comfortable is the default, so existing stores look exactly as before until you choose otherwise. Verified in light and dark mode and in right-to-left layouts.

= 1.9.27 =

Added: default storefront appearance. A new **Default appearance** control in the Design hub (Brand tab) lets you choose whether a first-time visitor sees your store in light, dark, or their device's own setting — useful when your brand is distinctly light or dark. Shoppers can still switch it themselves with the header theme toggle, and their choice is remembered on their device. The default is applied before the page paints, so there is no flash of the wrong mode, and it previews live as you change it. Existing stores keep following the visitor's device until you choose otherwise, so nothing changes until you opt in. Verified in right-to-left layouts.

= 1.9.26 =

Added: product card styling. A new **Product cards** control in the Design hub (Brand tab) lets you shape how products look everywhere they are listed — the shop grid, collections and search. Pick a card style (flat, a hairline border, or a soft shadow), an image shape (square, portrait or landscape) and a hover effect (zoom the image, lift the whole card, or none). Changes appear instantly in the live preview, and each starter theme now sets a fitting card look you can adjust. Nothing changes until you save, defaults reproduce the previous appearance exactly, and it was verified in light and dark mode and in right-to-left layouts.

= 1.9.25 =

Added: a live storefront preview in the Design hub. A **Preview** button opens a panel beside the customization tabs that shows your real storefront, and it updates the moment you make a change — adjust an accent colour, pick a font or button shape, or tap a starter theme, and the preview reflects it instantly, before you save. Switch between a desktop and a phone view to check both, refresh it, or pop it open in a new tab for a full-size look. Nothing touches your live store until you press Save, so you can shape the look with confidence and only commit once it's right. Verified in light and dark mode and in right-to-left layouts.

= 1.9.24 =

Added: starter themes. The Design hub now opens on a **Themes** tab with six ready-made looks — VendMizer, Minimal, Ocean, Sunset, Editorial and Midnight — each a coordinated set of storefront colours, a font pairing and a button shape, shown as a live little preview. Apply one in a single tap to give your store a polished, on-brand look instantly, then fine-tune it in the Brand tab. Applying a theme keeps your logo, store name and content untouched, and you can switch themes or adjust any individual value whenever you like — so getting a good-looking store is no longer a blank-page exercise.

= 1.9.23 =

Improved: all of your storefront's look-and-feel now lives in one place. The Navigation screen has become a **Design** hub, and a new **Brand** tab brings your store identity (name, logo, favicon), storefront colours, typography and button shape together with the existing Header, Menu, Footer and Mobile Nav tabs — so styling your store is one screen instead of a hunt across several. As part of this, the old **Branding** settings tab moved into Design → Brand; the **Appearance** settings tab is renamed **Dashboard Theme** and clarified, because it themes the admin dashboard (this control panel) and not your public store; and typography moved out of the **Storefront** settings tab into Design → Brand. The hub also links straight to the Homepage, Announcement bar and About/Contact page editors. Existing links and bookmarks keep working, and nothing you had already configured changes — this reorganises where the controls live, not what they do.

= 1.9.22 =

Fixed: several storefront interface issues on mobile. The sticky header now stays pinned to the top of the screen while you scroll (a page-level setting was quietly stopping it from sticking), and it slides away as you scroll down and returns as you scroll up for a cleaner reading view. The bottom navigation bar now honours its "Hide on scroll" option — it slides down out of the way as you scroll into a page and comes back when you scroll up. The cart and wishlist count badges on the bottom bar are no longer clipped; they show as a full circle in every language and layout (this was a side effect of the 1.9.21 bottom-bar update, where a text-trimming rule was also catching the icon). The redundant page title was removed from the shop and checkout pages: the shop page already shows its location in the breadcrumb, and the checkout page is self-evident, so the large heading only pushed the content down — the "Return to cart" link now sits cleanly at the top of checkout. Both headings are preserved for screen readers. Right-to-left layouts and light/dark mode were verified throughout.

= 1.9.21 =

Added: the mobile bottom navigation bar is now fully customisable, from Navigation → Mobile Nav in the admin. You can choose how the selected tab is marked (a coloured icon, a filled pill behind it, an underline or a small dot), whether tab labels are always shown, shown only under the selected tab, or hidden for an icon-only bar, and whether the bar sits edge to edge or floats as a rounded card with an adjustable corner radius. The bar's surface can be solid or a frosted blur over the page, its top hairline border can be turned off, and you can set a custom active colour, bar background and notification-badge colour — each optional and falling back to your theme when left unset — plus a numeric count or a plain dot for the cart badge. Every tab can be shown to everyone, only to signed-in shoppers, or only to signed-out visitors, so (for example) an Account tab can appear once a shopper logs in. If you enable more than five tabs, the extras now collapse into a tidy "More" sheet instead of crowding the bar. The customer dashboard's bottom bar automatically mirrors these choices, so the storefront and the signed-in account area stay visually consistent, in light and dark mode and in right-to-left languages. Defaults reproduce the previous appearance exactly, so nothing changes until you opt in.

= 1.9.20 =

Fixed: in the customer account area on mobile, the bottom tab you tap now turns your primary colour immediately, instead of only after a page refresh. The selected tab was being drawn correctly, but a hover style — which on a touch screen sticks to the last-tapped element — was overriding the active colour because it carried higher CSS priority, so the just-selected tab showed a muted grey until the next reload. The hover tint is now limited to real pointer devices and never applies to the active tab, matching the public storefront bottom bar.

= 1.9.19 =

Fixed: buttons, the active mobile bottom-nav tab, badges and accent sections now render in your exact chosen primary colour again. A recent accessibility pass had derived a darkened shade of the brand accent for these interactive surfaces to raise text contrast, which made a bright brand colour (for example the storefront blue) appear navy on the Add to Cart button, the sign-in button and the active tab while the rest of the store kept the true accent — so they no longer matched. The storefront now uses the merchant's primary colour directly on those surfaces, matching the hero and the rest of the theme, in both light and dark mode.

= 1.9.18 =

Improved: the address bar now reflects the language you are browsing in. Previously, switching the storefront to another language (for example Arabic) re-rendered the page but left the visible URL as the default-language address, so copying the link straight from the browser — or refreshing, or bookmarking — lost the language, even though the Share buttons already added it. The storefront now keeps the active language on the URL as you browse, switch language or move between pages (the default language keeps the clean address), so the link you see and copy always matches what is on screen and matches the Share buttons and the page's published alternate address. This completes the shared-link language fix from 1.9.17. The change is client-side only and has no effect on what a link-preview crawler sees.

= 1.9.17 =

Fixed: sharing a product or blog link while browsing in a non-default language (for example Arabic) now unfurls the preview in that language on iMessage, WhatsApp, Facebook, X and the rest. Previously the Share, Copy Link and social buttons handed out the bare page address with no language marker, so the service that fetched the link to build its preview card — which carries no store session and runs no JavaScript — always fell back to the store's default language, showing an English title and description for an Arabic page. The share and copy actions now append the shopper's active language to the link (using the same ?lang parameter the storefront already publishes as each page's official alternate address), so the preview matches what the shopper is viewing and search engines see the correct language for the shared URL. The default language keeps the clean address unchanged. On-page canonical and Open Graph URLs were aligned to the same rule so a rendered page in a non-default language references itself rather than the default-language URL.

= 1.9.16 =

Improved: staff sign-in and sign-out now behave like an admin area should. A store manager who signs in through the storefront login page is taken straight to the store dashboard (previously they stayed on the storefront with a "Go to dashboard" notification), and a manager signing out — from the dashboard or anywhere else — always lands back on the branded admin login screen, the same convention as wp-admin logging out to wp-login. A signed-out redirect target is no longer honoured for staff, so a crafted logout link can't choose where a manager lands. Customer sign-in and sign-out are unchanged.

= 1.9.15 =

Fixed: on a brand-new WordPress site still using the "Plain" permalink setting, the login-page takeover redirected wp-login.php to the store's login address — which cannot resolve without rewrite rules — leaving the administrator staring at the theme instead of a login form. The takeover (and every related login/logout redirect) now stands down automatically while permalinks are "Plain", so the standard WordPress login keeps working, and a dashboard notice explains that the storefront needs a pretty permalink structure and links to Settings → Permalinks; the moment any structure is saved the store routes install and the takeover arms itself. Found by testing activation on a clean WordPress 7.1 install, where "Plain" is the default.

= 1.9.14 =

Housekeeping from a full guidelines self-audit — no user-facing behaviour changes.
Cron: removed an unused one-minute cron schedule that nothing ever scheduled on, and raised the shipping status poller's minimum interval from 1 minute to 5 minutes (its default remains 30 minutes, and each shipment is still debounced to at most one poll per 15 minutes) — every recurrence this plugin schedules by default is now five minutes or slower.
Documentation: the "Third-party libraries" section now also records that every minified JavaScript file of the plugin's own (`assets/js/**/*.min.js`) ships beside its complete readable source, and how to regenerate the minified files with the included build script.

= 1.9.13 =

Security/standards hardening for the WordPress.org plugin review — no user-facing behaviour changes.
Database: every SQL statement that filters or orders by a list of ids now binds each value through wpdb::prepare() with one placeholder per value (the IN () and FIELD () lists), and every table name is written inline as {$wpdb->prefix}vendm_… in the query string itself instead of passing through an intermediate variable or object property — over 1,100 query sites were converted and re-verified. The analytics currency-conversion SQL fragment now also allowlists the column reference it is built around.
REST API: intentionally public, read-only storefront endpoints (products, categories, brands, tags, blog, testimonials, announcements, currencies, tracking, cart read, store pages) now declare the literal `__return_true` permission callback, per the plugin-review convention that makes public intent machine-checkable; privileged endpoints keep their capability, nonce, signature, throttle or token checks unchanged.
Compatibility: the Ajax nonce-recovery helper no longer carries a hardcoded `/wp-admin/admin-ajax.php` fallback — the URL always comes from `admin_url()` via the boot config (correct on subdirectory installs), and when the config is unavailable the helper defers to the page-reparse strategy instead of guessing a path.
Naming: the internal object-cache group was renamed from `vmz` to `vendmizer` and the two cache keys without a plugin prefix (`currency_settings`, `enabled_modules`) became `vendm_currency_settings` / `vendm_enabled_modules`, keeping all cache identifiers on the plugin's own prefix.
Fixed: a diagnostics helper restored wpdb error suppression to a hardcoded value instead of the caller's previous state; it now saves and restores the prior state and catches Throwable.

= 1.9.12 =

Fixed: two storefront REST endpoints could return HTTP 500 on some hosts, which showed up as browser console errors and cost points on PageSpeed's Best Practices audit. The home page's "recent reviews" widget reads GET /store/reviews/0 (reviews across all products) and the cart badge reads GET /cart on every page. The reviews query extracted the product name with a JSON function that, on strict MySQL (unlike the more lenient MariaDB), aborts the whole query if any product's stored name is not valid JSON — surfacing as a 500 on PHP 8.1+ hosts, where the database driver throws on error. That extraction is now guarded so a malformed name falls back to its raw value instead of failing the request. In addition, both reads are now wrapped so that any unexpected database fault degrades to a valid empty result (an empty review list, an empty cart) that the page renders normally, rather than a 500 — appropriate for read-only endpoints that must never break the page. The fault is logged server-side (when WP_DEBUG is on) for diagnosis. No functionality changes: reviews and carts load exactly as before.

= 1.9.11 =

Performance: the storefront JavaScript is now minified. It is built ahead of time with a real parser (terser) into `.min.js` files that ship in the plugin, and the asset bundler serves those in place of the readable sources, cutting the storefront bundle roughly in half over the wire and speeding up hydration. On a throttled mobile profile the shop page's PageSpeed Performance score rose from the high-70s to the low-90s and its Total Blocking Time dropped by around 350 ms; nothing about how the store behaves changes. Add `&min=0` to an asset URL, or run `node bin/build-min.mjs`, when developing.
Fixed: a layout shift on the home page. The inline critical CSS (used for the first paint) had drifted out of step with the main stylesheet on three points — the category row's padding, the hero's height at desktop widths, and the section spacing and heading size — so the page visibly re-flowed the moment the main stylesheet finished loading. The two are now kept in lockstep, taking the home page's desktop Cumulative Layout Shift from 0.105 to about zero and its PageSpeed Performance to 100. The category row, hero and section rhythm now reserve their final dimensions from the very first paint.
Improved: the critical CSS is now minified before it is inlined into each storefront page, clearing the "Minify CSS" audit and trimming the render-blocking inline bytes on every view.
Improved: the shop page is now server-rendered with a real product grid (breadcrumb, heading and the first page of products) so it paints meaningful content immediately and is fully crawlable, instead of showing a placeholder until the app loads.
Improved: the active promotional announcement is delivered inline with the page instead of being fetched separately, removing one network request from every storefront view.
Accessibility: the "Skip to content" link is now the first element on the page, ahead of the announcement region, so it is the first stop for keyboard and screen-reader users and no content sits outside a landmark. Accessibility stays at 100/100 (0 axe-core violations) on both the home and shop pages.

= 1.9.10 =

Accessibility: the storefront now meets WCAG AAA colour-contrast on the home and shop pages, verified with axe-core and Lighthouse (both reach 100/100 for Accessibility). Link text, the active bottom-nav tab and every accent-filled button now derive an automatically-darkened shade of your brand colour that stays readable — whatever colour you choose — instead of using the raw brand colour, which could fall below the contrast minimum. The shop sort menu gained an accessible name, and the announcement bar is now exposed as a labelled landmark to screen readers. None of this changes how the store looks beyond making low-contrast brand colours legible.
Improved: the shop image on category, brand and tag pages loads with priority (the first row eager, the rest lazy) so the main image appears sooner, and the server-rendered hero now reserves its final height to avoid a layout shift as the page finishes loading.

= 1.9.9 =

Improved: performance on large stores. The heaviest screens were reworked to stay fast as the catalog and order history grow, verified by load testing at 100,000 products and 300,000 orders:
* Storefront catalog pages now load about ten times faster on a large catalog. Sorting the shop grid (by newest, price or best-selling) previously re-scanned every matching product on each page; new sort-aware indexes let the store read only the products it needs, cutting the catalog query from roughly 190 ms to under 20 ms at 100,000 products.
* The admin Orders screen opens several times faster on a store with a long order history. Counting the total number of orders for the pager no longer joins in customer records it does not need, taking that step from roughly 660 ms to 50 ms at 300,000 orders.
* The Inventory screen now opens quickly on a large catalog. Sorting the stock list by quantity previously re-scanned every managed product on each page; a new sort-aware index lets it read only the page it needs, cutting that query from roughly 130 ms to under 1 ms at 100,000 products.
* The sales report (revenue over a date range) now reads only the orders in the chosen period instead of scanning the entire order history. For a typical 30-day report on a store with 300,000 orders, that takes the query from roughly 120 ms to 30 ms, and it stays fast however long the store's history grows.
* Catalog and inventory totals (the product and stock counts shown on listing and inventory screens) are now cached briefly and refreshed automatically whenever products, categories or stock change, so those counts no longer re-run a full tally on every page view.
None of these change how the store behaves or the figures it reports — only how quickly these screens respond as a store scales.

= 1.9.8 =

New: custom checkout fields now appear in the checkout section you assign them to — Contact, Shipping address, Billing address or Additional information — instead of all landing together under "Additional information". A field falls back to the Additional area whenever its section isn't on the page (a digital-only order has no shipping step; billing that matches shipping shows no billing block), so a field is always reachable and never lost, and it moves to its proper place the moment that section appears.
New: a number checkout field's minimum and maximum, and a text field's maximum length, are now applied by the shopper's browser as they type (the store already enforced them on submit).

= 1.9.7 =

Fixed: orders placed in a currency that was later disabled now still convert to your base currency in reports and analytics. The rate lookup skipped currencies that were switched off, so historical orders taken in a currency you no longer offer fell back to an unconverted figure and could distort totals; every currency's rate is now available for converting past orders, whether or not it is currently offered at checkout.

= 1.9.6 =

Fixed: refunding an order paid via Moyasar no longer errors. The handler called an email method that does not exist, which failed the refund webhook and — on a partial refund — could record the refund a second time when the gateway retried. Refunds now complete and the refund email is sent, matching every other payment gateway.
Fixed: the "Enabled" switch on a direct-shipping carrier now shows its on/off graphic. It was rendered without its slider element, so it appeared as bare "Enabled" text with no visible control.
Fixed: switching the Tracking & Analytics module off now actually stops Google Analytics and the Meta (Facebook) Pixel from loading on the storefront. They previously kept loading whenever their IDs were saved, regardless of the toggle — a consent/privacy gap.
Fixed: the storefront catalog display toggles now take effect. "Show related products", "Show recently viewed", "Enable price filter" and "Enable attribute filters" were ignored (those sections always rendered or stayed hidden regardless of the setting), and the related / recently-viewed counts now follow the configured number instead of a fixed one.
Hardened: an inbound contact webhook whose body field arrives as a non-string value can no longer cause a fatal.

= 1.9.5 =

Fixed: newly added or imported data now appears on the Dashboard right away instead of sometimes needing a manual page refresh. The Dashboard's figures are cached briefly for speed; that cache is now cleared reliably whenever products, orders or a bulk import change the underlying data — including on stores running a persistent object cache, where the previous cache-clear silently did nothing and the old numbers lingered.
Fixed: after the plugin is updated, the admin console and the customer account pages are no longer served from the browser's cache. A stale page could otherwise load the previous version's scripts, which showed up as the dashboard appearing not to load until a hard refresh.
Improved: on the PDF invoice, an item's add-on selections, gift-card recipient and bundle components no longer overlap the next line when several of them stack up under one item.

= 1.9.4 =

Improved: the admin order screen and the customer's account order view now also list a POS bundle's components indented under the bundle line, matching the receipt, invoice and order email. Previously those two on-screen views showed the bundle but not its contents for POS-style bundles (whose components are stored on the parent line rather than as separate rows).

= 1.9.3 =

Improved: a bundle sold at the POS register now keeps its components on every document. Previously a POS bundle listed its contents only on the immediate on-screen receipt and lost them on a reprint or invoice. With the components now saved on the order (see the VendMizer Pro update), the invoice and the order email both list them indented under the bundle, the same way an online bundle's components appear.

= 1.9.2 =

Improved: order line-item totals and details now read consistently across the invoice, the order emails, the admin order screen and the customer's account order view. Specifically:

Invoice: each line's Total is now shown before tax, so the Total column adds up to the Subtotal line (POS invoices previously showed a tax-inclusive line total that did not sum to the subtotal). The invoice now also lists a product's add-on selections and a gift card's recipient beneath the item, matching the receipt.

Order emails: the order-summary block now shows every amount that makes up the total — tip (with its percentage), redeemed and earned loyalty points, the payment fee, the coupon code on the discount line, gift card and store credit, and the shipping method name — so the listed lines always reconcile with the emailed grand total. Add-ons and gift-card recipient details now also appear on the status-update and resent order emails, not only the confirmation.

Admin order screen: a bundled line item is no longer left off the order — it always renders (indented under its bundle when the parent is a bundle, otherwise as its own line). Gift card, store credit, redeemed points and the coupon code were added to the order totals.

Customer account order view: a product's add-on selections, the tip, redeemed and earned loyalty points, and the custom checkout fields ("Additional information") the customer entered at checkout now appear, matching the invoice and emails.

= 1.9.1 =

Fix: an order's PDF invoice could leave line items off. There were two causes. Alternating "zebra" rows were painted in the stripe's own light-grey colour, so on any multi-item order every second item was drawn grey-on-grey and looked blank — the item was there, just invisible. Separately, an item linked to a bundle by the admin-order convention (bundle_parent_item_id) was skipped from the item list but never redrawn under its parent, so it disappeared completely. Invoices now render every ordered item — genuine bundle components indented beneath their parent, everything else as its own line — and can never silently drop a line, so the items on the invoice always match the order. The same "never drop a line item" fix was applied to the HTML order-email receipt.

= 1.9.0 =

Fix: "Attach PDF receipt to order emails" now works. The setting had always defaulted on and promised a PDF on order confirmation, shipped and delivered emails, but nothing in the sending code ever generated or attached the file, so no email ever carried one. Order emails now attach the order's PDF invoice (named Invoice-<order number>.pdf) to both the customer and admin messages when the setting is on; the file is built at send time and removed straight after, and the on/off toggle is honoured. The receipt travels with the email on every path — the automatic send, an admin "Resend" from the email log, and any queued retry — so a resent order email now carries the same PDF as the original. The set of events that carry the receipt can be adjusted with the new vendm_pdf_receipt_events filter.

Improved: the PDF receipt now reflects the whole order and its totals always reconcile. Tips/gratuity and redeemed loyalty points were absent from the totals, so any order with a tip or a points redemption printed a grand total that did not match the lines above it — both now appear (the tip with its percentage, the redemption with the points spent). The discount line names the coupon that was applied, points earned are noted under the total, and the customer's order note is printed on the receipt. Orders without any of these simply omit the corresponding lines, so ordinary receipts look the same as before.

Fix: the Sandbox / Test Mode switch was invisible on the Authorize.net, Klarna, 2Checkout and Payoneer payment gateway settings — the control used a stylesheet class that no longer exists, so only its text label showed. The switches now render correctly (on, off, and in right-to-left layouts).

= 1.8.1 =

Fix: the checkout page could show "Failed to load page module" and never render. In 1.8.0 the checkout script was inadvertently given its own closing line, but that script is deliberately an open fragment that the checkout bundle closes only after the payment-gateway code is appended — so the early close broke the assembled bundle and the whole checkout failed to start. The stray line has been removed and the checkout loads normally again. No settings or data are affected; this is a checkout-script fix only.

= 1.8.0 =

New: split shipping — a single order can now be sent to several addresses at once, each address getting its own items, its own shipping method and its own shipping cost, all settled as one payment. At checkout a signed-in shopper can turn on "ship to several addresses", assign each item to one of their saved addresses, and pick a shipping method per destination; the running total, the confirmation page, the order emails and the account order view all show the per-destination breakdown. The pricing engine ships in the free plugin but stays dormant behind a switch that only VendMizer Pro's new "Multiple Shipping Addresses" module turns on — every price is computed and summed on the server, so the amount a shopper confirms is exactly what is charged. A store without Pro is byte-for-byte unchanged: the ordinary single-address checkout is untouched.

= 1.7.0 =

New: the Saved Address Book is now a built-in, free module — signed-in shoppers can save multiple addresses and pick one at checkout instead of retyping. It appears in the Module Manager (Settings → Modules) where you can turn it on or off; it is on by default. This feature previously required VendMizer Pro; existing saved addresses are kept exactly as they were.

= 1.6.0 =

New: custom checkout fields can now show or hide based on the cart's products, a product category in the cart, the chosen shipping method, or the chosen payment method — not just another field's value. The storefront applies the rule live as the shopper changes payment, shipping or the cart, and the server enforces the same rule authoritatively so a hidden field is never required and its value is never saved. Dormant unless VendMizer Pro's field editor sets such a rule, so a store without the editor is unchanged.

= 1.5.0 =

New: custom checkout fields can include a File upload type — a shopper attaches a file (for example a prescription, an ID or artwork) during checkout, and it appears as a secure download link on the order in the admin, on the confirmation and status emails, and in the CSV export. Dormant unless VendMizer Pro's field editor offers the type, so a store without the editor is unchanged. Uploaded files are stored under a random, unguessable name outside the web-guessable path, with the folder hardened against script execution, and unreferenced uploads are swept automatically.

= 1.4.1 =

* Fixed: custom checkout fields under "Additional information" now render with the storefront's field styling — text, number, email, phone, date, dropdown, textarea, checkbox, radio and multi-select each display correctly with a proper label, and no longer overflow the card or size a checkbox like a large empty box.

= 1.4.0 =

New: custom checkout-field values now appear on the order confirmation and status emails, the PDF invoice, and the CSV order export (previously admin-only). Dormant unless VendMizer Pro captures them, so a store without the editor is unchanged.
* New: the checkout supports conditional fields — a field can show or hide based on another field's value, evaluated live as the shopper types or chooses.

= 1.3.0 =

New: extension hooks that power the saved-address book (VendMizer Pro's Multiple Shipping Addresses).
* New: the account Addresses page and checkout can host a multi-address book — a saved-address picker at checkout and a "save this address" option — dormant unless VendMizer Pro provides it, so nothing changes on its own.
* Fixed: the admin order view now shows multi-select checkout-field values with their labels.

= 1.2.0 =

New: extension hooks that power the Checkout Field Editor (VendMizer Pro).
* New: the checkout can render custom fields and the order can capture their values — dormant unless VendMizer Pro provides them, so nothing changes on its own.
* Improved: the admin order view now shows any custom checkout-field values captured on an order.

= 1.1.0 =

New: Backup & Restore — download a backup of your whole platform or just your store data, and restore it later.
* New: a Backup & Restore tab under Tools. Create a backup (choose what to include — catalogue, orders, customers, content, configuration, logs), download it as a single file, and restore it later for recovery or for moving to another install.
* New: restoring always takes a safety backup of your current data first, shows you what a file contains before you commit, and refuses a backup made by a newer version than the one installed.
* Security: restore only ever writes into VendMizer's own tables and options (validated against the live schema) and verifies every part of a backup against its checksum before trusting a byte of it.
* Note: this first release backs up the database and settings; bundling media and downloadable files is coming next.

= 1.0.27 =

Friendlier, consistent empty states across the settings & config lists.
* Improved: Product Tags, Tax Rates, Downloads, Shipping Zones, Shipping Packages and Webhooks now show a proper empty state — an icon, a short explanation, and a one-click next step — instead of a bare "No results" line.
* New: Downloads and the Webhooks delivery log now tell you when it's your filters that found nothing (with a one-click "Clear filters") versus a genuinely empty list.

= 1.0.26 =

The Blog Posts list joins the Table/Cards family; Inventory gets a friendlier empty state.
* New: the Table / Cards toggle now works on the Blog Posts list, remembered per device — cards keep the title, author, status and date readable on a phone. Clicking a card opens the same post editor as a table row.
* New: a proper empty/no-results state on Blog Posts, with a one-click way to write your first post (or a hint to clear filters when a search or filter finds nothing).
* Improved: the Inventory screen now shows a proper empty state — an icon, a short explanation, and a next step — instead of a bare "No results" line when a filter finds nothing or there are no stock-tracked products yet.

= 1.0.25 =

Reviews and Brands lists get the Table/Cards toggle; friendlier empty states.
* New: the Table / Cards toggle now works on the Reviews and Brands lists, remembered per device. Selecting, approving, viewing and deleting reviews — and editing or deleting brands — all work the same in either view.
* Improved: Reviews shows a filter-aware empty state (a "no reviews in this status" message versus a genuinely empty list), and the Categories screen now shows a proper empty state with a one-click way to add your first category.

= 1.0.24 =

Coupons list gets the Table/Cards treatment.
* New: the Table / Cards toggle now works on the Coupons list, remembered per device — cards keep the code, discount, usage and status readable on a phone. Selecting, bulk actions, edit-on-click and delete all work the same in either view.
* New: a proper empty/no-results state on Coupons, with a one-click way to add your first coupon (or a hint to clear filters when a search finds nothing).

= 1.0.23 =

Transactions list joins the Table/Cards family; Staff gets smarter empty states.
* New: the Table / Cards toggle now works on the Transactions list — the dense ten-column table becomes readable cards on a phone, remembered per device. Tapping a card opens the same transaction detail as a table row.
* New: Transactions shows a proper empty state that tells you whether it's your filters or simply no activity in the selected period.
* Improved: the Staff list now says "No staff match your search" on a filtered miss (instead of always offering to invite your first staff member), while a genuinely empty team still gets the invite prompt.

= 1.0.22 =

Orders and Customers lists get the Products treatment.
* New: the Table / Cards toggle now works on the Orders and Customers lists too — cards make both readable on a phone, and the choice is remembered per device.
* New: proper empty and no-results states on both lists, with a one-click "Clear filters" when a search or filter finds nothing.
* Improved: on the Customers list, clicking a card opens the customer (the rows already did); Orders cards open the order the same way. Action buttons on a card are never triggered by an accidental card click.

= 1.0.21 =

Products list: helpful empty states and click-to-edit rows.
* Improved: an empty or no-results list now shows a proper empty state — an icon, a short explanation, and a next step — instead of a bare "No results" line. A filtered list that finds nothing offers a one-click "Clear filters"; an empty catalogue offers "Add product".
* New: click anywhere on a product row or card to open it for editing (the row already showed a pointer cursor, now it does what it promises). The checkbox, star, and the edit/duplicate/delete buttons keep working on their own — an accidental row click never fires them.
* Improved: the row edit and duplicate buttons read a little stronger and highlight on hover, so the row actions are easier to spot.

= 1.0.20 =

Product editor: clearer money, computed, SEO and checkbox fields.
* Improved: the Regular, Sale and Cost price fields now show your store's currency symbol inside the field, so it's obvious you're entering money and in which currency.
* Improved: computed and read-only values (like the variable-product stock roll-up and the sold-count) now look distinctly non-editable — muted, with a dashed border — instead of looking like fields you can type into.
* New: the SEO Meta Title and Meta Description fields show a live character counter that turns amber as you near the recommended length and red once you pass it.
* Improved: one consistent checkbox look across the whole admin — the plain form checkboxes now match the ones used in the product tables and image gallery.

= 1.0.19 =

Products list: table or cards, your choice.
* New: a Table / Cards switch on the Products list. Cards make the list readable on a phone, where the wide table used to run off the screen after the product name; the table stays for everything it does best. Your choice is remembered on each device, and on a small screen cards are the starting point — the table is always one tap away.
* This is a shared control built to spread to the other list screens (Orders, Customers and the rest) next.

= 1.0.18 =

Product editor: one unified image gallery.
* Improved: the Images tab is now a single gallery instead of a "Primary Image" dropzone competing with the gallery below it. Drag any image to reorder it, star any image to make it the primary one shown first, and give every image its own alt text — not just the primary. One place, one model.
* New: image order is saved the moment you drop, in a single atomic call, so a reordered gallery can't end up half-applied.
* Fixed: a new product now keeps each image's alt text and your chosen primary image from the very first save, rather than always forcing the first-uploaded image to be primary and dropping the alt text.

= 1.0.17 =

Product editor: safer, faster add-a-product flow.
* New: leaving a half-edited product now warns before discarding — whether you click another menu item, the back arrow, or reload the tab — so an accidental navigation no longer throws away unsaved work. Saving (or explicitly confirming the discard) clears the warning.
* New: the SKU field is pre-filled with a unique, editable code on a new product, so a quick add no longer stalls on "SKU is required". Type over it any time; the server still enforces uniqueness.
* Improved: required-field validation now jumps to the offending tab, rings the field, marks the tab in the rail, and shows an inline message — instead of only a toast that left you hunting across tabs for what was wrong. The message clears the moment you start fixing it.

= 1.0.16 =

Gift card codes at checkout.
* Fixed: the checkout gift-card field silently dropped the digits 0 and 1 from a code before checking it, so a card whose code contained either (a migrated or hand-entered code — auto-generated codes leave out ambiguous glyphs) always came back "invalid gift card" with no way for the shopper to tell why. The field now keeps every letter and digit, exactly as the server stores them. Auto-generated codes never contained 0 or 1, so nothing changes for them.

= 1.0.15 =

Shared tax resolver for the point of sale.
* New: a location-aware tax-rate resolver the register uses to tax an in-store sale by its own branch's jurisdiction, matching the same rates the online checkout applies (most-specific location wins — a city rate beats a state rate). This closes a Pro-side bug where every register billed one arbitrary rate regardless of branch and the cashier's payment screen previewed a different one; it needs VendMizer Pro 1.0.17+ to take effect at the register.

= 1.0.14 =

Admin reliability and polish.
* Fixed: opening a Pro admin screen (POS, Branches, Subscriptions, Loyalty, Returns, Gift Cards, Tickets, Add-ons, Affiliates) by its direct link — a bookmark, a browser refresh, or a pasted URL — could land on the dashboard instead of the page you asked for. Two causes: the asset endpoint answered a request for an unknown page module with the whole admin app, which re-booted the interface mid-load and reset the route; and the initial route could resolve a moment before an add-on's page had registered. Both are closed, and add-on pages now open reliably from a cold load however you reach them.
* Improved: staff and profile avatars fall back to the same coloured initials the rest of the admin already uses when a Gravatar image can't load (privacy blockers, an offline or intranet site, or a region that blocks Gravatar), instead of showing a broken-image icon.

= 1.0.13 =

Multi-branch staffing follow-up — the attribution control, and tighter scope enforcement.
* New: an "Online order branch" control on Settings → Checkout — attribute website orders to the store's default branch (the default) or leave them unassigned. A customer's local-pickup choice still wins, and single-location stores are unaffected.
* Security: branch scope now also covers opening or acting on an individual order, bulk order actions, and bulk stock updates. A staff member limited to specific branches can no longer reach another branch's order by its link, change it in a bulk action, or push a bulk stock update to a branch they aren't assigned to — closing the gaps the first release left on the single-order and bulk paths.

= 1.0.12 =

Multi-branch staffing — assign staff to branches, and make that scope a real boundary. (Multi-branch itself requires VendMizer Pro.)
* Staff: Invite and Edit now include a "Branch access" step, shown once the store runs more than one branch — grant all branches, or a specific set with a primary. Assignment used to live only on the Branches screen; it now travels with the role, and the staff list shows each member's branches.
* Security: a staff member limited to specific branches now only sees those branches' orders, inventory, analytics and exports, and can only adjust stock or open a register at a branch they are assigned to. Previously the branch selector was a convenience filter a scoped member could simply drop to see every branch's figures; it is now enforced on the server. Owners and head-office staff still see everything.
* Online orders now record a branch — attributed to the store's default branch (a customer's local-pickup choice still wins), so branch reports finally include web sales.

= 1.0.11 =

* Account dashboard: fixed the glance cards (subscriptions, points balance, store credit, orders) breaking their layout once their figures loaded. On narrow screens a card carrying a longer note — the store-credit line — stretched its own column and pulled the two-column grid out of alignment; the cards now hold an even grid whatever the content, the longer note trims with an ellipsis instead of overflowing, and the "All Caught Up" state lines up at the same height as the number cards. CSS only — no markup or data changes.

= 1.0.10 =

* Housekeeping: removed unused legacy code paths and tidied related comments and documentation. No functional change.

= 1.0.9 =

* Updated "Tested up to" to WordPress 7.1.

= 1.0.8 =

* Documentation: the contact module's code comments now note that its mail-settings routes require the Manage Settings capability, matching the 1.0.6 permission change. No functional change.

= 1.0.7 =

* Corrected the Tabby links in the External services list to their current locations. Documentation only — no functional change.

= 1.0.6 =

* Contact form mail settings (outgoing SMTP, inbound IMAP, and the inbound-mail webhook) now require the Manage Settings capability rather than the Manage Contact Inbox capability. Staff who manage the inbox keep full access to read and reply to messages; changing the mail-server configuration is now an administrator-level action.
* Cached shipping labels are written through the WordPress filesystem API, and the label folder's directory-listing guard is now an empty index.php. No change to how labels are stored or opened.
* Housekeeping for the WordPress.org plugin directory review: the "External services" disclosure is now a top-level readme section. Its contents are unchanged — every service was already listed.

= 1.0.5 =

* Initial release. Everything listed in the description above ships in this first version: catalog, cart and checkout, orders, customers, coupons, inventory, shipping and tax, reviews, wishlist, digital downloads, the storefront and blog, analytics, the cookie consent banner, the product sitemap and Merchant Center feed, contact inbox, media library, staff roles, outgoing webhooks, maintenance and coming-soon modes, and the bilingual English/Arabic interface with right-to-left support.
* The storefront does not take over your site's front page unless you turn that on in Settings.
* Uninstalling keeps your store data by default. Removal is a choice you make before deleting the plugin, not the default behaviour.
* The media library accepts SVG uploads, and digital-download products accept fonts, e-books and archives as well as documents and images.
* Signed receipt links carry an expiry and stop working thirty days after the order is placed; the window is adjustable with the `vendm_receipt_link_ttl` filter.
* Queued store email is sent by a five-minute cron event rather than a one-minute one, which is far kinder to shared hosting and still well inside any delivery expectation.
* Trust badges, the homepage editor's starter rows and the setup wizard describe what your store offers rather than shipping promises of our own. Nothing claims free shipping, a returns window or a point-of-sale feature on your behalf.

