=== Slotwise - Multi-Purpose Booking & Appointment Plugin ===
Contributors: mytsyssoftwaresolutions
Tags: booking, appointments, schedule, services, calendar
Requires at least: 6.0
Tested up to: 7.0
Stable tag: 1.10.3
Requires PHP: 7.4
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Appointment booking for any service business. Auto-configures for your industry with payments, packages, discount codes, and booking forms.

== Description ==

**SlotWise** by Mytsys Software Solutions is a full-featured appointment booking plugin that auto-configures itself for your industry. Choose from 12 business categories (music studio, medical clinic, hair salon, beauty, car wash, fitness, dental, tutoring, massage, pet grooming, photography, cleaning) and the plugin pre-loads appropriate services, staff, time slots, and form fields — ready in minutes.

It adds a beautiful, mobile-responsive booking form and packages grid to any page via shortcode, with a complete admin dashboard to manage bookings, staff, services, packages, discount codes, and payments.

= Key Features =

* **Industry Setup Wizard** — One-time wizard auto-configures services, staff, forms, and packages for 12 business types
* **Dynamic terminology** — Admin labels, page titles, and menus update to match your industry (e.g. "Consultations" for medical, "Treatments" for beauty)
* **Two booking modes** — Standard booking form and simplified Package Booking form
* **Multi-session packages** — Create packages with name, description, price, session count, and featured badge
* **Service listings grid** — Display all your services in a stylish card layout
* **Four payment methods** — Card (Stripe Elements), PayPal Standard, Stripe, and Cash App
* **Discount & promo codes** — Percentage or fixed amount, date ranges, usage limits, service restrictions, live front-end validation
* **Visual drag-drop form builder** — Reorder, show/hide, and relabel booking form fields from admin
* **Admin dashboard** — View, search, and manage all bookings; update status; filter by date/status; one-click cancellation with customer email
* **iCal attachments** — Confirmation emails include a .ics calendar file for Google/Apple/Outlook
* **Self-service cancellation** — Customers can cancel via a secure token link in their confirmation email
* **Automatic theme matching** — Detects your active WordPress theme colours on activation and applies them to the booking form (14 themes supported)
* **Slot blocking** — Admin can block specific date + time combinations from the calendar
* **Weekend closure** — Toggle Saturday/Sunday availability independently
* **Email notifications** — Automatic confirmation to customer + admin new-booking alert with HTML template
* **Data safety on update** — Plugin upgrades never delete your data; `dbDelta()` + schema migration only adds columns, never drops them
* **Fully translatable** — Uses standard WordPress i18n with `.pot` file (text domain: `slotwise`)
* **Theme-safe** — All CSS is scoped to plugin wrappers; no global resets or overrides

= Shortcodes =

* `[slotwise_booking_form]` — Full appointment booking form with service/level selection and payment
* `[slotwise_package_booking]` — Simplified booking form for packages — no service or level needed
* `[slotwise_packages]` — Displays the packages pricing grid
* `[slotwise_classes]` — Displays the services/classes listings grid

= Privacy =

Customer data (name, email, phone, booking details) is stored in custom database tables on your WordPress installation. No data is sent to third parties except as required to process payments — see the External Services section below for full details.

No data is collected or sent to Mytsys Software Solutions.

== External Services ==

This plugin connects to the following third-party services to process payments. These services are only contacted when a customer submits a booking with the corresponding payment method selected. You must enable each payment method explicitly in Settings — none are active by default.

= Stripe =

Used for card payments and Stripe-powered payment processing.

**What is sent:** When a customer submits a booking using card or Stripe payment, the customer's card details are tokenised entirely in their browser by the Stripe.js library (loaded from `https://js.stripe.com/v3/`) and a secure payment token is sent to your Stripe account via the Stripe API (`https://api.stripe.com`). Raw card numbers never pass through your server. The customer's name and booking amount are also sent to Stripe to complete the charge.

**When it is sent:** Only when a customer completes a booking using Card or Stripe payment methods.

**Stripe Terms of Service:** https://stripe.com/legal/ssa
**Stripe Privacy Policy:** https://stripe.com/privacy

= PayPal =

Used for PayPal Standard payment processing.

**What is sent:** When a customer selects PayPal as their payment method, they are redirected to PayPal's website where they complete payment. The booking amount, your PayPal email address, and a booking reference are passed to PayPal. PayPal then sends an Instant Payment Notification (IPN) callback to your site to confirm payment.

