=== AuctionForge ===
Contributors: SmoothByte IT
Tags: auctions, bidding, auction house
Requires at least: 7.2
Stable tag: 3.7.1.25

== Changelog ==

= 3.7.1.25 =
* **New: BidSpirit Shop support.** BidSpirit's July 2026 API release added a Shop -- a fixed-price storefront the auction house manages alongside its auctions, bought outright rather than bid on. AuctionForge can now import it and sell from it. Switch it on under Settings -> Synchronization -> "Enable BidSpirit Shop" (off by default, so nothing changes on sites that do not use it); the house must also have Shop enabled on BidSpirit itself. Shop items are imported as ordinary lots under their own Shop page, so the existing catalog layout, CDN images, categories, site search, pagination and cache purging all apply unchanged, and purchases go through BidSpirit's shop/purchaseShopItem. Buyers need an account but do NOT need per-auction approval, so the shop is a lower-friction channel than an auction. Sold items stay listed and show their purchase price instead of a Buy button. The shop refreshes on the hourly sync, because BidSpirit sends no webhooks for shop stock. This is separate from the older "Auctions as shop" setting, which presents a real auction as a storefront -- both can be used on the same site and neither affects the other. No schema or config-version change.
* **Sold shop items are now marked as sold.** A shop item that has been bought stays listed but shows "Sold for" with the price it made, on both the shop page and the item page, instead of still showing a purchase price with no Buy button. A new setting, Settings -> Synchronization -> "Hide sold shop items", removes them from the listing entirely if you would rather they did not appear (off by default). Both apply only to the BidSpirit Shop; the older "Auctions as shop" presentation is unchanged.

= 3.7.1.24 =
* **Fixed: edits to pages, posts and Elementor templates did not clear the page cache.** Cache purging was wired only to auction changes, so editing a page -- in Elementor, in the block editor, or by changing a menu or a Customizer setting -- left the previous version cached and served to visitors until it expired on its own: up to 24 hours on an nginx FastCGI cache, plus whatever a CDN in front of it holds. It looked intermittent rather than broken because an unrelated auction sync would eventually purge the whole site anyway. Saving any page, post, lot, Elementor template, menu or Customizer setting now purges the cache, and all purges triggered by one request are combined into a single purge. Purging also now happens after the page has been sent back to the browser, so the editor's Update button no longer waits on the CDN. No schema or config-version change.

= 3.7.1.23 =
* **Fixed: lot videos showed "Error 153 - Video player configuration error" on iPhones and iPads.** Lazy-load plugins (WP Smush, LiteSpeed, WP Rocket) rewrote the lot gallery's YouTube `<iframe>` so it loaded from `about:blank` via `contentWindow.location.replace()`, which sends no `Referer` header on WebKit -- so every browser on iOS (Safari *and* Chrome) hit YouTube's `EMBEDDER_IDENTITY_MISSING_REFERRER` rejection while desktop and Android played the same video fine. The gallery iframe now carries `class="skip-lazy"` (honoured by all three lazy-loaders) plus native `loading="lazy"`, `referrerpolicy="strict-origin-when-cross-origin"` and YouTube's standard `allow` list, so it keeps lazy loading without losing the referrer. No schema or config-version change.

= 3.7.1.20 =
* **Fixed: "Auto commit mysql disabled" silently stopped sync jobs from completing.** On sites with that option ON, the lot importer set MySQL `autocommit=0` but never restored it, so the sync worker's "mark job completed" write was left in an uncommitted transaction and rolled back — every catalog/item sync job showed as failed even though the lot data saved. The importer now restores `autocommit=1` after it commits. Sites with the option off (default) are unaffected. No schema change.

= 3.7.1.19 =
* **Fixed: catalog category filter emitted invalid SQL and returned no lots.** When a listing used `bp_index` purely as a JOIN marker (e.g. the `bp_lots_by_category` widget / category-filtered catalog), `BidspiritQuery` built `CAST(bpMeta.\`itemIndex\` AS UNSIGNED)  0` with no comparison operator — a MariaDB syntax error that failed the whole query, so category-filtered listings showed nothing and the site error log filled with "error in your SQL syntax" (66k+ entries on the affected dev site since March). The itemIndex clause now emits a valid no-op when no comparison is supplied; real filtering (term/category) is unchanged. No schema or config-version change.

= 3.7.1.18 =
* **Added: admin control to require (or waive) bidder phone verification.** A new "Phone Verification" setting on the *Settings → Profile & Users* tab exposes the previously CLI-only `require_phone_verification` option as a dropdown — Automatic (required outside the UK), Always required, or Never required. Auction houses whose BidSpirit account is not provisioned for SMS phone verification can now switch it to "Never required" from wp-admin so bidders are no longer blocked at bid time. No schema or config-version change.

= 3.7.1.16 =
* **Fixed: auction pages returned HTTP 500 when an auction had no day schedule.** `page_auction.php` called `count()`/`foreach` on the `auction_days` term meta, which is `null` for auctions without a day schedule -- a fatal `count(null)` TypeError under PHP 8 that white-screened the auction page for visitors and search crawlers. `auction_days` is now normalised to an array before use. No schema change.

= 3.7.1.15 =
* **Cache now clears when a catalog is published or expanded.** Populating an auction's catalog imports its lots, which purged each lot page and its parent catalog page but NOT the template-rendered auction *listing* pages (home / archive / house pages). An auction that had just gained its first lots kept showing "Catalog available soon" on cached listings until the cache TTL expired. Importing a brand-new lot now also purges the listings -- coalesced into a single full purge per sync on shutdown (no per-lot thrash); plain lot updates are unaffected.

= 3.7.1.14 =
* **Cache now clears when an auction changes state (upcoming→live→ended).** Auction state transitions are time-based and arrive as `auction_state` term-meta writes (the `bp_auto_close` cron, SafetySync, the import pipeline) — which fire `updated_term_meta` but not `edited_<taxonomy>`, so the existing purge hooks never saw them and a finished auction kept showing "Enter Live Auction" / stayed in the upcoming list until the cache TTL expired. `CachePurge` now also hooks `added/updated_term_meta` for `auction_state` on auction terms and purges the listings on any state change.
* **nginx full-purge is now host-scoped.** On a shared server the FastCGI cache directory is used by every site, so a full purge (auction-level change) used to wipe unrelated sites' caches too. `NginxCacheSupport::purgeAll()` now parses each cache file's stored KEY and deletes only entries belonging to the current site's host.

= 3.7.1.13 =
* **nginx FastCGI/proxy cache purge on catalog & lot changes.** Sites fronted by nginx `fastcgi_cache` (micro-caching) now purge the same URLs already sent to Cloudflare and LiteSpeed, so catalog and item pages refresh on sync instead of serving a stale cached copy. Opt-in per site and completely inert otherwise, via a new `NginxCacheSupport` layer in `CachePurge`. Configure with `AF_NGINX_PURGE_URL` / `AF_NGINX_PURGE_METHOD` (the `ngx_cache_purge` module), or `AF_NGINX_CACHE_DIR` / `AF_NGINX_CACHE_LEVELS` / `AF_NGINX_CACHE_KEY` (direct cache-file deletion), or simply activate the rtCamp "Nginx Helper" plugin and AuctionForge delegates to it. Constants (wp-config.php) are preferred; matching `af_nginx_*` options also work.

= 3.7.1.12 =
* **Open-invoices banner now honours "Enable My Invoices tab".** The site-wide "This account has open invoices / Pay Now" banner rendered even on sites with the Invoices tab disabled, and its button led to a profile tab that does not exist. The banner (and its unpaid-bills lookups) is now skipped entirely when the tab is off: `BidspiritViews::bp_footer()` emits `window.afBillingTabEnabled` from the same `enable_billing_tab` setting that gates the tab, and `checkUnpaidBillsBanner()` returns immediately when it is false.
* **US auction houses resolve to the US "Catalog" dialect again (Auto mode).** Restores the address-signature detection lost in an earlier merge: US addresses end in "City, ST 12345" with no country name, so the state+ZIP tail was mistaken for an unknown country and resolved to "Catalogue". `Dialect::countryToDialect()` now recognises a US state code + 5-digit ZIP tail (and a bare trailing ZIP with a `$` currency as a secondary signal). Named countries — including Canada and Australia, whose `$` currencies and 4-digit/lettered postcodes do not match — still resolve to "Catalogue".
* **LiteSpeed purges during sync no longer stack admin notices.** Per-lot cache purges call LiteSpeed's `Purge::purge_url()` quietly instead of the public `litespeed_purge_url` action, which enqueued a dismissible "Purge url …" admin notice for every changed lot — hundreds after a full-auction sync. Manual purges elsewhere keep their confirmations; falls back to the action if the LiteSpeed class is unavailable.
* **Sync log no longer records one row per skipped auction.** The import worker logged "  Skipping auction N (excluded or not matched)" for every excluded auction on every run — the dominant source of `af_sync_log` growth on multi-house sites. It now logs a single "Skipped N/M auctions (excluded or not matched)" summary per run.
* **Housekeeping.** Removed stray `.bak` working files that had been shipping inside the plugin tree since 3.7.1.4.