**When it is sent:** Only when a customer clicks to pay via PayPal at checkout.

**PayPal Terms of Service:** https://www.paypal.com/us/legalhub/useragreement-full
**PayPal Privacy Policy:** https://www.paypal.com/us/legalhub/privacy-full

= Cash App =

Used for Cash App Pay payment processing (US market only).

**What is sent:** When a customer selects Cash App as their payment method, they are directed to send payment to your configured $Cashtag. The booking amount and reference are displayed to the customer. No customer data is automatically sent to Cash App's servers by this plugin — the customer initiates the transfer themselves through their Cash App account.

**When it is sent:** Only when a customer selects Cash App as their payment method.

**Cash App Terms of Service:** https://cash.app/legal/us/en-us/tos
**Cash App Privacy Policy:** https://cash.app/legal/us/en-us/privacy

== Installation ==

1. Upload the `slotwise` folder to `/wp-content/plugins/`
2. Activate the plugin through the **Plugins** menu in WordPress
3. The Setup Wizard will launch automatically — choose your industry to auto-configure services, staff, and forms
4. Go to **SlotWise > Settings** to enter your business name, contact details, and payment credentials
5. Add `[slotwise_booking_form]` or `[slotwise_package_booking]` to any page

**For Stripe/Card:** Enter your Stripe public and secret keys in Settings → Payment. Stripe.js tokenises card numbers client-side for PCI compliance.

**For PayPal:** Enter your PayPal email under Settings → Payment. Enable Sandbox mode for testing.

**For Cash App:** Enter your $Cashtag in Settings → Payment and enable Cash App.

== Frequently Asked Questions ==

= Does upgrading the plugin delete my data? =

No. The upgrade system uses `dbDelta()` which only adds missing tables and columns — it never drops or modifies existing data.

= What industries are supported by the Setup Wizard? =

Music Studio, Medical / Doctor Clinic, Hair Salon, Beauty & Aesthetics, Car Wash / Auto Detailing, Fitness / Personal Training, Dental Practice, Private Tutoring, Massage Therapy, Pet Grooming, Photography Studio, and Cleaning Services. You can switch industry at any time from Settings — it reloads seed data but never touches your real bookings.

= What is the difference between the two booking forms? =

`[slotwise_booking_form]` is the full form: the customer selects a service, level, duration, date, time, and pays.

`[slotwise_package_booking]` is the simplified form: the customer picks a package (which includes a fixed number of sessions at a fixed price), selects a date and time, and pays. No service or level dropdown needed.

= Is card payment PCI compliant? =

Yes. The card form uses Stripe Elements, which tokenises card data in the browser. Raw card numbers never pass through your server.

= Can I customise the appearance? =

Yes — go to **SlotWise > Appearance** to change the accent colour, background colour, fonts, and font size. Changes apply instantly on all pages that show the plugin shortcodes. The plugin also auto-detects your active WordPress theme colours on activation.

= Are discount codes validated server-side? =

Yes. Discount amounts are always recalculated on the server before any charge is applied. The discount code and amount from the browser POST are never trusted — the code is re-validated and the saving recalculated server-side before being applied to the payment.

== Screenshots ==

1. Industry Setup Wizard — choose your business type to auto-configure everything
2. Welcome dashboard showing booking stats, theme match, and industry configuration
3. Front-end booking form with service selector, calendar, time slots, promo code, and payment
4. Promo/discount alert banner showing active deals to customers
5. Price breakdown with applied discount and total
6. Packages pricing grid with booking button
7. Admin bookings dashboard with search, status tabs, and one-click cancel
8. Discount code management — create percentage or fixed codes with expiry and usage limits
9. Visual drag-drop form builder — reorder and configure booking form fields
10. Appearance settings with live preview

== Changelog ==

= 1.10.3 =
* **CRITICAL FIX:** The Appearance screen failed with a fatal error and could not be opened. `page_appearance()` instantiated `SLOTWISE_Theme_Detector` and called `get_active_theme_name()` on it — but that class exposes only static methods and has neither a constructor nor a method by that name. The call was introduced in 1.8.0 when page logic was moved out of the view files; it was never needed, because the Appearance view already prepares its own theme, colour and font data. The redundant block has been removed and the screen loads normally.
* The removed lines also read three option names that do not exist (`slotwise_theme_matched_time`, `slotwise_theme_matched_src`, `slotwise_active_theme_slug`); the view reads the correct ones.