= 3.7.1.11 =
* **"Auction is live" popup on lot pages.** When BidSpirit reports an auction as live, single-lot pages now show the "Auction is live — Enter live auction" popup (linking to the BidSpirit bidding console), exactly as auction catalogue pages already did. Previously a one-lot auction had no live indication anywhere on the site, because its catalogue URL redirects straight to the lot page and only the catalogue code path surfaced the live state.

= 3.7.1.2 =
* **Background sync worker spawn no longer times out under load ("Worker spawn HTTPS failed: cURL error 28").** `SyncQueue::spawnWorker()` triggered the worker via a blocking loopback request with a hard-coded 5s timeout, so whenever the site's own backend was slower than 5s the spawn aborted as a hard error and raised a sync alert even though the site was up. The timeout is now **15s by default** (overridable via the `AUCTIONFORGE_WORKER_SPAWN_TIMEOUT` constant or `auctionforge_worker_spawn_timeout` filter), the `127.0.0.1` loopback now uses the site's own scheme (avoiding an http→https redirect that forced the slower fallback), and a final non-blocking best-effort request now starts the worker when the backend is merely slow — logged as a warning rather than an error, with the hourly SafetySync cron as backstop. No schema/config change; healthy hosts behave exactly as before.

= 3.7.1.1 =
* **Active uptime monitoring (Kuma keyword health marker).** The keepalive mu-plugin now emits an invisible `<!-- af-health:ok -->` marker on every rendered front-end page (via `wp_head`/`wp_footer`). This lets Uptime Kuma verify the site is genuinely serving WordPress by pulling the homepage and asserting the marker is present — replacing the unreliable push-heartbeat model, which measured WP-Cron liveness rather than real availability and routinely false-flagged perfectly healthy sites as down. The marker lives in the mu-plugin, so it survives AuctionForge being deactivated or mid-update. Bundled keepalive bumped to 1.3.0 (auto-reinstalls on update); the legacy push heartbeat is retained this release for a safe transition.

= 3.7.0.43 =
* **Submission "Need help?" contact is now per-site.** The vehicle-submission help panel previously shipped a single hardcoded email/phone, so every site showed the same (wrong) contact regardless of tenant. It now reads the auction house's email and phone from the site's stored business details (SEO & AEO Optimizer LocalBusiness: `local_email` / `local_phone`), with `apply_filters('auctionforge_help_email')` / `apply_filters('auctionforge_help_phone')` overrides for other sources, and a neutral fallback that hides a row — or the whole block — when no value is set.

= 3.7.0.42 =
* **Submission form markup & content fixes (SEO).** The vehicle-submission templates rendered in the site footer on every page used `<h1>` headings ("Submit your vehicle for auction", "Detail received successfully"), producing multiple H1s in the page source. These are now `<h2>`, leaving a single H1 per page. Replaced the shipped Lorem-ipsum placeholder copy and the dummy "Need help?" contact details (`support@domain.com` / `+3265464325`) with real, production-appropriate text.

= 3.7.0.41 =
* **No more duplicate auction listings** — A brand-new auction could be created twice in WordPress when two syncs ran at the same moment (e.g. a bulk re-sync overlapping a catalogue webhook), each creating its own `auction_number` term before the other committed. The auction then appeared twice and its lots were split across the two copies. `Auction::save()` now serialises term creation per auction with an advisory lock and re-checks authoritatively against the database before inserting, so an existing auction is updated in place instead of duplicated. Complements the existing per-auction catalogue-sync lock that already prevents duplicate lot posts.

= 3.7.0.40 =
* **Lot countdown shows minutes** — The "begins to close" countdown on lot pages now ticks live and shows days/hours/minutes (and seconds in the final minute), so a lot less than an hour from closing no longer reads "0 days and 0 hours". Falls back safely to the previous display on stale-cached pages.

= 3.5.0.6 =
* **Bid Event Tracking** — All successful bids (regular, catalog, pre-sale buyout, post-sale purchase) are now recorded in the user tracking system with `event_type: bid`, capturing the bidder's email, IP, device fingerprint and full browser identity.
* **Self-Healing Database Schema** — Auto-detects and adds any missing tracking table columns on version upgrade.

= 3.5.0.5 =
* **Self-Healing Database Schema** — The plugin now auto-detects and adds any missing tracking table columns on version upgrade, ensuring sites running older database schemas seamlessly migrate without manual SQL intervention.

= 3.5.0.4 =
* **API Key Auto-Healing** — The WordPress plugin now seamlessly self-registers a fresh API Key when WPDeploy returns an `api_key_collision` quarantine blocker. This fully automates the security handover when deploying exact database clones to testing or proxy staging servers without showing ominous red warning cards to developers.

= 3.5.0.3 =
* **API Key Collision Defense** — Implemented an strict identity verification system to prevent cloned testing sites from polluting the central WPDeploy tracking network.
* **Network-Level Quarantine** — Central API dynamically extracts and strictly validates the incoming payload domain against the registered API Key domain, forcefully blocking mismatched traffic (HTTP 403).
* **Local Critical Alert Banner** — Added a persistent crimson warning banner in the WordPress admin panel that intercepts `api_key_collision` errors, directly instructing developers to update cloned keys.

= 3.5.0.2 =
* **Modal Trigger Hotfix** — Restructured internal UI rendering priority to gracefully deploy the inline Modal triggers inside custom IP log view without colliding with cached `z-index` stacks.

= 3.5.0.1 =
* **Hotfix:** Added automatic database schema updater for WPDeploy silent plugin upgrades to prevent column mismatch crashes on `wp_auctionforge_user_tracking`.

= 3.5.0.0 =
* **Full-Spectrum Device Fingerprinting** — Expanded the User Tracking system with 9 new forensic identifiers.
* WebRTC Local IP discovery exposes true internal network addresses even behind VPNs.
* Math Precision Fingerprint detects CPU architecture differences (Intel vs AMD vs Apple Silicon).
* Speech Synthesis voice enumeration and Media Device counting for unique hardware profiling.
* Battery API, Connection Profile, Color Gamut/HDR, and Accessibility preference tracking.
* Form Fill Timing measurement with automatic bot detection flag (<500ms = suspected bot).
* Async collection pipeline fires identifiers in background on page load for zero-latency capture.
* Fingerprint hash upgraded from fp2 to fp3 generation incorporating all new signals.
* Admin dashboard tooltip now shows dynamic identifier count per user with BOT? warning badge.

= 3.4.0.0 =
* **Sync Architecture Expansion** — Fully unified the WPDeploy remote console architecture matching identical features of native plugin.
* Standardized REST API endpoints including adding missing `/sync/auctions` route to managed instances.
* Optimized Sync Dashboard with queue controls, 5 active KPI gauges, and real-time streaming console.
* Resolved proxy JSON crash issues caused by HTML fallback responses on child instances lacking full endpoint routing.

= 3.2.9.2 =
* **Timed Auction Fixes** — Corrected a boolean check on the main upcoming/archive views that improperly hid the Timed Auction badge for catalog pages.

= 3.2.9.1 =
* **Timed Auction Enhancements** — Added a new WP-Admin setting "Change text to 'Auction will close in'" that updates the countdown prompt exclusively for timed/online auctions from "The auction will start in / Live bidding starts in" to "The auction will close in / Auction will close in" across single lot pages and wide upcoming auction shortcodes.

= 3.2.9.0 =
* **Billing & Invoices Polish** — Corrected front-end profile invoice ID tracking.
* Restructured data payloads feeding into `dompdf` generator to display accurate payment statuses rather than cached API defaults.
* Conditionally hid payment instructions on downloaded invoice files when the bill is fully paid.
* Changed "Invoice No" textual display to "Bidder No" on document output.

= 3.2.8.0 =
* **Billing & Invoices PDF Generation** — Implemented server-side generation using `dompdf` allowing users to directly download their exact HTML Bidspirit invoice locally via secure WordPress endpoints.
* Added native UI handling for `PAYMENT_IN_PROGRESS` invoice statuses correctly displaying "In Progress" with a "Resume" action button within the My Invoices interface.