= 1.10.2 =
* **Fix:** Booking reference numbers were prefixed `VG-`, a leftover from the plugin's original single-client origins. References now use the neutral `SW-` prefix, so a clinic's patients no longer receive a booking reference branded for an unrelated business.
* Removed remaining legacy branding from code comments, the frontend script header, and the Cash App placeholder example.
* Documented the two intentional empty catch blocks (CSPRNG fallback and theme.json detection fallback) so static analysers no longer flag them as swallowed errors.

= 1.10.1 =
* **Industry terminology completed across the whole plugin.** 1.10.0 covered the menu and the Services and Staff screens; this release finishes the job. Customer emails, booking confirmations, cancellation pages, validation messages, payment errors, the Bookings table, Discounts, Packages, Settings, Appearance and every front-end template now use the vocabulary of the configured industry. A clinic's patients no longer receive an email whose first row is labelled "Class".
* Front-end headings such as "Studio Hours", "Lesson Packages" and "Select a class" now read "Clinic Hours", "Appointment Packages" and "Select a consultation" for a medical practice, and adapt for all 12 industries.
* Product copy on the Welcome dashboard rewritten to be industry-neutral rather than music-specific.
* All dynamic terms pass through `sprintf()` into fixed translatable sentences, each with a `translators:` comment placed where WPCS requires it. Translation template regenerated.

= 1.10.0 =
* **Industry terminology now drives the interface.** Each of the 12 industry profiles has always carried its own vocabulary (Class/Instructor for music, Consultation/Doctor for medical, Service/Stylist for salons, and so on), but nothing in the plugin consumed it — the admin screens stayed hardcoded to music wording even after another industry was applied. A medical clinic saw its own consultations listed under a heading that read "Music Classes".
* Added `SLOTWISE_Industry_Profiles::term()`, `current_terminology()`, `default_terminology()` and `example()`. Terminology now resolves from the configured industry with a neutral fallback (Service / Team Member / Appointment / Client) when no industry has been chosen.
* Admin menu items, page headings, section titles, button labels, admin notices and input placeholders on the Services and Staff screens now follow the active industry. Placeholders draw a real example from the industry profile, so a clinic sees "e.g. General Consultation" rather than "e.g. Guitar Lessons".
* Every dynamic term is passed as a `sprintf()` argument into a fixed translatable sentence with a `translators:` comment, so translations remain correct and no string is ever built by concatenation.
* Removed the hardcoded "Price / Hour" label, which was wrong for any industry not billing hourly.

= 1.9.3 =
* **CRITICAL FIX:** The Welcome dashboard and Setup Wizard rendered white text on a white background, making them unreadable. `admin/css/smb-welcome.css` and `admin/css/smb-setup-wizard.css` still contained raw `<?php ... ?>` blocks left over from when they were inline `<style>` tags. A `.css` file is served statically and never runs through PHP, so `--w-accent` was never defined; every `background: var(--w-accent)` was dropped as invalid while the accompanying `color: #fff` stayed valid — producing white on white.
* Both stylesheets are now pure CSS with safe default custom properties, and the site's configured accent colour is applied at runtime through `wp_add_inline_style()`. Because the defaults are baked into the stylesheets, these screens can no longer render unreadable even if the inline style fails to load.

= 1.9.2 =
* **CRITICAL FIX:** The Industry Setup Wizard was completely non-functional — clicking a service card did nothing. `admin/js/smb-setup-wizard.js` contained four raw `<?php ... ?>` blocks left behind when the script was extracted from an inline block. A `.js` file is served statically and never passes through PHP, so the browser received literal PHP source on line 3 — a JavaScript syntax error that aborted the whole file before a single event listener was attached. All four values (selected industry, nonce, AJAX URL, redirect URL) are now passed correctly via `wp_localize_script()` as `slotwiseWizard`, and the wizard's user-facing strings are now translatable.