= 3.2.7.0 =
* **Billing & Invoices Integration** — Added "My Invoices" tab to the user profile area.
* Logged-in users can now view their Paid and Unpaid invoices fetched dynamically from Bidspirit.
* New admin toggle under Bidspirit Options to enable or disable the billing tab.
* Unpaid bills include a direct "Pay Now" link.

= 3.2.4 =
* **Sync worker hang fix** — Sync workers were hanging indefinitely at the InsertLotsBS stage because third-party plugins (Smush Pro, SmartCrawl SEO, WPMU DEV) hooked into `wp_insert_post` and made blocking external HTTP calls for every lot. Added `WP_IMPORTING` constant and a `pre_http_request` filter to block all non-essential outbound HTTP during the batch post-insertion phase. Loopback and BidSpirit API calls are still allowed.
* **Stuck staging row recovery** — When workers were force-killed mid-sync, staging rows in `import_history` were left with `syncstart=1`, making them permanently invisible to subsequent sync runs. Workers now operate in an environment where this is mitigated by the faster completion times.

= 3.2.2 =
* **Full Sync discovery fix** — Full Sync now discovers auctions from BidSpirit API before syncing, fixing the issue where freshly deployed sites found 0 auctions.

= 3.2.0 =
* **Archive Sync** — New button on admin Sync Status page to re-sync all past (ARCHIVED/ENDED) auctions. Past auctions now appear in the dropdown.
* **Sync Tasks v3** — Admin task cards now use the v3 queue system. Old importData/importBidSpirit tasks replaced with queue-based Sync Active/Archived buttons.
* **How It Works guide** — Rewritten for v3 webhook-first architecture with job types, queue mechanics, cron setup, and troubleshooting.
* **Dual-write removed** — Legacy webhook handlers removed; all sync now goes through the v3 queue exclusively.
* **ITEMS_UPDATED fix** — Individual lot edits on BidSpirit now trigger real-time sync (auctionId extraction from nested payload).
* **ItemUpdateWorker fix** — Now processes staged data into WP posts + bp_meta_data via InsertLotsBS (was only staging).
* **Worker spawn fix** — HTTP loopback uses 127.0.0.1 with Host header to avoid nginx vhost routing issues.
* **API token** — Plugin expects `AUCTIONFORGE_API_TOKEN` constant in wp-config.php.
* DB version: 22 → 23

= 3.0.0 =

### New Feature: Webhook-First Sync Architecture (Sync v3)
- **Webhook-driven sync** — BidSpirit webhooks now enqueue jobs into a dedicated queue table (`af_sync_queue`) instead of relying solely on polling cron tasks. Data flows from webhook → queue → background worker → WordPress posts.
- **Job Queue System** — New `af_sync_queue` table with priority-based FIFO processing, automatic deduplication by auction_id + job_type, and atomic job claiming to prevent race conditions.
- **Background Worker** (`syncWorker.php`) — Async CLI process spawned by webhooks or the safety cron. Claims and processes queued jobs with a configurable timeout (default 240s). Handles catalog_sync, item_update, full_sync, day_ended, and archive_sync job types.
- **Hourly Safety Net** (`syncSafety.php`) — Replaces the old per-minute importData and hourly importBidSpirit cron tasks with a single hourly cron that: resets stuck jobs, spawns workers for pending jobs, runs a 24-hour health check (auto-enqueues full sync if no webhooks received), and purges old data.
- **Real-time Live Console** — New dark-themed terminal console on the Sync Status admin page. Polls `af_sync_log` every 1.5s showing timestamped, color-coded log entries from webhooks, workers, queue operations, and the safety cron. Supports pause, clear, and auto-scroll.
- **Queue Status Dashboard** — New card on Sync Status page showing pending/processing/completed/failed/skipped counts, recent jobs table, worker status badge, and action buttons (Full Sync, Sync Selected Auction, Spawn Worker).
- **Dual-write mode** — Webhooks enqueue jobs AND still run the legacy handlers during transition. Once verified, legacy handlers can be removed cleanly.
- **WP-Cron safety hook** (`auctionforge_sync_safety`) — Always-active hourly WP-Cron event that resets stuck jobs and spawns workers, independent of the external cron system.
- **Cron installer v3** (`setup.sh`) — Updated task definitions: removed `importData` (every minute) and `importBidSpirit` (hourly), added `syncSafety` (hourly at :10). New `migrate` command for interactive v2→v3 crontab transition.
- **cURL timeout** — BidSpiritRequest now has a 30-second cURL timeout (was unlimited) to prevent hung connections blocking the sync pipeline.

### New Files:
- `inc/sync/SyncQueue.php` — Queue model with enqueue, claim, complete, fail, skip, reset, spawn, purge
- `inc/sync/SyncLogger.php` — Real-time logging with level filtering and incremental polling
- `inc/sync/CatalogSyncWorker.php` — Processes catalog/full/day-ended sync jobs
- `inc/sync/ItemUpdateWorker.php` — Processes individual item update jobs
- `crontasks/syncWorker.php` — CLI background worker process
- `crontasks/syncSafety.php` — CLI hourly safety net process

### New DB Tables:
- `af_sync_queue` — Job queue (status, priority, source, attempts, worker PID, timing)
- `af_sync_log` — Real-time log entries (context, level, auction_id, job_id)

### Technical Details:
- DB version bumped from 20 to 22
- 7 new constants: AUCTIONFORGE_SYNC_WORKER_TIMEOUT, AUCTIONFORGE_SYNC_STALE_JOB, AUCTIONFORGE_SYNC_HEALTH_CHECK, AUCTIONFORGE_SYNC_LOG_RETENTION, AUCTIONFORGE_SYNC_QUEUE_RETENTION, AUCTIONFORGE_CURL_TIMEOUT, AUCTIONFORGE_PHP_BINARY
- 3 new AJAX handlers: auctionforge_sync_log_poll, auctionforge_sync_queue_status, auctionforge_sync_enqueue
- Net cron change: 5 entries → 4 entries, ~62 PHP executions/hour → 1

= 2.7.1.2 =

### Fixes:
- Added missing `vatAndCommission()` function in `loadBid()` — the sales tax display on single lot pages was never being rendered because the code to call `loadTemplate` for `vat-and-commission-template` was missing from `app.js`. Also added missing `setAuctionItemInfo()` call.

= 2.7.1.1 =

### Fixes:
- 2 decimal places now only appear in the bid modal (Your max bid, Total Price) via `showDecimals` flag. Start price, increments list, and other prices show whole numbers as before.

= 2.7.1.0 =

### Fixes:
- Fixed Buyer's Premium value not showing on single lot pages — added missing `auction-commission-field` population in `auctionInfoInit()` from upstream reference.

= 2.7.0.9 =

### Fixes:
- Fixed fatal error during plugin install/upgrade: `class-stats-database.php` require failure. Added `file_exists` guard in `AF_Stats_Init::activate()` and plugin-specific guard in `upgrader_process_complete` hook to prevent running upgrade logic for unrelated plugin updates.

= 2.7.0.8 =

### Fixes:
- Synced `formattedPrice()` across both `app.js` and `app.min.js` — added second currency support, better null/NaN handling, and always 2 decimal places on all prices. Fixes sites where minification plugins may load `app.min.js` instead of `app.js`.

= 2.7.0.7 =

### Fixes:
- All prices now display with 2 decimal places (e.g. $10.00 instead of $10) via `formattedPrice()` using `toLocaleString` with `minimumFractionDigits: 2`.

= 2.7.0.6 =

### Fixes:
- Added missing `estimatedPrice`, `content`, and `isShop` template variables to widget and shortcode `lot-content-template` calls, preventing potential ReferenceErrors on catalogue pages with ended auctions or shop items.

= 2.7.0.5 =

### Fixes:
- Fixed Stripe payment form not visible in Add Payment Method modal — `#stripePaymentElement` had `hidden` class that was never removed. Now unhides form on Stripe `ready` event and re-hides on modal close.

= 2.7.0.4 =

### Fixes:
- Fixed `bidspiritApi.getAuctionSettingsData is not a function` error in getFullPriceInfo — use local auctionSettingsData variable instead of non-existent API method.

= 2.7.0.3 =