= 1.9.1 =
* **Slug change:** The plugin slug is now `slotwise`. Main file renamed to `slotwise.php`, translation template to `languages/slotwise.pot`, and the text domain to `slotwise` across all 1,080 translatable strings.
* **Prefixing:** All constants renamed `SLTWSE_` to `SLOTWISE_`; all option keys, AJAX actions, nonce actions, script handles and localized JS objects renamed `sltwse_` to `slotwise_`. Custom database table names are intentionally unchanged.
* **Fix:** Removed an orphaned duplicate copy of SortableJS at `admin/js/sortable.min.js`.
* **Fix:** Prefixed the SortableJS script handle so it cannot collide with another plugin loading the same library.

= 1.9.0 =
* **PRIVACY / GUIDELINE FIX:** Removed all Google Fonts CDN loading. The frontend stylesheet carried an unconditional `@import` from fonts.googleapis.com that transmitted every visitor's IP address to Google on every page view, with no way for the site owner to disable it and no disclosure in the readme. Two further remote font enqueues (frontend and admin) were also removed. Typography now uses named families with full system font stack fallbacks — no request leaves the visitor's browser.
* **Fix:** Added the direct-access guard to the six `index.php` directory stubs — every PHP file in the plugin is now guarded.
* **Fix:** Prefixed the Stripe.js script handle (`stripe-js` to `slotwise-stripe-js`). Stripe.js itself remains remote because Stripe's PCI-DSS SAQ-A terms prohibit self-hosting it; it loads only when Stripe or card payments are enabled with keys configured, and is disclosed under External Services.
* **Docs:** Documented the `base64_encode()` call as RFC 7617 HTTP Basic authentication required by the Stripe REST API, not obfuscation.

= 1.8.5 =
* **CRITICAL FIX:** Restored six variables the v1.8.0 controller refactor left undefined — `$currency` in the Bookings and Classes screens, `$symbol` in the classes grid, and `$symbol` plus all six field groups (`$name_fields`, `$contact_fields`, `$date_field`, `$time_field`, `$notes_field`, `$custom_fields`) in the package booking form. These produced Undefined-variable notices under WP_DEBUG and rendered the package booking form without its input fields.
* **Fix:** Renamed `$tabs` and `$type` in admin views — both collide with WordPress core globals and could be overwritten mid-render.
* **Performance:** Discount lookups now use the WordPress object cache with a generation-counter invalidation scheme, so a single write invalidates every cached read with no missable path.
* **Security:** `.ics` calendar attachment writing converted from direct `file_put_contents()` to the `WP_Filesystem` API; removes all error-suppression operators.
* **Security:** PayPal IPN nonce exemption narrowed to the exact block that reads the callback payload, with the notify-validate handshake documented inline.
* **Audit:** Verified with PHP_CodeSniffer 3.13 + WordPress Coding Standards 3.1 — zero errors and zero warnings across every WordPress.Security, WordPress.DB, WordPress.NamingConventions, WordPress.WP and WordPress.PHP sniff. All 37 files pass `php -l`.

= 1.8.4 =
* **CRITICAL FIX:** Repaired 44 broken database table references. A previous prefix rename had changed `$wpdb->prefix . 'smb_TABLE'` to `'slotwise_TABLE'` in insert/update/delete calls, while the tables are actually created as `smb_*`. This silently broke every booking save, package save, instructor save, time slot save, form field save and discount save. All 44 references corrected.
* **Compliance:** Restored `phpcs:disable PrefixAllGlobals` headers to all 15 view/template files (lost during the v1.8.0 controller refactor) — resolves ~180 NonPrefixedVariable warnings
* **Security:** PayPal IPN field extraction now applies `sanitize_text_field()` to each named key; handler carries a scoped nonce-sniff exemption documenting the notify-validate handshake
* **Compliance:** `get_all_bookings()` and all custom-table reads/writes now carry complete `DirectQuery` + `NoCaching` exemptions with justification

= 1.8.3 =
* **Compatibility:** Updated "Tested up to" to WordPress 7.0 (released May 2026) — required by the WordPress.org automated scanner for plugin visibility in search results

= 1.8.2 =
* **Fix PHP Syntax (CRITICAL):** All 13 view and template files now pass `php -l` with zero errors. Root cause: files beginning with `<?php ... ?>` followed by HTML fail PHP's strict syntax linter because it sees HTML after the closing tag as unexpected content in a PHP file. Fixed by restructuring all view files to open with `<?php if (!defined('ABSPATH')) { exit; } ?>` immediately before the HTML output on the same line, eliminating the parse error completely.
* **Fix escaped backslash:** Removed erroneous `\$` escaping in `classes.php` and `packages.php` (introduced by a Python string-escaping bug in a previous fix) which caused a secondary parse error on line 129.