### Fixes:
- Fixed `fullPriceInfo is not defined` ReferenceError that prevented bid confirmation modal from opening on single lot pages and catalogue pages.
- Added missing `getFullPriceInfo()`, `getCommissonInfoForPrice()`, and `roundToCents()` functions ported from upstream.
- All three bid submission paths (submitBid, submitBidCatalog, submitBidCatalog3) now correctly pass fullPriceInfo to the modal-bid-template.

= 2.7.0.0 =

### New Features: Advanced Lot Analytics
- **Conversion Tracking** — View-to-bid ratio table showing lot views, catalogue/external clicks, favorites, shares, bid status, and sold price with sell-through rate summary cards
- **Peak Viewing Times** — Hourly bar chart showing when lots get the most views (last 30 days) with peak hour identification
- **Pre-Sale vs Live Comparison** — Per-auction breakdown of views before auction day vs during/after, with pre-sale percentage
- **Category Performance** — Analytics grouped by lot category: views, lots, favorites, sold count, average views per lot
- **Share Analytics** — Track shares by channel (Facebook, Twitter, WhatsApp, Email, Pinterest, Copy Link) with per-channel badges and most-shared lots table
- **Search → View Funnel** — Correlates internal search queries with subsequent lot page views to show which searches lead to browsing
- **Click-Through Tracking** — Distinguishes catalogue clicks (user came from auction catalogue) vs external clicks (direct/search/referral)
- **CSV Export** — One-click export of all lot analytics data, filterable by catalogue
- **Hot Lot Badge** — Automatic "🔥 Popular" badge on catalogue page thumbnails for lots exceeding view threshold (default: 5 views)
- All new panels respect the catalogue filter dropdown
- New DB table: af_lot_shares (per-channel share events)
- New columns: catalogue_clicks, external_clicks, share_count on af_lot_analytics
- DB version bumped to 20

= 2.6.3.0 =

### Fixes:
- View counter now shows on lot pages even with 0 views
- Fixed backfill and trending queries failing on URLs with trailing slashes

= 2.6.2.0 =

### Fixes:
- Fixed 404 errors on auction/lot pages after DB version update by flushing rewrite rules automatically

= 2.6.1.0 =

### Fixes:
- Renamed shortcodes to use bp_ prefix for consistency: [bp_popular_lots], [bp_most_favorited], [bp_trending_lots]

= 2.6.0.0 =

### New Feature: Lot Analytics
- Added comprehensive lot view and favorite tracking system
- Two new settings in AuctionForge > Settings (both disabled by default):
  - **Enable Lot Analytics** — master toggle for the entire tracking system
  - **Show View Counter on Lots** — display a public view count on each lot page
- New admin dashboard tab: **Lot Analytics** (under Site Statistics) with:
  - Auction Performance comparison table
  - Most Viewed Lots (all time)
  - Most Favorited Lots
  - Trending Lots (last 7 days)
  - Backfill from existing pageview data button
- Local favorites tracking mirrors BidSpirit favorites for analytics
- Three new shortcodes for displaying popular lots:
  -  — most viewed lots grid
  -  — most favorited lots grid
  -  — trending lots (configurable time window)
- All shortcodes support limit, columns, auction_id, and period filtering
- New DB tables: af_lot_analytics, af_lot_favorites (DB version 19)

= 2.5.9.0 =

### Improvements:
- Redesigned Recently Viewed items as square card grid (rows/columns) instead of horizontal list.
- Changed lot thumbnail to contain (show full image) instead of cover (crop to fill).

= 2.5.8.0 =

### Fixes:
- Fixed Recently Viewed cloned image thumbnails using non-existent resize prefix. Now uses raw image filename for reliable CDN loading.

= 2.5.7.0 =

### Fixes:
- Fixed Recently Viewed thumbnail URLs — now builds correct BidSpirit CDN URLs for both cloned (Fastly) and non-cloned (Cloudinary) images instead of broken relative paths.

= 2.5.6.0 =

### Fixes:
- Fixed placeholder image fallback path still referencing old uco-auctions directory.
- Added background color for empty thumbnail containers in Recently Viewed.

= 2.5.5.0 =

### Improvements:
- Removed PAST/Upcoming badges from Recently Viewed items display.
- Added lot thumbnail images to Recently Viewed items with proper card layout.

= 2.5.4.0 =

### Fixes:
- Fixed item variable not being passed to auctionIsLive-template (was lost during previous file edits).

= 2.5.3.0 =

### Fixes:
- Fixed `item is not defined` in `displayBidFormData` — removed stray `item: item` from house-info-template call where item variable does not exist in scope.