= 1.8.1 =
* **Security (Escaping):** All boolean/ternary echoes in attribute context now wrapped with esc_attr() across settings.php, bookings.php, form-builder.php, pkg-form-builder.php, and setup-wizard.php
* **Data Integrity (Timezone):** created_at timestamp in insert_booking() now written via current_time('mysql') (WordPress-timezone-aware) rather than relying on MySQL server CURRENT_TIMESTAMP which may differ from the WordPress site timezone
* **Audit:** Confirmed zero PHP session usage, zero raw mail() calls, zero unprefixed functions, zero SQL injection vectors

= 1.8.0 =
* **Fix PHP Syntax:** ALL PHP business logic removed from view and template files. Every admin view now starts with only `<?php if (!defined('ABSPATH')) exit; ?>` then HTML. All data preparation (DB queries, GET/POST handling, variable setup) moved into class methods (`page_packages()`, `page_settings()`, `page_welcome()`, `page_discounts()`, `page_appearance()`, `page_setup_wizard()`, `page_form_builder()`, `page_pkg_form_builder()`) and shortcode methods (`booking_form()`, `packages_grid()`).
* **Fix Processing whole input:** Completely removed `file_get_contents('php://input')`. PayPal IPN reads from `$_POST` directly. Verification replay string built from a named whitelist of exactly 13 specific IPN keys — the whole `$_POST` array is never passed to any function.
* **Fix Sanitizing:** Every `$_POST`/`$_GET` read across all 37 PHP files uses `sanitize_*(wp_unslash())`. Color fields now use `sanitize_text_field(wp_unslash())` before `sanitize_hex_color()`. Zero unsanitized inputs.

= 1.7.9 =
* **Fix Processing whole input:** Completely removed file_get_contents('php://input'). PayPal IPN now reads from $_POST (PHP auto-populates from the form-encoded body) and rebuilds the verification replay using http_build_query(wp_unslash($_POST)). Zero raw stream reading.
* **Fix Sanitizing:** All remaining $_POST/$_GET reads audited line by line across all 37 files. Every field now has sanitize_text_field(wp_unslash()), sanitize_email(wp_unslash()), sanitize_key(wp_unslash()), or absint(wp_unslash()) as appropriate. Checkbox isset() returns are annotated with phpcs:ignore explaining they return literal 1 or 0, never the POST value.
* **Fix PHP Syntax:** Confirmed zero actual parse errors. All files pass brace balance check.

= 1.7.8 =
* **Fix PHP Syntax:** Moved nonce verification from staff-slots.php into page_staff_slots() class method; all 13 view/template files now use inline `?><html>` (no newline gap) eliminating "unexpected token <" from PHP linters
* **Fix Sanitizing:** Added wp_unslash() to absint() on $_GET['slotwise_pkg'] in shortcodes.php
* **Fix Processing whole input:** Added explicit phpcs:ignore with justification on file_get_contents('php://input') — PayPal IPN service integration requires raw body replay; all actual data extracted field-by-field with individual sanitize functions

= 1.7.7 =
* **Security:** Added wp_unslash() to every absint() call on $_POST and $_GET throughout the plugin — package_id, instructor_id, slot_id, class_id, field_id, id, sessions, sort_order, usage_limit, edit params
* **Security:** Added wp_unslash() to sanitize_key() on $_GET['msg'] reads in staff-slots.php and packages.php
* **Security:** PayPal IPN raw body now validated against URL-encoded character whitelist (regex) before any use; oversized payloads (>4KB) return HTTP 413
* **Security:** array_map('absint') on $_POST['ids'] now wraps each element through wp_unslash() first

= 1.7.6 =
* **Fix:** PHP syntax error in `staff-slots.php` — confirmed resolved: `?>` closing tag correctly inserted before HTML output on line 8
* **Fix:** PayPal IPN `invoice` field was read from `$ipn` but was missing from the sanitized extraction array — added with `sanitize_text_field()`
* **Fix:** Removed double-wrapped `wp_nonce_url()` on instructor and slot edit links in `staff-slots.php`
* **Security:** Added `wp_verify_nonce()` + `wp_nonce_url()` to `classes.php` edit GET param (`slotwise_edit_class`)
* **Security:** Added `wp_verify_nonce()` to `admin/class-smb-admin.php` `page_classes()` edit GET read
* **Security:** `slotwise_admin_confirm` and `slotwise_confirm` GET params now wrapped with `sanitize_text_field( wp_unslash() )`

= 1.7.5 =
* **Fix:** PHP syntax error in `staff-slots.php` — missing `?>` closing tag before HTML output
* **Security:** Added nonce verification to `packages.php` edit GET param via `wp_nonce_url()` + `wp_verify_nonce()`
* **Security:** Package pre-select GET params in `shortcodes.php` now verified with `slotwise_pkg_preselect` nonce generated by `wp_nonce_url()` in the "Book This Package" button
* **Security:** Added `phpcs:ignore` annotations with full justification on token-authenticated handlers (`admin-cancel`, `cancel`) — 64-char cryptographic tokens serve as nonce equivalents for email link auth
* **Security:** PayPal IPN handler: added 4KB size limit on raw POST body; all fields extracted with individual `sanitize_*()` functions instead of using the raw array
* **Compliance:** `sltData` → `slotwiseData`, `sltAdmin` → `slotwiseAdmin` — JS localized object names now use the full plugin prefix (updated in 7 PHP and JS files)
* **Compliance:** `database.php` `get_all_bookings()` now uses `%i` identifier placeholder in `wpdb->prepare()` for `ORDER BY` column name instead of string concatenation

= 1.7.4 =
* **Compliance:** All global functions renamed to `slotwise_` prefix (`slotwise_run`, `slotwise_add_cron_intervals`, `slotwise_expire_pending_bookings`, `slotwise_plugin_action_links`, `slotwise_status_badge_class`, `slotwise_pay_pill_class`)
* **Compliance:** Added `phpcs:disable WordPress.NamingConventions.PrefixAllGlobals` to all 16 view/template files — local variables in included views are not in global PHP scope
* **Security:** All remaining `$_POST` numeric fields now wrapped with `sanitize_text_field( wp_unslash(...) )` before casting: `price`, `value`, `min_amount`, `slotwise_package_price`
* **Security:** Canvas form field AJAX handlers now use `sanitize_key()` for `field_key`/`type` and `sanitize_text_field()` for `label`/`placeholder`; `sort_order` uses `absint( wp_unslash(...) )`
* **Compliance:** `database.php` upgrade() method wrapped in `phpcs:disable/enable` block covering SchemaChange, DirectQuery, and NoCaching for all ALTER TABLE operations

= 1.7.3 =
* **New:** Backward-compatible shortcode aliases — old `[smb_booking_form]`, `[smb_classes]`, `[smb_packages]`, `[smb_package_booking]` continue working on existing pages after upgrade
* **New:** Automatic one-time shortcode migration — on activation/upgrade, all pages and posts are scanned and old `smb_*` shortcode tags are silently replaced with `slotwise_*` equivalents in the database; migration runs once and never repeats

= 1.7.2 =
* **Compliance:** Prefix renamed from `smb_`/`SMB_` to `slotwise_`/`SLOTWISE_` (6-char unique prefix) across all PHP classes, constants, options, AJAX hooks, nonce actions, and shortcodes
* **Compliance:** Removed CDN fallback for SortableJS — now bundled locally in `admin/js/vendor/Sortable.min.js`
* **Compliance:** Removed `ini_set('display_errors')` from security class — error display is WordPress core's responsibility
* **Security:** Added nonce verification to all admin GET reads (`staff-slots.php` edit forms, `discounts.php` edit links, `bookings.php` filter/search form)
* **Security:** All `$_POST` fields now explicitly extracted and sanitized with appropriate `sanitize_*()` functions before use; whole `$_POST` array no longer passed to any function
* **Security:** `esc_sql()` added to whitelisted `$orderby` and `$order` values in `get_all_bookings()` query
* **Escaping:** `wp_kses_post()` used for all HTML variable output in email templates; `wp_kses()` used for anchor link output in settings page
* **Shortcodes renamed:** `[slotwise_booking_form]`, `[slotwise_package_booking]`, `[slotwise_packages]`, `[slotwise_classes]`