= 2.5.2.0 =

### Fixes:
- Fixed `item is not defined` JavaScript error in `auctionIsLive-template` on single lot pages.
- Fixed statistics table auto-creation running on frontend requests (restricted to admin only).

= 2.5.1.0 =

### Fixes:
- Fixed `hideleadingBidSection is not defined` JavaScript error on single lot pages.
- Fixed statistics database tables not auto-creating on plugin update (only activated on fresh install).
- Added self-healing table creation on `plugins_loaded` for sites that update without re-activating.
- Cleaned up duplicate `AF_Stats_Init::activate()` calls in DB update function.
- Bumped DB version to 18.

= 2.5.0.0 =

### New Features:
- **Site Statistics System** — Full website analytics built into the plugin admin (Lots → Site Statistics).
  - Dashboard with KPI cards: visitors, sessions, pageviews, bounce rate, avg duration, pages per session.
  - Period comparison: current vs previous period with percentage change indicators.
  - Traffic over time line chart (pageviews, visitors, sessions).
  - Traffic channels doughnut chart (organic search, direct, referral, social, email, paid).
  - Hourly distribution bar chart.
  - Device type pie chart (desktop, mobile, tablet).
  - Top Pages tab with views, unique visitors, avg time on page, search, pagination, CSV export.
  - Referrers tab with traffic channel badges, sessions, bounce rate, avg duration, CSV export.
  - Countries tab with horizontal bar chart and visitor/pageview counts.
  - Technology tab with browser, OS, device breakdown and screen resolutions.
  - Searches tab tracking internal WordPress and auction search queries.
  - 404 Errors tab tracking broken URLs with hit counts and referrers.
  - Real-Time visitors view (last 5 minutes, auto-refreshes every 10 seconds).
  - Settings tab: enable/disable tracking, exclude IPs and roles, IP anonymization (GDPR), data retention.
  - Date range picker with presets (7, 14, 30, 90, 365 days) and custom range.
  - Lightweight frontend tracker (~3KB JS) with session management, heartbeat, and beacon API.
  - GeoIP country/city lookup via ip-api.com with 24-hour cache.
  - Bot detection (30+ crawler patterns filtered).
  - UTM parameter tracking (source, medium, campaign, term, content).
  - Traffic channel auto-classification.
  - Outbound link click tracking.
  - Time-on-page tracking via 30-second heartbeat + page unload beacon.
  - Daily cron for automatic data retention purge.
- **Recently Viewed Items** — New profile page tab showing the last 20 viewed lots per user.
  - Admin setting to enable/disable (Lots → Settings → Enable user viewing history).
  - Tracks lot views with timestamp, stores in custom DB table.
  - Clear history button for users.
  - Disabled by default.

### Fixes:
- Fixed `settings is not defined` JavaScript error on auction catalog and single lot pages.
- Fixed `postAuctionSaleOpened is not defined` JavaScript error on auction pages.
- Fixed missing `vendor.min.css` and `password-validation.min.css` (404 errors).
- Added missing template variables to all `loadTemplate` calls for `lot-content-template`, `leadingBid-template`.

= 2.4.4.0 =

### Fixes:
- Fixed the display of purchase prices on shop pages for unsold items. Items with a buyout price now correctly show the "Purchase Price" in both ended and active auction states.

= 2.3.5 =

### New Features:
- Added the ability to create a menu item that opens a modal login window. (`[href="#bp-profile"]`)
- Prices are now displayed with a secondary currency.
- Added an option to display prices including taxes.

### Improvements:
- Login and registration pages no longer leave users on the default welcome page after authentication.
- Updated German localization.
- Added Dutch localization.
- Optimized the synchronization process for auction houses with a large number of auctions.
- Updated the user manual. Added a section with available shortcodes.

### Fixes:
- Fixed a synchronization issue when default and secondary language settings were incorrect in the plugin.
- Fixed broken content in the *Make an inquiry about this item* link when special characters were present in the lot title.
- Fixed display of current bids for lots depending on the auction house settings.
- Fixed display of current bids for lots that are loaded when scrolling the page, if the "Enable pagination" option is disabled.
- Fixed handling of lots that were removed from the auction.
- Fixed bid display on the catalog page when the *enablePrebidsUnderStartPrice* option is enabled.