= 1.7.1 =
* **New:** Active promo alert banner — when discount codes are running, an animated amber banner appears on the booking form showing the code and saving, with a scroll-to-field button
* **Fix:** Promo code field was hidden in some configurations; now always shown when active discounts exist
* **Improvement:** Promo code input shows green/red visual state on success/error; Apply button hides and Remove appears after successful application
* **Improvement:** Price summary redesigned with clearer breakdown rows, divider, and accent-coloured total
* **Fix:** Hero title on Welcome dashboard was hardcoded to "Music Studio" — now dynamically reflects active industry
* **Fix:** Theme match banner was appearing as white-on-white on some themes — replaced with solid visible styling
* **Compliance:** All inline `<style>` and `<script>` tags extracted to enqueued CSS/JS files (`wp_enqueue_style`, `wp_enqueue_script`) in accordance with WordPress plugin guidelines
* **Compliance:** Text domain corrected to match plugin slug (`slotwise`)
* **Compliance:** External services (Stripe, PayPal, Cash App) documented in readme

= 1.7.0 =
* **New:** Industry Setup Wizard — first-run wizard lets you choose from 12 business categories
* **New:** SLOTWISE_Industry_Profiles class — 12 industry presets each with services, staff, time slots, form fields, packages, and terminology
* **New:** Dynamic admin menus, page titles, and labels that update to match your selected industry
* **New:** Plugin rebranded to SlotWise
* **New:** Automatic theme colour detection expanded to 14 WordPress themes
* **New:** Welcome dashboard with animated stats, theme-match banner, and industry configuration panel

= 1.5.2 =
* **New:** Discount and promo code system — percentage or fixed amount, date ranges, usage limits, service restrictions
* **New:** Live AJAX discount validation on booking form with real-time price update
* **New:** Discounts admin page with full CRUD and usage tracking
* **Improvement:** Mobile UI improvements throughout
* **Compliance:** Plugin Checker compliance pass — all warnings resolved

= 1.5.1 =
* **New:** iCal (.ics) attachment on confirmation emails
* **Improvement:** Admin cancel flow improved with reason field and updated email template

= 1.5.0 =
* **New:** Visual drag-drop form builder with AJAX-powered reorder and inline field editing

= 1.4.9 =
* **New:** Admin can cancel any booking from dashboard with optional reason sent to customer
* **New:** Booking status tabs: All / Confirmed / Pending / Cancelled (with live counts)

= 1.4.8 =
* **New:** Self-service cancellation — secure token link in customer emails
* **Fix:** Apostrophe in customer name causing DB write error

= 1.4.7 =
* **New:** HTML confirmation email to customer with booking reference
* **New:** Admin notification email on each new booking

= 1.4.0 =
* **New:** Cash App as a fourth payment method
* **New:** Payment method visibility respects enable/disable toggles
* **Security:** Full audit pass; all output escaped; all queries use `$wpdb->prepare()`

= 1.3.0 =
* **New:** Custom calendar date picker replaces native input — weekends in red, booked dates in amber
* **New:** Slot blocking — admin can block specific date + time combinations
* **New:** Visual drag-drop form builder (initial version)
* **New:** Package redirect: "Get Started" on packages page scrolls to booking form

= 1.2.0 =
* **New:** `[slotwise_package_booking]` shortcode — simplified booking form for packages
* **New:** Multi-session lesson packages with pricing grid shortcode
* **New:** Stripe Elements for PCI-compliant card processing
* **Security:** All nonces, sanitization, and escaping hardened

= 1.1.0 =
* Calendar with AJAX-powered booked-date greying
* Rate limiting, nonce checks, IP proxy detection
* PayPal IPN callback for automatic payment confirmation

= 1.0.0 =
* Initial release: booking form, class listings, PayPal, Stripe, admin dashboard, email notifications

== Upgrade Notice ==

= 1.9.0 =
Privacy update — the plugin no longer loads fonts from Google, so visitor IP addresses are never shared with third parties.

= 1.8.5 =
Important update — fixes the package booking form rendering without its fields, and resolves all remaining code-standards findings.

= 1.8.4 =
Important update — fixes a bug that prevented bookings and settings from saving. All users should update.

= 1.8.3 =
Compatibility update — now tested and confirmed working with WordPress 7.0.

= 1.8.1 =
Recommended update — improves output escaping throughout the admin dashboard and ensures booking timestamps always use your WordPress timezone setting.

= 1.7.1 =
Safe to upgrade — your data is preserved. Fixes white-on-white theme banner, hardcoded hero title, and brings the plugin into full WordPress.org compliance.
