=== PocketEvents Pro - Event Manager ===
Contributors: maksim85
Tags: event management, event registration, event calendar, pwa, event booking
Tested up to: 6.9.1
Requires PHP: 7.4
Stable tag: 1.7.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Modern event management plugin for WordPress. Create and manage events, workshops, courses, and conferences with speaker management, multiple display layouts, online payments, and a built-in smartphone app (PWA).

== Description ==

**PocketEvents Pro** is a comprehensive, high-performance event management system for WordPress. Ideal for event organizers, training providers, conference hosts, workshop leaders, and any organization that manages events with registrations.

Create events with multiple dates, assign speakers, accept registrations, process payments, and manage everything from a powerful dashboard — or directly from your smartphone via the built-in Progressive Web App.

= Core Features =

* **Full Event Management:** Create and manage events, workshops, courses, and conferences with titles, descriptions, images, pricing, locations, and web conference links.
* **Multiple Event Dates:** Each event can have multiple dates/sessions with individual start and end times, speaker assignments, capacity limits, and price overrides.
* **Speaker / Instructor Management:** Manage speakers with profile photos, biographies, titles, and contact details. Assign speakers to events globally or per individual date.
* **Category System:** Organize events into color-coded categories. Filter frontend displays by category using shortcodes.
* **Ticket Types:** Define global ticket categories (e.g. "Standard", "VIP", "Early Bird") and assign them to events with individual pricing. Visitors select a ticket type during registration.
* **Participant Capacity:** Set minimum and maximum participant limits per event and per date. Auto-cancellation for events that don't reach the minimum.

= Registration & Participants =

* **Online Registration:** Visitors register for events through a responsive frontend form with event and date selection.
* **Multiple Registration Forms:** Create unlimited registration form layouts and assign one specific form per event. The default form is used automatically when an event has no custom form selected. Forms can be duplicated, previewed directly from the cards overview, and managed without affecting events that already use them.
* **Registration Status Workflow:** Track registrations through statuses: Pending → Confirmed / Rejected / Cancelled.
* **Group Registrations:** Support for individual and group registrations with company/organization name and multiple participants per registration.
* **Manual Registrations:** Admin can create registrations manually from the backend.
* **Participant Directory:** Central view of all unique participants across events, with search, marketing consent tracking, and event association overview.
* **Self-Service Cancellation:** Each registration includes a unique cancellation token. Participants can cancel via a link in their confirmation or reminder email.
* **Bulk Actions:** Select and delete multiple registrations at once. CSV export for data analysis.

= Frontend Display =

* **Multiple Layouts:** Display events in Card (Kacheln), Table, or Masonry grid layouts.
* **Shortcode System:**
  * `[pocketemp_events]` — Display event listings with configurable layout, columns, category filter, price display, and event limit.
  * `[pocketemp_events layout="kacheln" kategorie="ID" columns="3" show_price="true"]` — Card grid filtered by category.
  * `[pocketemp_events layout="tabelle"]` — Table view with date and status columns.
  * `[pocketemp_event id="123"]` — Display a single event in detail.
  * `[pocketemp_bookingform kategorie="ID"]` — Category-filtered event form (legacy compatibility).
* **Customizable Design:** Adjust colors (background, buttons, tiles, text), corner radius, and button styles via CSS variables with live preview.

= Payment System [Addon] =

* **Stripe Integration:** Accept credit card payments via Stripe Payment Intents API. Configure public key, secret key, and webhook endpoint directly in settings. No external dependencies required.
* **Invoice / Bank Transfer:** Built-in invoice payment method with custom title and description.
* **Custom Payment Methods:** Create unlimited additional payment methods with custom titles, descriptions, tooltips, and emoji icons.
* **25+ Currencies:** EUR, USD, GBP, CHF, SEK, NOK, DKK, PLN, CZK, HUF, JPY, CAD, AUD, NZD, HKD, SGD, RON, BGN, HRK, RUB, TRY, BRL, MXN, INR, and more.
* **Invoice Manager:** Auto-incrementing invoice numbers with configurable prefix, digit count, year inclusion, and suffix. Upload company logo, set company details, header/footer text.
* **PDF Invoices:** Generate professional PDF invoices with TCPDF. Email delivery and ZIP export.
* **VAT Support:** Configure VAT-exempt (small business) mode or percentage-based VAT with tax labels and VAT ID display.
* **Payment Tracking:** Track payment status (paid/pending) per registration with invoice history.

= Email Notifications =

* **6 Email Types:**
  1. Registration Received — Initial confirmation when a participant registers
  2. Confirmation — Registration approved by admin
  3. Rejection — Registration declined
  4. Cancellation — Registration cancelled (by admin or participant)
  5. Reminder — Automatic pre-event reminder (configurable 1–30 days before)
  6. Booking Confirmation — Payment-related confirmation
* **Custom Templates:** Separate email templates per language with dynamic placeholders ({name}, {event_name}, {event_date}, {event_time}, {event_location}, {company_name}, {message}, {payment_method}, {webcall_link}, {cancellation_link}).
* **Email Logo Header:** Upload a custom logo for email headers.
* **Quick Email Actions:** Accept or reject registrations directly from admin notification emails via styled action buttons — no login required.
* **Auto-Confirmation:** Optionally auto-confirm new registrations.

= Calendar & Scheduling =

* **Visual Calendar:** Full calendar view of all event dates with click-to-view participant details.
* **Calendar Subscription:** Subscribe to event calendars via iCal/ICS feed. Compatible with Google Calendar, Outlook, Thunderbird, and Apple Calendar. Token-based authentication for secure access.
* **Auto-Cancellation:** Events that don't reach the minimum participant count by the cancellation deadline are automatically cancelled with participant notification.

= Progressive Web App (PWA) =

* **Install as Native App:** Install the event management system as a native-like app on any mobile device or desktop — no app store required.
* **Offline Access:** Service worker caching (Network-First strategy) provides offline access to your management dashboard.
* **Push Notifications:** Receive instant push notifications for new registrations via VAPID-key encrypted messaging.
* **Full Mobile Management:** Access Dashboard, Registrations, Calendar, Categories, Speakers, and Statistics from the app.
* **Custom App Identity:** Configure app name, icons (badge + full color), colors, and splash screen.

= Multi-Language Support =

8 built-in languages with full interface and email template translation:

* 🇩🇪 German (de_DE)
* 🇬🇧 English (en_US)
* 🇮🇹 Italian (it_IT)
* 🇪🇸 Spanish (es_ES)
* 🇫🇷 French (fr_FR)
* 🇵🇱 Polish (pl_PL)
* 🇷🇺 Russian (ru_RU)
* 🇸🇪 Swedish (sv_SE)

= Security & Performance =

* Nonce verification on all forms and AJAX handlers
* SQL injection prevention with `wpdb->prepare()` throughout
* XSS prevention with proper escaping (`esc_html()`, `esc_attr()`, `esc_url()`)
* Capability checks with `current_user_can()`
* Token-based email actions with cryptographic tokens
* Direct access prevention on all PHP files
* Service worker caching for optimized PWA performance
* Transient caching for options and trial data

== Installation ==

= Installation via WordPress Dashboard (recommended) =

1. Navigate to `Plugins` > `Add New` in your WordPress admin area.
2. Search for "PocketEvents Pro".
3. Click `Install Now` and then `Activate`.
4. The setup wizard will automatically appear to guide you through the initial configuration.
5. Add the shortcode `[pocketemp_events]` to the page where you want to display your events.

= Manual Installation =

1. Download the plugin file (`.zip`).
2. Extract the `.zip` file on your computer.
3. Upload the `event-manager-pro` folder to `/wp-content/plugins/` on your server.
4. Go to `Plugins` in your WordPress admin area and activate "PocketEvents Pro - Event Manager".
5. Add the shortcode `[pocketemp_events]` to the desired page.

== Usage ==

= Setup Wizard =

After activation, the setup wizard automatically appears and guides you through 6 steps:

1. **Company Information:**
   - Enter company name
   - Configure notification email address
   - Enable/disable email notifications

2. **Opening Hours:**
   - Activate weekdays for events
   - Set time windows for each day
   - Configure time intervals

3. **First Event:**
   - Create your first event
   - Set event title, dates, and description

4. **First Speaker:**
   - Add a speaker/instructor
   - Set name, title, email, and profile photo

5. **Language:**
   - Select the desired language for frontend and backend
   - All texts are automatically translated

6. **Summary:**
   - Review your settings
   - Access the dashboard to start managing events

You can restart the setup wizard at any time via `PocketEvents` > `Settings` > "General" tab > "Setup Wizard" section.

= Getting Started =

1. **Configure General Settings:**
   - Go to `PocketEvents` > `Settings` > "General" tab
   - Enter company name and notification email
   - Configure cancellation settings and reminder emails (1–168 hours before event)

2. **Create Events:**
   - Go to `PocketEvents` > `Events` > `Add New`
   - Enter title, description, image, price, and location
   - Add event dates with start/end times, capacity, and speaker assignments
   - Optionally assign a category and ticket types with per-option pricing

3. **Add Speakers:**
   - Go to `PocketEvents` > `Speakers`
   - Add speakers with name, title, biography, contact info, and profile photo
   - Speakers can be assigned to events globally or per individual event date

4. **Organize with Categories:**
   - Go to `PocketEvents` > `Categories`
   - Create color-coded categories to group your events
   - Use category-filtered shortcodes for landing pages

5. **Configure Ticket Types (optional):**
   - Go to `PocketEvents` > `Ticket Types`
   - Define duration-based options (e.g. "1 Hour", "Half Day")
   - Assign options to events with individual pricing

6. **Set Up Payments [PRO]:**
   - Go to `PocketEvents` > `Payments` > `Settings`
   - Configure Stripe API keys and webhook for credit card payments
   - Add custom payment methods (bank transfer, cash, etc.)
   - Set your preferred currency

7. **Customize Email Templates:**
   - Go to `PocketEvents` > `Settings` > "Emails" tab
   - Customize subject and message for each of the 6 email types
   - Use placeholders: {name}, {event_name}, {event_date}, {event_time}, {event_location}, {company_name}, {cancellation_link}, and more
   - Optionally upload a logo for email headers

8. **Customize Design:**
   - Go to `PocketEvents` > `Settings` > "Design" tab
   - Adjust background, text, button, and tile colors
   - Set corner radius for form and tiles (0–50 pixels)

9. **Embed Events on Your Website:**
   - Create a new page or edit an existing one
   - Add the shortcode `[pocketemp_events]`
   - Publish the page

= Managing Events =

**Dashboard:**
- Go to `PocketEvents` > `Dashboard`
- View weekly and monthly statistics, upcoming events, registration overview
- Quick-access tiles for common actions (New Event, Settings, Calendar)
- Category-based statistics and shortcode overview

**Event List:**
- Go to `PocketEvents` > `Events`
- View all events with status indicators (active/inactive)
- **Duplicate Events:** Copy existing events with auto-generated names
- Click on any event to edit, or use quick actions to activate, deactivate, or delete

**Registrations:**
- Go to `PocketEvents` > `Registrations`
- View registrations grouped by event date
- Filter by status (All, Pending, Confirmed, Cancelled)
- Click any registration to view full details, change status, or send messages
- **Bulk Actions:** Select and delete multiple registrations at once
- **CSV Export:** Export filtered registrations for analysis

**Calendar:**
- Go to `PocketEvents` > `Calendar`
- Visual calendar view of all event dates
- Click any date to see participant details in a modal
- **Calendar Subscription:** Generate iCal/ICS feed URLs for Google Calendar, Outlook, Thunderbird, or Apple Calendar

**Participants:**
- Go to `PocketEvents` > `Participants`
- Central directory of all unique participants
- Search by name or email, filter by marketing consent
- See which events each participant is registered for

**Speakers:**
- Go to `PocketEvents` > `Speakers`
- Card-based view with photos, titles, and event counts
- Activate/deactivate speakers without deleting
- Set sort order for frontend display

**Statistics:**
- Go to `PocketEvents` > `Statistics`
- Date-range filtered analytics with quick filters (last month, 3 months, 6 months)
- Registration trends and cancellation rates

**Invoice Manager [Addon]:**
- Go to `PocketEvents` > `Invoice Manager`
- Configure invoice numbering (prefix, digits, year, suffix)
- Upload company logo and set company address
- Configure VAT settings (exempt or percentage-based)
- Generate PDF invoices with live preview

== Frequently Asked Questions ==

= What types of events can I manage? =

PocketEvents Pro is designed for any type of event: workshops, courses, conferences, seminars, meetups, training sessions, or recurring events. Each event can have multiple dates with individual capacity limits, speakers, and pricing.

= What is the Progressive Web App (PWA) feature? [PRO] =

The PWA feature allows you to install the event management system as a native-like app on any mobile device or desktop. Benefits include:
- **Offline Access:** Access your event dashboard even without internet
- **Push Notifications:** Get instant alerts for new registrations
- **App-Like Experience:** Full-screen mode, custom icon, and splash screen
- **No App Store Required:** Install directly from your website

To enable PWA, go to `PocketEvents` > `Settings` > "App" tab and configure your app name, icons, and push notifications.

= How do I install the app on my device? [PRO] =

**On Android/Chrome:**
1. Open your WordPress admin panel in Chrome
2. Look for the "Install" prompt in the address bar or click the menu (⋮) > "Install app"
3. Confirm installation

**On iOS/Safari:**
1. Open your WordPress admin panel in Safari
2. Tap the Share button (□↑)
3. Scroll down and tap "Add to Home Screen"
4. Tap "Add"

= Do push notifications work on all devices? [PRO] =

Push notifications require:
- HTTPS connection (SSL certificate)
- Supported browser: Chrome, Edge, Firefox, Samsung Internet (iOS Safari does not support push notifications)
- User permission to show notifications
- PWA app installed on the device

= How do I display events on my website? =

Add the shortcode `[pocketemp_events]` to any page or post. You can customize the display:

* `[pocketemp_events layout="kacheln"]` — Card grid (default)
* `[pocketemp_events layout="tabelle"]` — Table view
* `[pocketemp_events kategorie="5"]` — Show only events from category ID 5
* `[pocketemp_events columns="3" show_price="true"]` — 3-column grid with prices
* `[pocketemp_event id="42"]` — Display a single event in detail

= How many languages are supported? =

The plugin supports 8 languages: German (DE), English (EN), Italian (IT), Spanish (ES), French (FR), Polish (PL), Russian (RU), and Swedish (SV). All interface texts and email templates are available in each language.

= Can I customize the frontend design? =

Yes. Go to `PocketEvents` > `Settings` > "Design" tab. You can adjust background, text, button, and tile colors as well as corner radius values. Changes are applied via CSS variables for instant preview.

= How do payment integrations work? [PRO] =

PocketEvents Pro supports Stripe for credit card payments, invoice/bank transfer, and unlimited custom payment methods. Configure your Stripe API keys in `PocketEvents` > `Payments` > `Settings`. Select from 25+ currencies.

= How do reminder emails work? =

Reminder emails are automatically sent to participants before their event. Configure the timing (1–168 hours in advance) in `PocketEvents` > `Settings` > "General" tab > "Reminders" section. Each reminder includes the event details and a cancellation link.

= Can participants cancel their own registrations? =

Yes. Each registration receives a unique cancellation token. A cancellation link is included in confirmation and reminder emails. Participants click the link to cancel directly from the frontend. Both the participant and the administrator receive cancellation confirmation.

= Can I accept or reject registrations from emails? =

Yes. Each admin notification email contains styled action buttons to Accept, Reject, or View a registration — no need to log into the backend.

= How does the calendar subscription work? =

Go to `PocketEvents` > `Calendar` and click "Calendar Subscription". Generate a secure iCal/ICS feed URL, then paste it into Google Calendar, Outlook, Thunderbird, or Apple Calendar. Events sync automatically.

= Can I generate invoices? [PRO] =

Yes. The Invoice Manager lets you configure invoice numbering, upload your logo, set company details, and configure VAT. PDF invoices are generated via TCPDF and can be emailed to participants or exported as ZIP.

= What is the setup wizard? =

The setup wizard is an interactive guide that appears automatically after plugin activation. It helps you configure your event management system in 6 steps: Company Info, Opening Hours, First Event, First Speaker, Language, and Summary. You can restart the wizard at any time from `PocketEvents` > `Settings` > "General" tab.

= What happens when an event doesn't reach the minimum number of participants? =

If an event date reaches its cancellation deadline without meeting the minimum participant count, the system can automatically cancel the date and notify all registered participants via email.

== Screenshots ==

1. Event overview with card and table views, category filtering, and quick actions.
2. Event editor with dates, speakers, pricing, capacity, and image upload.
3. Registration list grouped by event date with status indicators and bulk actions.
4. Visual calendar view with click-to-view participant details.
5. Speaker management with card view, photo, title, and event count.
6. Category management with color coding, event counts, and shortcode display.
7. Settings page with email template customization and placeholder system.
8. Design settings with color pickers, corner radius, and live preview.
9. Frontend event display in card/grid layout with responsive design.
10. Dashboard with statistics, upcoming events, and quick-action tiles.
11. Payment settings with Stripe configuration and custom payment methods. [PRO]
12. Invoice Manager with PDF preview, numbering, and VAT configuration. [PRO]
13. Setup wizard guiding through initial configuration.
14. PWA smartphone app — Dashboard view. [PRO]
15. PWA smartphone app — Calendar view. [PRO]
16. PWA smartphone app — Registration management. [PRO]
17. Statistics page with date-range filtered analytics.
18. Participant directory with search, marketing consent filter, and event chips.

== Changelog ==

= 1.7.2 =
* FIXED (27.10.2026): More robust license check. If the license server cannot be reached, is busy or returns an error, the license stays active. Only a real server answer (invalid, expired, revoked or limit reached) changes the status; after 72 hours without a successful check the status changes to error. The license page checks at most every 12 hours, saving the key checks right away. After an error the plugin waits one hour (backoff) before the next request, also for update and trial checks. On a signature error the site registers again automatically, once. The license page shows a neutral notice instead of "License problem (Status: error)".
* IMPROVED (28.10.2026): License and update checks now use the new address mrstudio.codes.
* IMPROVED (28.10.2026): New contact address for license and support: plugins@mrstudio.codes.
* IMPROVED (28.10.2026): Renew now opens the secure checkout directly.
= 1.7.1 =
* IMPROVED: On the license page, Renew Support & Updates adds one year of support and updates for this license. After payment you return to the license page, where a notice shows the new end date in the plugin language. The invoice is sent by email.
= 1.7.0 =
* IMPROVED: On the license page the key is stored on your site and only the last characters are shown. Leave the field empty to keep the saved key. If the new check cannot be reached, the previous check is used.
= 1.6.9 =
* NEW: Tax-compliant credit notes (Gutschriften) for refunded bookings. When the payment status of a booking is set to "Refunded" (manually on the registration edit page, via the invoice list inline action, or via a Stripe refund), the system automatically generates a credit note as the leading document. The original invoice PDF is preserved unchanged.
* NEW: Credit notes receive their own number using a configurable prefix (default "GS-") combined with the year, digit, and suffix settings of the invoice number pattern. New "Credit Note Prefix" field in the Invoice Manager.
* NEW: The credit note PDF shows all refunded amounts as negative values including VAT, an explicit reference to the original invoice number with its date, and the payment status "Refunded" below the payment method.
* NEW: Both documents remain available separately: the invoice list, the booking detail modal, and the registration edit page now offer download buttons for the original invoice PDF and the credit note PDF.
* NEW: Database migration 4.2.0 adds the columns gutschriftnummer, gutschrift_pdf_pfad, and rechnungsdatum (invoice date) to the registrations table. The invoice date is backfilled from the booking creation date and stored when a payment is marked as paid.
* NEW: Email choice dialog on the Invoices page. Refunding a booking via Stripe, clicking "Mark as Refunded", or changing the payment status dropdown to "Refunded" now opens the same email choice dialog as on the registration edit page: the admin can send the credit note email (with the credit note PDF attached) to the guest or apply the refund silently. The credit note number and download link appear in the list and in the booking detail modal immediately, without a page reload.
* NEW: Admin BCC copy for document emails. A new setting in Settings > Emails (below the Email Header Logo) lets you enter an admin email address. Every guest email that contains an invoice or credit note PDF, whether it is the registration email, the confirmation email, a Stripe auto-accept email, a manually sent invoice, or a refund email, is also delivered as a blind copy to this address. Leave the field empty to disable the feature.
* NEW: Refund email templates. Settings > Emails now includes a "Payment refunded" card for individual registrations and one for group registrations, with separate templates per language. When the payment status is set to "Refunded" and the admin chooses to notify the guest, the guest now receives this dedicated credit note email with the credit note PDF attached instead of the generic registration email. New placeholders: {credit_note_number} and {invoice_number}. The email header uses the same light purple (#E8E0FB) as the Refunded badge.
* TRANSLATION: Added English source strings and German (de_DE) translations for all new credit note strings.

= 1.6.8 =
* NEW: License support and update status is now shown on the license page and in Settings → About. Active licenses display the support end date; expired support shows a warning with a direct renewal link.
* NEW: Added renewal buttons for Support & Updates packages on the license page, linking to the matching WooCommerce product for Standard, Pro, and Ultra license types.
* IMPROVED: The plugin updater now blocks update notifications when the license is invalid or the support/update period has expired, preventing installations without an active support package.
* TRANSLATION: Added English source strings and German (de_DE) translations for all new support and update messages.

= 1.6.7 =
* IMPROVED: Booking prerequisites description on the event editor Booking Prerequisites tab updated. The checkbox label now reads "Existing customers only" and the description clarifies that only active customers can register for the event, that they must enter their customer number or email address, and that persons who are not active customers cannot book. The prerequisite categories description now explains that active customers must additionally have completed a specific prerequisite category (e.g. a trial day or introductory walk).
* IMPROVED: Prerequisite categories description on the event editor Booking Prerequisites tab now clearly states that these prerequisites apply to events requiring an active customer status plus an additional prerequisite condition.
* IMPROVED: Main tooltip on the Customer Categories page restructured into three clearly separated sections: Booking Option (with price levels, assigned by admin to a customer account), Booking Option for Customers (customer can select it themselves during checkout), and Prerequisite (no price levels, manually assigned by admin once an active customer has completed a prerequisite).
* IMPROVED: "Booking Options" checkbox description on the Customer Categories edit page updated. Checked now reads: Booking option, can be manually assigned to a customer account by the admin. When that customer books an event, the option is automatically added to the booking (surcharge on invoice). Unchecked: Prerequisite. The admin assigns it once a customer has completed something (e.g. Phase 1 training). Events can require specific prerequisites so only eligible customers may register.
* IMPROVED: Customer detail page section heading "Category Assignment" renamed to "Customer Category / Automatic Booking Option" to better communicate that the listed categories are admin-assigned booking options automatically applied to invoices.
* FIXED: JavaScript error "e.target.closest is not a function" on the Customer Categories admin page. The tooltip mouseenter handler now guards against non-Element event targets (e.g. text nodes) before calling closest(), matching the existing guard in the mouseleave handler.
* TRANSLATION: Updated German (de_DE) translations and POT entries for all changed strings.

= 1.6.6 =
* FIXED: Email header logo was not displayed in Microsoft Outlook. The logo URL was embedded as an external src attribute which Outlook blocks by default. The logo is now resolved server-side to a local file path and embedded as a Base64 data URI, so Outlook receives the image data directly without making an external HTTP request. WordPress media library uploads and URLs mapped via ABSPATH are both supported. WebP images are automatically converted to PNG before encoding.
* FIXED: Email header logo showed a black background in Gmail dark mode when the logo is a transparent PNG. The logo is now wrapped in a small white-background table cell, which ensures transparent PNG areas always appear against white regardless of the email client's color scheme.

= 1.6.5 =
* FIXED: Guest confirmation emails (registration received, confirmed, cancelled, etc.) were not rendering correctly in Microsoft Outlook desktop. The emails appeared as unstyled plain text instead of the branded HTML layout. Root cause: the function pocketemp_build_guest_html_email() returned only a bare HTML fragment (a div element) without a complete document structure (no DOCTYPE, html, head, or body tags). Outlook's Word-based rendering engine requires a full HTML document to render HTML emails and falls back to plain text without it. Additionally, the layout used CSS div elements which Outlook handles poorly. The function now returns a complete HTML document with table-based layout, bgcolor attributes on all table cells (required by Outlook), and removes overflow:hidden which is unsupported by Outlook. Admin notification emails were unaffected as they already used the correct structure.

= 1.6.4 =
* NEW: Payment initialisation form in the Payment Details tab for registrations created without payment information. When no payment method is stored, the tab now shows a form with three dropdowns (Price, Payment Method, Payment Status) instead of a plain notice. All dropdowns start empty so the admin must actively choose each value. Clicking "Save and Generate Invoice" stores the payment details and immediately generates an invoice number.
* NEW: Price selection on the manual registration page (New Registration) redesigned as radio-button style cards. Each option shows a radio indicator on the left and the price and label on the right. The selected card is highlighted with the plugin's dark blue colour and a filled white radio dot, making the active selection clearly visible.
* FIXED: On the manual registration page, selecting a specific discount code from the price cards always applied the first configured discount code instead of the one actually clicked. The JS click handler now stores the selected code, amount and type in dedicated hidden fields, and the PHP handler reads those fields directly instead of iterating to the first valid code.
* FIXED: Fatal error "pocketemp_get_pm_attach_setting(): Argument #1 must be of type string, null given" on the registration edit page when a registration has no payment method. The null check now short-circuits before the function is called.
* FIXED: "Registration not found" error when clicking "Save and Generate Invoice" for a valid registration without payment info. The handler used get_var() on the zahlungs_methode column, which returns null both for a missing row and for a NULL column value. Replaced with get_row() so existence and column state are checked independently.
* TRANSLATION: Added German (de_DE) translations and POT entries for all new strings: Payment Details, the payment initialisation notice, Save and Generate Invoice, Payment saved and invoice generated, Payment information already exists for this registration, Please select a price, Please select a payment method, Please select a payment status, and the placeholder option text.

= 1.6.3 =
* NEW: Price adjustment field in the Payment Details tab of the registration edit page. When the payment status is Pending, a dropdown lists the original event price and all configured discount codes with calculated final prices. The admin can select any option and click Apply to update the booking price before the invoice is finalised.
* NEW: Email choice dialog for price adjustments. Before applying a price change, a modal asks the administrator whether a notification email should be sent to the guest.
* NEW: Safety lock for price adjustments when the invoice was already delivered at registration. If the selected payment method is configured to attach the invoice to the registration confirmation email, clicking Apply shows a blocking red warning dialog. The dialog explains that changing the price of an already-issued invoice is not permitted under EU invoicing regulations (same invoice number, different amount) and describes the correct procedure: cancel the registration and its invoice, then create a new registration with the correct price. The dialog also informs the admin how to disable the setting in Payment Settings to allow price adjustments on future registrations.
* NEW: New AJAX handler pocketemp_ajax_update_anmeldung_preis. Validates that the payment status is still Pending, stores the base price in preis_gebucht (before discount), and writes the discount code, amount, and type to the separate rabatt_* columns.
* NEW: New AJAX handler pocketemp_ajax_send_anmeldung_notification. Sends the standard registration confirmation email (with invoice and ticket attachments if configured for the payment method) to the guest on demand.
* FIXED: Double-discount bug in PDF invoice generation. Previously, the discounted final price was stored in preis_gebucht, and the PDF also subtracted the discount stored in rabatt_betrag_angewendet, resulting in the discount being applied twice. The fix stores only the base price in preis_gebucht; the discount columns remain the single source of truth and are applied once by the PDF renderer.
* IMPROVED: Apply buttons in the Payment Details tab now use the button-small CSS class instead of button-primary, consistent with the PDF download button.
* FIXED: Invoice number row in the Payment Details tab: the invoice number text is now vertically centred with the PDF download button using inline flex alignment.
* TRANSLATION: Added German (de_DE) translations and POT entries for all new strings: Adjust Price, The price will be changed to, Price Cannot Be Changed, Correct procedure, Cancel this registration and its invoice, To allow price adjustments in the future, Understood, Price can only be adjusted while payment is pending, Price updated, Notification sent, and the full EU law warning texts.

= 1.6.2 =
* NEW: Per-form "Allow Group Registrations" toggle in the form builder. The company section header now has a toggle to enable or disable group and company registrations for that specific form. When disabled, the "Register as Company / Group" toggle is hidden in the frontend booking form and the company and participant field sections are hidden in the form builder.
* FIXED: Custom section titles (e.g. "Anmeldedetails" instead of "Ihre Angaben") set in the form builder were saved correctly but not shown in the form preview in the backend or in the frontend booking form. Both now display the custom title.
* FIXED: On the Speakers (Referenten) list page, clicking Delete or Deactivate incorrectly redirected to the speaker edit page instead of performing the action and returning to the list.
* NEW: Speakers with active event assignments can no longer be deleted or deactivated. A clear error message is shown instead.
* NEW: When changing a registration status, a dialog now appears asking the administrator whether a notification email should be sent to the guest, or whether the status should be updated silently without sending an email.
* IMPROVED: The payment status section on the registration edit page now shows a full dropdown with all available payment status options instead of a single "Mark as Paid" button that was only visible when the status was Pending.
* FIXED: In the registration detail modal (date/appointment overview), the "Edit" link for an individual registration pointed to the wrong page. It now correctly links to the detail/edit page of the respective registration.

= 1.6.1 =
* FIXED: Masonry grid featured card (first event): the "Sold Out" overlay in the center of the hero image has been removed. The red "Ausverkauft" badge in the top badge row is sufficient and no longer competes with the event title.
* FIXED: Masonry grid featured card: the "Sold Out" badge (ghost variant) is now displayed with a solid red background (#dc3232) instead of the previous semi-transparent white overlay, making it clearly visible against any background color.
* FIXED: Masonry grid featured card on mobile (max-width 768 px): the badge row (category + sold-out) was left-aligned and could overlap the date chip in the top-left corner. The badge row is now centered on all screen sizes.

= 1.6.0 =
* NEW: Invoice-relevant flag per form field in the form builder. Each field in the individual and company sections now has a receipt icon toggle. When active (red), the field value is included in the invoice recipient block. System fields (Name, Email, Phone, Company Name, Company Address, Invoice Email) default to invoice-relevant. Custom fields default to off and can be enabled individually. Both system and custom fields can be toggled at any time.
* NEW: Invoice recipient block is now built dynamically from the form configuration. Instead of always showing the hardcoded name, email, and phone, the invoice PDF now renders exactly the fields marked as invoice-relevant for the form assigned to that event. Falls back to the previous hardcoded block when no fields are flagged (backward compatible).
* NEW: Customer master data (Stammdaten) is now applied when generating an invoice. If a customer's address, postal code, or other contact details were added or updated in the customer management view after the original registration, those values are now reflected on the invoice. For system fields, the customer profile value overrides the booking record value. For custom fields, the booking record takes priority with the customer profile as a fallback.
* TRANSLATION: Added "Invoice-relevant" (Rechnungsrelevant) and "Not invoice-relevant" (Nicht rechnungsrelevant) to the POT file and the German (de_DE) PO file.

= 1.5.9 =
* FIXED: Auto-cancelled dates were immediately moved to the archive, causing them to disappear from the registrations view without any chance for the admin to review them. Auto-cancelled dates now remain visible in the registrations list (page=pocketemp-anmeldungen) with their red "Auto-cancelled" badge until the admin explicitly clicks the new "Move to Archive" button in the date detail modal.
* FIXED: When a date was auto-cancelled and all remaining active dates of an event were gone, the event was automatically set to inactive. Events are no longer deactivated by the auto-cancel cron. An event without active dates will not appear in the frontend regardless and should remain active so the admin can add new dates without having to manually re-enable the event.
* FIXED: The capacity pill in the event archive tab showed the global event max-participant setting instead of the per-date override. The archive display now correctly uses max_teilnehmer_override first and falls back to the event-level max only when no override is set.
* FIXED: Rejected (ABGELEHNT) registrations were counted towards the capacity in the event editor Dates tab. Only Pending and Confirmed registrations are now included in the count.
* FIXED: The "Mark as Completed" button in the registrations modal was displayed for all dates including future ones. A CSS !important rule on the detail-actions container was overriding the JavaScript-set display:none. Fixed using a server-side data-is-past attribute and a dedicated pocketemp-hidden CSS class that also carries !important.
* NEW: The admin now receives an email notification (to the configured notification address) whenever a date is auto-cancelled. The email includes the event name, date, time, effective minimum participant count, and the number of cancelled registrations, plus a direct link to the registrations view.
* IMPROVED: The modal action button in the registrations view now shows "Move to Archive" (red styling) for auto-cancelled dates and "Mark as Completed" (green styling) for past dates, so the two actions are clearly distinct. The confirmation dialog text also differs accordingly.
* IMPROVED: Clicking the registration count for a date in the event editor Dates tab now deep-links to the registrations view with the detail modal for that specific date automatically opened via a termin_id URL parameter.
* IMPROVED: Mobile list layout (max-width 640 px): title, subtitle, and details link are now centered. Date chip and time are vertically centered when only one event date exists and no multi-date badge is shown.

= 1.5.8 =
* FIXED: Custom payment method override toggle (Eigene Zahlungsmethoden) was not persisted after saving an event. Root cause: the $format array for the event save query was missing one entry for the highlights_label field that was added to $data in v1.4.6 without a matching format specifier. This caused a one-position shift in format-to-data alignment, so zahlungsmethoden_override (a TEXT column storing JSON) received the %d integer format instead of %s. WordPress wpdb::prepare() cast the JSON string to 0 before executing the UPDATE. On reload json_decode("0") returned an integer rather than an array, so the override was always empty and the toggle reset to OFF. The same shift also corrupted preis (decimal prices truncated to integer), stornierungsdatum (date string stored as 4-digit year), and buchungs_voraussetzungen (booking prerequisite JSON stored as 0). Fixed by inserting the missing %s format entry for highlights_label at the correct position in the $format array.

= 1.5.7 =
* NEW: Per-participant category surcharges for company and group bookings. Administrators can assign price categories (defined in Participant Categories settings) to individual participants in a group booking. The surcharges are shown as separate line items in the checkout, written to the booked options for correct invoice generation, and included in Stripe payment intent amounts so the charged total is always accurate.
* NEW: Admin booking detail view shows the assigned price category per participant as a read-only tile, making it visible which category level was booked for each group member.
* IMPROVED: Stripe payment flow now includes per-participant category surcharges in the PaymentIntent amount for company group bookings. The pending booking transient is updated with correct gebuchte_optionen and participant preiskat values before the payment is initiated.
* IMPROVED: Category resolution now uses a two-level email-based fallback. When a customer card has empty or outdated category entries for a participant, the system searches the booking history by email address and falls back to per-participant preiskat values from earlier bookings.

= 1.5.6 =
* NEW: Per-date registration deadline (Anmeldefrist) field added to the event date editor. When set, the date is automatically hidden from the frontend after the deadline passes. If left empty, registration remains open until the event start date. The value is stored in a new database column anmeldefrist (migration 4.0.0) and enforced via SQL in both frontend event queries.
* IMPROVED: List layout (layout="list") now adapts to narrow containers such as 4-column WordPress block layouts. CSS container queries (container-type: inline-size on the list wrapper) switch the row to a compact 2-column grid with the date chip on the left and stacked event info on the right at container widths below 560px. The hard min-width: 230px on the date column has been removed so the layout no longer overflows in narrow contexts.
* TRANSLATION: Added missing German (de_DE) translation for the "Min. Participants Deadline" hint text. Added new translation strings for the registration deadline field in POT and de_DE PO files.
* FIXED: Participant email addresses entered during group/company registration were not displayed in the admin booking edit view. The form-field rendering loop skipped the email tile for system (non-removable) fields regardless of whether the field was visible to the user. The condition now only skips the email tile when the field is actually hidden in the form config.
* FIXED: The system phone field (Telefon) was shown in the Participant Details section for group/company bookings even though phone is not a system field in the company form. The row is now only rendered for individual bookings; for group bookings the phone number is shown only if it was added as a custom field in the company form config.
* FIXED: Group participant email confirmation now replaces the cancel link with an informational text instead of sending the real cancellation URL. Participants in a group booking cannot cancel on their own; the message directs them to contact the group organizer. All supported plugin languages are covered.
* TRANSLATION: Added missing POT and German (de_DE) translation strings for "Group Participants" and "Participant %d" used in the admin booking edit view.

= 1.5.5 =
* FIXED: Customer self-cancellation via cancellation link did not update the invoice payment status. The payment status remained "Bezahlt" after self-cancellation instead of switching to "Storniert" with the prior status preserved. The fix in templates/stornierung.php, admin/anmeldungen-liste.php, and pocketemp_ajax_update_anmeldung_status now mirrors the logic already used by the auto-cancellation cron: if the booking had a payment method, zahlungs_status is set to "storniert" and the prior status is saved in zahlungs_status_vor_stornierung so the invoice list can display "Storniert (hatte bezahlt)" or "Storniert (nicht bezahlt)" and offer the appropriate refund actions.
* FIXED: Custom form participant fields were not rendered when the "Register as Company/Group" toggle was activated in the frontend booking modal. pocketemp_generate_participant_rows resolved the event ID via .closest('.pocketemp-modal') which never matched because the modal container uses class pocketemp-modal-overlay. As a result the event ID was always 0, causing pocketemp_form_config_for to fall back to the default form. The function now reads the event ID from the hidden pocketemp_event_id input inside the form, which is always present and correct.
* IMPROVED: Admin select/dropdown fields now display text correctly. The shared input rule set line-height: 0 which compressed text in select elements. The select-specific rule now overrides line-height to normal and adds vertical padding (6px top/bottom) so option labels are fully readable.
* IMPROVED: Custom dropdown arrow (SVG chevron via background-image) is no longer accidentally overridden. padding-right: 35px is now marked !important in the select rule so no conflicting padding can hide the arrow.
* IMPROVED: Removed a redundant inline style attribute from the category select in the event editor sidebar. border, border-radius, width, and padding were duplicated from the stylesheet. A new CSS rule (.pece-sidebar-meta select) sets max-width: 100% !important for sidebar selects instead.

= 1.5.4 =
* FIXED: Auto-cancellation cron incorrectly cancelled event dates that had already met the minimum participant count. The scheduled single-termin cancel handler (pocketemp_handle_single_termin_cancel) now checks the effective minimum against the current active participant count and aborts when the minimum is reached, matching the HAVING clause logic used by the main nightly cron.
* NEW: Prior payment status is now preserved when a booking is cancelled. A new database column zahlungs_status_vor_stornierung (migration 3.9.0) stores the payment status that was active before cancellation. Both the auto-cancel cron and the manual invoice cancel action populate this field.
* NEW: Invoice list now distinguishes between cancelled bookings that had previously paid and those that never paid. Cancelled entries show either "Storniert (hatte bezahlt)" (red badge) or "Storniert (nicht bezahlt)" (grey badge) based on the saved prior payment status. The same annotation appears in the booking details modal and in the refund action button styling.
* FIXED: Booking details modal (Buchungsdetails) did not show the prior payment status annotation for cancelled invoices. The zahlungs_status_vor_stornierung field was missing from the booking_data array passed to the modal, so "Storniert (hatte bezahlt)" and "Storniert (nicht bezahlt)" were never rendered. The field is now included.
* IMPROVED: Status changes are now blocked for bookings whose event date was automatically cancelled. The action buttons (confirm/reject) in the registrations modal are hidden when the date has auto_cancelled = 1. As an additional backend guard, the pocketemp_ajax_update_anmeldung_status AJAX handler rejects any status-change request for such bookings with a clear error message.

= 1.5.3 =
* NEW: About tab added to Settings. Displays plugin version, Plugin Updates status, a feature request link, and a language-aware What's New section. The What's New content is loaded from updates-de.md (German) or updates-en.md (English) based on the active language setting.
* NEW: User-friendly What's New files (updates-de.md, updates-en.md) added to plugin root. Each feature entry carries a ::new::, ::fixed::, or ::improved:: badge marker that is rendered as a color-coded chip next to the section heading.
* IMPROVED: Plugin description in the About tab updated to include customer management alongside registrations, tickets, invoices, and reminders.

= 1.5.2 =
* FIXED: Reminder emails were sent as plain text instead of HTML. The cron handler now uses the central pocketemp_send_event_email() function, which wraps the message in the standard branded HTML email template and sets Content-Type to text/html. Group bookings automatically use the group reminder template via the existing group-template routing logic.
* NEW: Editable reminder email templates added to the Settings email templates page. A new card in the individual section (Reminder Email - Individual) and one in the group section (Reminder Email - Group) allow full control over subject and message body. Option keys: pocketemp_event_email_erinnerung_betreff, pocketemp_event_email_erinnerung_nachricht, pocketemp_event_email_grp_erinnerung_betreff, pocketemp_event_email_grp_erinnerung_nachricht.
* NEW: Default reminder email template texts for all supported languages (de_DE, en_US, en_US_AMPM, it_IT, es_ES, fr_FR, pl_PL) for both individual and group bookings. Templates are shown pre-filled in the settings page and used as fallback when no custom template has been saved yet.
* IMPROVED: Reminder email template cards in the settings email tab are now hidden automatically when the reminder activation toggle is turned off, and reappear instantly when re-enabled.

= 1.5.1 =
* FIXED: Event description formatting was lost in the frontend event detail modal. Text entered in the WordPress editor (classic/TinyMCE) using paragraphs, line breaks, and blank lines was displayed as a single unformatted text block. Root cause: the AJAX handler returned the raw database value without applying WordPress paragraph processing. The handler now runs wpautop() on the description before sending it to the frontend, converting double line breaks to paragraph tags exactly as WordPress does for standard post content.

= 1.5.0 =
* FIXED: "Existing customers only" (Nur fuer bestehende Kunden) flag on the Booking Prerequisites tab was not being saved on online/production installs. The database column `nur_fuer_kunden` was never added by a migration, so it was silently skipped on installs where it did not exist. Migration 3.8.0 now adds the column to existing databases. The column was also added to the CREATE TABLE schema for fresh installs.
* FIXED: Per-event highlight section label was ignored in the frontend event detail modal. The modal always showed the global label from plugin settings instead of the per-event `highlights_label` value stored in the database. The modal JavaScript now uses the per-event label with a fallback to the global setting.

= 1.4.9 =
* FIXED: Invoice Manager "Refresh Preview" button now reflects unsaved form values immediately without requiring a settings save first. A new AJAX endpoint (pocketemp_preview_invoice_pdf_live) accepts the current form state via POST, sanitizes all fields, and passes them as overrides to the PDF generator. The preview iframe receives the resulting PDF as a Blob URL. The PDF class methods generate_preview_pdf(), build_sample_invoice_number(), and get_pdf_content() now all accept an optional $overrides array so saved DB values can be selectively replaced for preview purposes.
* IMPROVED: Currency selector on the Payments > Currency page replaced the native <select> dropdown with clickable tile chips. Each chip shows the currency symbol and 3-letter code. The active currency is highlighted in the plugin's primary color (#1a4a8a) with white text. A hidden input carries the selected value on form submit. This eliminates rendering issues on third-party themes where dropdown option styling (e.g. Euro symbol) was overridden or lost by theme CSS.

= 1.4.8 =
* FIXED: Archived event dates (marked as Completed in the event editor) are now excluded from all frontend SQL queries. Such dates were previously visible in the event card overview, the event detail modal, and the booking form date selector. All three relevant queries now include AND t.archiviert = 0 so archived dates never appear in the frontend.
* IMPROVED: Event detail modal date chips now distinguish between sold-out and available dates. Sold-out dates are rendered as red chips; available dates appear in the category color. Users can see at a glance which dates cannot be booked.
* IMPROVED: Date badge in the event detail modal turns red when all upcoming dates for an event are sold out, providing an immediate visual signal before the modal is opened.
* IMPROVED: Sold-out date chips in the booking form date selection step are now styled with a red border and red text color so unavailable dates are immediately recognizable.
* FIXED: The Register button in the event detail modal now correctly shows Sold Out only when all future (upcoming) dates are sold out. Previously, past dates were included in the check, which could incorrectly mark an event as sold out even when upcoming dates were still available.

= 1.4.7 =
* NEW: Invoice number generation mode in Invoice Manager. Administrators can now choose between Sequential (next number = last + 1) and Random mode (unique random number within the configured digit range). The setting is stored per installation. If no free random number is found after 100 attempts, the system falls back to sequential automatically.

= 1.4.6 =
* NEW: Per-event highlight label. The label for the Highlights section (e.g. "Highlights", "What you will learn") can now be set individually per event in the event editor. New events default to the global label setting (Settings). Changing the label on one event does not affect any other event.
* FIXED: Customer booking toggle did not open for customers with exactly one booking. Clicking the customer row or the toggle arrow button had no effect. Fixed — the expand/collapse now works correctly for all customers with one or more bookings.
* IMPROVED: All new strings added to pocketevents-pro.pot and translated in pocketevents-pro-de_DE.po.

= 1.4.5 =
* NEW: Custom login URL setting in App Settings (PWA). Administrators can now enter a custom login path to override the default wp-login.php redirect. This is required for environments that use a custom login page (e.g. security plugins that disable the default WP login URL). The custom URL is stored in the plugin options and used everywhere a PWA login redirect is generated: the App infobox link, the auth redirect, the logout redirect, and the access email.
* NEW: Invoice archive feature. A new "Archive" button and "Archived" filter tab have been added to the Invoices page. Invoices can be soft-archived (hidden from the main view without deleting them). A counter badge shows the number of archived invoices. A new database column `inv_archiviert` is added automatically via ALTER TABLE on first load. The Archived tab allows viewing and unarchiving individual invoices.
* NEW: Speaker events tab in the speaker detail page (dozent-bearbeiten.php). A second tab "Events" shows all events the speaker is linked to, with event name, date, status, and a direct edit link.
* NEW: Push subscriptions table in App Settings. The previous "Delete all subscriptions" button is replaced by a table listing each active push subscription with user name, device/browser info, and activation date. Each row has an individual delete button. Bulk-delete remains available.
* IMPROVED: "Open app in browser" button in App Settings is now always visible on all screen sizes. Previously both buttons (Open in Browser, Copy Link) were hidden on desktop. "Copy Link" remains mobile-only.
* IMPROVED: App Settings layout restructured. App Name and Short Name remain in a 2-column row. Custom Login URL spans the full width below them. App Icon and Push Notifications return to 2 columns. Achieves a cleaner visual hierarchy.
* IMPROVED: Customer archive instead of delete. "Delete Customer" has been replaced with "Archive Customer" (soft-delete, status: archiviert). Archived customers are excluded from all active lists and filter tabs, and are accessible via a dedicated "Archived" filter tab. A "Reactivate" button on the detail page restores the customer to active status.
* IMPROVED: Stripe bookings now correctly assign a customer number and store form data during the payment confirmation webhook. Previously new customers created via Stripe checkout were missing their customer number and submission metadata.
* IMPROVED: Customer cards in the customer list now always start collapsed, including customers with only one booking. A "1 Booking" count badge is now also displayed for single-booking customers (previously only shown from 2 bookings).
* IMPROVED: Global tooltip CSS updated across all admin pages. All question-mark tooltips now use the plugin's primary color (#1a4a8a) as background with white text. Tooltip on the Invoice Archive button is now triggered only by hovering the question-mark icon, not the entire button.
* IMPROVED: Tooltips and descriptions updated in the Customer Categories page (teilnehmer-kategorien.php) for "Active" and "Booking Options" fields to reflect the current customer management system.
* IMPROVED: Masonry grid layout fix. Events with long highlight text no longer cause the date, time, and location badge to wrap to two lines. The badge section is now capped to a single line with overflow handling.
* IMPROVED: Speaker/Lineup page hardcoded German strings fixed. Page title "Referenten / Lineup" and button "Neuen Referenten hinzufügen" replaced with the correct English source strings "Speakers / Lineup" and "Add New Speaker" which already existed in the translation files.
* FIXED: Discount code suffix in guest booking confirmation emails. Fixed-amount discount codes incorrectly showed "SAVE (-50%)" instead of "SAVE (-50€)" in the email sent to the guest. The admin email was already correct; the guest email template now applies the same type-check logic.
* FIXED: Discount code suffix in the manual booking admin page (neue-anmeldung.php). Same fix as above applied to the manual booking UI where fixed-amount codes also showed percent instead of the currency symbol.
* FIXED: Booking confirmation modal (frontend) displayed time in HH:MM:SS format. Seconds are now stripped so the time shows as HH:MM only. Fix applied to both the standard booking and the Stripe payment confirmation handlers.
* FIXED: Termin date/time lock in the event editor. When a date (Termin) has at least one non-cancelled registration, the Start Date, From time, and To time fields in the Termin edit modal are disabled and a yellow notice explains the restriction. A server-side guard additionally restores the original values on save, preventing changes even if the JS lock is bypassed.
* IMPROVED: All new strings from the above features added to pocketevents-pro.pot and translated in pocketevents-pro-de_DE.po.

= 1.4.4 =
* FIXED: Corrupted emoji/icons in the Invoices admin page (payment-invoices.php) restored. All filter tab icons (⏳ Pending, ✓ Paid, ✗ Cancelled, 🚫 Rejected, ↩ Refunded), action button icons (✓ Mark as Paid, ↩ Reset to Pending, ↩ Refund via Stripe, ↩ Mark as Refunded), and the empty-state document icon (📄) were replaced with HTML entity equivalents to prevent future encoding corruption. Payment method column icons (🧾 Invoice, 💳 Stripe card, and method-specific icons for PayPal, SEPA, Klarna, Sofort) restored.
* IMPROVED: Customer Management hub page tile layout fixed to 2 columns (matching the Payments hub page) and wrapper width constrained to fit the 2×2 tile grid. Previously the wrapper was wider than the tiles, leaving unused space on the right.
* IMPROVED: "Create Customer" submenu entry removed from the WordPress sidebar. The page remains accessible via the Customer Management hub tile. Now consistent with Customer Categories and Customer Numbers which are also tile-only sub-pages.
* IMPROVED: Booking Prerequisites section in the event editor (tab "Voraussetzungen"): all display strings are now properly translatable via __() / esc_html_e(). New strings added to POT and de_DE PO files: "Booking Prerequisites", "Existing customers only", the description for customer-only restriction, the prerequisite category description, and the empty-state notice.
* IMPROVED: Discount type toggle labels in the event editor (Global Discount Codes) replaced hardcoded German strings ("Rabatt in Wert", "Rabatt in Prozent") with translatable English source strings "Discount in Value" and "Discount in Percent". German translations added to de_DE PO file.
* IMPROVED: PWA hamburger menu entry "Teilnehmer" renamed to "Customer Management" and now links to the Customer Management hub page (pocketemp-teilnehmer-verwaltung) instead of the customer list. Active state covers all customer sub-pages.
* IMPROVED: PWA bottom navigation now includes a "Customers" item (people icon) between Anmeldungen and Kalender. Active state covers all customer management sub-pages.
* IMPROVED: All customer management sub-pages (pocketemp-teilnehmer-verwaltung, pocketemp-teilnehmer-kategorien, pocketemp-teilnehmer-kundennummern, pocketemp-kunde-anlegen) added to the PWA pages list so the PWA navigation chrome renders correctly on all those pages.

= 1.4.3 =
* NEW: Archive tab in the event editor now shows a readonly participants modal when clicking the registrations badge of an archived date. The modal uses the same layout, header, and table styling as the registrations page modal, including the event name, date, capacity pill, and an edit event button.
* NEW: "Mark as Completed" button added to the registrations modal (Anmeldungen page). For past dates, admins can archive the date directly from the modal without navigating away. Uses a green confirm dialog. The event card is removed from the list after archiving.
* FIXED: Archive modal table header now correctly uses the plugin color setting (--pocketemp-container-bg) instead of a hardcoded blue gradient. The CSS variable is now properly injected on the registrations page as well.
* FIXED: Confirm dialog for the archive action in the event editor now uses the green positive confirm style (pocketempConfirmPositive) consistent with the registrations page.
* FIXED: Registrations badge hover color corrected (was showing white text on dark background).
* IMPROVED: CSS variable injection added for the registrations list page so modal header and table header colors always reflect the configured plugin color scheme.

= 1.4.2 =
* IMPROVED: Added German translations for the "Download PDF" (PDF herunterladen) and "Send Invoice" (Rechnung senden) buttons in the booking detail modal on the Invoices page. Both strings are now covered in the de_DE PO file.

= 1.4.1 =
* FIXED: Default form usage count in Settings > Form did not include events that fall back to the default (events with no explicitly assigned form). Previously, an event using the default automatically via fallback was counted as "not assigned" on the default form card. Now the default form's "Used by X event(s)" includes both explicitly assigned events and events with no form_id (NULL or 0), so the count reflects the actual number of events that will render this form on the frontend.

= 1.4.0 =
* NEW: Multiple registration forms. The single form builder has been replaced by a forms manager that lets you create, duplicate, and delete any number of registration form layouts. One form is always marked as default and is used whenever an event has no specific form assigned.
* NEW: Per-event form assignment. The event editor has a new tab "Form" with a dropdown to pick the form for that event. No live preview, just a dropdown plus a quick link to manage forms.
* NEW: Settings > Form has been redesigned as a card grid showing each form with its name, default badge, and event usage count. Editing a form opens the existing builder with a name field and a "Use as default form" toggle.
* NEW: Database migration 2.11.0 creates the pocketemp_forms table and adds a form_id column to the events table. The existing single form configuration is automatically migrated into the new table as the default form, no manual action required.
* NEW: Frontend automatically loads the correct form per event from the localized configuration map. Default form is used as fallback.
* CHANGED: Form deletion is blocked when the form is assigned to one or more events, or when it is the default form. Reassign the default to another form first to remove the previous default.
* CHANGED: Save handler in the form builder now writes to a specific form row and mirrors the default form into the legacy option for backwards compatibility with any third-party reads.

= 1.3.7 =
* FIXED: Invoice logo not appearing in generated PDFs on hosting environments where the WordPress upload path differs from the server DOCUMENT_ROOT (e.g. STRATO /mnt/web506/... paths). Root cause: TCPDF's HTML parser prepends DOCUMENT_ROOT to absolute image paths when DOCUMENT_ROOT is not found within the path, producing an invalid combined path that TCPDF silently ignores. The fix converts the cached logo to a Base64 data URI (data:image/png;base64,...) before passing it to the PDF template. TCPDF has native data URI support in its HTML parser and processes the image data directly without any filesystem path resolution, making logo rendering reliable on all hosting configurations regardless of path structure.

= 1.3.6 =
* FIXED: Invoice logo not appearing in generated PDFs even when the logo file was successfully cached and the file existed on disk. Root cause: the previous implementation rendered the HTML template with a URL in the img src attribute, then tried to replace that URL with the local filesystem path using a regular expression. The regular expression that matched class before src in the tag never fired because the template emits src before alt before class. The fallback regex was also fragile and could produce unexpected results on certain server configurations. Fix: get_pdf_content() now resolves the logo to a local filesystem path (via get_logo_file_path()) before rendering the template and passes it directly as $logo_path. The template sets $logo_path as the img src when available. TCPDF receives the local path it requires without any post-render string manipulation. The entire regex-based HTML replacement block has been removed from both generate_pdf() and generate_preview_pdf().

= 1.3.5 =
* FIXED: Invoice logo continued to be missing from generated PDFs on some server environments, even after the v1.3.3 caching fix. Three independent root causes were identified and addressed. (1) Silent WebP byte mismatch: image optimisation plugins (Imagify, Converter for Media, ShortPixel) can replace the bytes of an uploaded PNG/JPG with WebP content while keeping the original file extension. The path resolution code found the file on disk, TCPDF tried to parse it as PNG or JPEG, failed silently, and produced a PDF without a logo. Fix: all local file paths are now validated with getimagesize() which detects the true MIME type regardless of extension; WebP files are automatically converted to PNG via PHP GD before being passed to TCPDF. (2) Loopback HTTP blocked: many shared hosting environments and servers with strict firewall rules block outgoing HTTP requests from PHP back to the same server (loopback). When the logo URL belonged to the same WordPress installation, wp_remote_get() failed or timed out. Fix: a new Step 3 resolves same-server URLs directly via ABSPATH + site_url/home_url string math, without any HTTP request. This covers not only upload-directory URLs but any file accessible via the site URL. (3) HTTP download accepted invalid content: if a URL was blocked by hotlink protection and the download returned HTML with a misleading image/* Content-Type header, the HTML was saved as the logo file, getimagesize() then failed on the written file, and it was discarded. Fix: the downloaded file is now also validated with getimagesize() after writing; invalid files are deleted and a failure is reported.
* Added "Test Logo" button to the Invoice Manager. Clicking it runs the full logo-caching pipeline and shows a step-by-step diagnostic log so administrators can see exactly which resolution step succeeded or failed on their server. The log includes the detected MIME type, the resolved filesystem path, HTTP response codes, and specific error messages for each step.

= 1.3.4 =
* FIXED: Emails with PDF attachments (invoice, ticket) were delivered without the attachment when MailPoet with "Send all WordPress emails through MailPoet" was enabled. Root cause: MailPoet replaces the global WordPress PHPMailer instance with its own WordPressMailer on plugins_loaded. WordPress reuses this instance for all wp_mail() calls. WordPressMailer::send() routes the email via MailPoet's API, and the API transport only reads the message body, completely ignoring the attachment array. The previous fix (adding attachments via a priority-9999 phpmailer_init hook) did not help because even though the files were added to the attachment array, MailPoet's send() path never reads that array. The new fix detects that the global $phpmailer is not the standard PHPMailer\PHPMailer\PHPMailer class, temporarily clears the global before the wp_mail() call (forcing WordPress to create a fresh standard PHPMailer instance for this one send), then restores the MailPoet instance afterward so subsequent emails without attachments continue to flow through MailPoet normally. SMTP plugins such as WP Mail SMTP are not affected because they configure the mailer via phpmailer_init hooks, not by replacing the global instance.

= 1.3.3 =
* FIXED: Invoice logo not appearing in the PDF or in the admin preview when the logo URL points to an external server that uses hotlink protection (blocks image requests from other domains). The previous implementation downloaded the image fresh on every PDF generation using a temporary file, which failed silently whenever the source server returned a 403 or redirect instead of the actual image. The fix downloads and caches the logo to a persistent local file (wp-uploads/pocketemp/invoice-logo.{ext}) the moment the settings are saved. Subsequent PDF generations and the admin preview both use this local cached copy, so no external HTTP request is needed at render time. The HTTP 200 status code and image/* Content-Type are now validated before accepting the download, rejecting HTML error pages that hotlink-protection systems return. SSL certificate errors trigger an automatic retry without strict verification. WebP images (converted by optimization plugins) are automatically converted to PNG via PHP GD when available.

= 1.3.2 =
* FIXED: Invoice and ticket PDF attachments were silently dropped when MailPoet (or another email plugin) is active and configured to route all WordPress emails through its own sending API. The root cause: MailPoet replaces the WordPress PHPMailer instance inside the `phpmailer_init` hook with its own `WordPressMailer`, whose API transport ignores the attachment list that was previously built by `wp_mail()`. Introduced a new internal helper `pocketemp_send_mail()` that passes attachments through two independent channels: the standard `wp_mail()` `$attachments` parameter (works for all normal setups) plus a `phpmailer_init` hook at priority 9999 that re-adds any missing files directly to whatever mailer instance is active after all third-party plugins have run. This makes attachment delivery robust regardless of which mail transport or plugin is installed.
* IMPROVED: Invoice Manager logo field now accepts both a media library selection and a direct image URL. A visible URL input field was added below the existing "Select Logo" button so admins can paste any external or CDN image URL directly. The PDF generator resolves the logo in three stages: (1) media library attachment path, (2) direct local upload path, and (3) as a last resort fetches the URL via HTTP and writes it to a temporary file, so TCPDF can always read the image even when image-optimization plugins have replaced or removed the original file on disk.

= 1.3.1 =
* NEW: Group Registrations toggle in Settings > General. When disabled, the "Register as Company / Group" toggle is hidden in the frontend form and the Company / Participant sections are hidden in the form builder.
* NEW: Eye icon (visibility toggle) for Phone and Message system fields in the form builder. Hidden fields are not rendered in the frontend registration form.
* FIXED: Form builder labels (Required, Optional, field type names) were always falling back to English defaults because the `formBuilderLabels` object was read from the legacy `SimpleBookingEinstellungen` variable instead of `pocketempEinstellungen`.
* FIXED: PHP 8.x fatal error when opening the "Create New Event" page. The variable `$event_termine_archiv` was only assigned inside the `if ($event_id > 0)` block, causing a `TypeError` on `count()` when no event ID was present. PHP 7.4 silently returned 0 for undefined variables, but PHP 8.0 throws a fatal error.
* FIXED: Event dates in the admin events list (table view "Next Date" column and card view date chips) were always displayed in German regardless of the plugin language setting. Replaced `date_i18n(get_option('date_format'))` with `pocketemp_format_date()` so the plugin's own language setting is respected.

= 1.3.0 =
* NEW: Per-event "No Payment Required" option. When enabled for an event, the checkout step is skipped in the frontend even if payments are globally active. Participants can register without paying.
* NEW: "Custom Payment Methods" and "No Payment Required" toggles in the event editor are now mutually exclusive. Enabling one automatically disables the other.
* IMPROVED: License menu slug renamed from `pocketemp-reservierungen-lizenz` to `pocketemp-lizenz` for consistency with all other plugin page slugs.
* IMPROVED: Demo user (subscriber role) can now access all plugin menu pages including Payments, Tickets, and License when the plugin is in demo mode.
* IMPROVED: Added i18n strings for "No Payment Required" and its description to the POT file and German translation.

= 1.2.9 =
* FIXED: Push notification was not triggered on the frontend when a visitor registered for an event. The push block was only present in the SimpleBooking handler but missing from the main event registration AJAX handler.
* FIXED: Google Wallet pass always showed "Ticketnummer: VIP" because the ticket type name was placed in the `ticketNumber` field, which renders with a fixed "Ticket number" label. The real ticket number is now in `ticketNumber`, and the ticket type is shown as a separate `textModulesData` entry with a "Ticket Type" label.
* FIXED: Google Wallet pass showed the old event date after a date change. The Wallet class ID was based only on the event ID, causing Google to serve the cached class. The class ID now includes the date ID so each event date gets its own unique Wallet class.
* IMPROVED: Ticket Scanner "Auto check-in valid tickets" toggle is now enabled by default.
* IMPROVED: Renamed legacy JavaScript global `SimpleBookingEinstellungen` to `pocketempEinstellungen` in both the PHP localization call and the settings JS file, removing the last reference to the old plugin.
* IMPROVED: Added missing i18n strings for the Ticket Scanner to the POT file and German translation.

= 1.2.8 =
* FIXED: PWA install overlay never appeared when clicking "App im Browser öffnen" -- the overlay and its child elements carry the class `pocketemp-hidden` which has `display: none !important` in `backend-style.css`. jQuery's `fadeIn()` sets an inline style that CSS `!important` overrides. Fixed by calling `removeClass('pocketemp-hidden')` before each `fadeIn()` call for the overlay, loading state, and success state.
* FIXED: Ticket Scanner button in the Ticket List filter bar now correctly appends `&pwa=1` to the URL when the admin is in PWA mode, ensuring the scanner page opens inside the PWA chrome.
* IMPROVED: Digital ticket view now displays the ticket type name (e.g. "Standard", "VIP") beneath the QR code instead of the raw ticket number. Falls back to the ticket number when no ticket type is assigned.
* IMPROVED: Google Wallet pass `ticketNumber` field now shows the ticket type name instead of the internal ticket number, matching the printed ticket display.
* FIXED: Toggle rows (Firma/Gruppe, Bestaetigungs-E-Mail, etc.) on the Neue Anmeldung form were always visible regardless of the related checkbox state. Root cause: `display: table-row !important` in `neue-anmeldung.css` blocked jQuery's `.hide()` and `.show()`. Removed `!important` from the `<tr>` display rule so JS show/hide works correctly again.

= 1.2.7 =
* NEW: Ticket-Scanner button added to the Ticket List filter bar — a green camera button next to the search field links directly to the Ticket Scanner page for quick access from the ticket overview.
* NEW: PWA bottom navigation updated — Rechnungen (Invoices) and Tickets added as persistent bottom nav items; Referenten and Teilnehmer removed. Zahlungen and Tickets added to the hamburger side menu.
* NEW: Rechnungen bottom nav item links directly to the Invoices list page (pocketemp-payment-invoices) instead of the Payments hub.
* NEW: Ticket pages (pocketemp-tickets, pocketemp-ticket-list, pocketemp-ticket-scanner, pocketemp-ticket-settings, pocketemp-ticket-manager) are now registered as PWA pages, ensuring the bottom navigation and PWA chrome are shown on all ticket-related admin pages.
* IMPROVED: PWA bottom nav active state — replaced fragile URL string-includes matching with URLSearchParams-based page parameter comparison. Active color now correctly applies to all nav items (Anmeldungen, Kalender, Rechnungen, Tickets, Einstellungen), not only to Veranstaltungen.
* IMPROVED: PWA bottom nav active item is automatically scrolled into the center of the nav bar on page load, so the current page is always visible without manual scrolling.
* FIXED: `--pocketemp-button-color` CSS variable used in PWA nav hover and active states now includes a `#2271b1` fallback value — the variable is only injected on specific pages, causing the color to be undefined (and therefore invisible) on all other PWA pages.
* IMPROVED: Invoice list (pocketemp-payment-invoices) renders as a card layout on mobile and in PWA mode — table headers are hidden, each row becomes a standalone card with labeled fields (Datum, Gast, Event, Betrag, Status), and the invoice number is styled as a prominent card header.
* IMPROVED: Anmeldungen page in PWA mode — sidebar is hidden, event cards fill the full screen width, and the registration detail modal stops at the bottom navigation bar instead of extending behind it.
* IMPROVED: Tickets hub page (pocketemp-tickets) in PWA mode — max-width constraint removed so the content fills the full screen width.
* IMPROVED: Neue Anmeldung form on mobile and in PWA mode — form-table rows now stack vertically (label above input) for single-column layout. All text inputs, selects, and textareas are full-width with 16 px font size to prevent iOS auto-zoom.
* IMPROVED: Toggle rows (Firma/Gruppe, Bestätigungs-E-Mail, etc.) on mobile display as a flex row with the label on the left and the toggle switch on the right, matching a native mobile settings style. Redundant inline label text inside the toggle is hidden; the row header provides the label.
* FIXED: Toggle switch (pocketemp-toggle-track) on mobile is now always visible with explicit min-width, height, and ::after pseudo-element dimensions forced via !important to prevent theme or WordPress admin styles from collapsing the track.
* FIXED: PWA bottom navigation z-index raised to 100001 to ensure it stays above WordPress admin modals and overlays.

= 1.2.6 =
* IMPROVED: PDF invoice template completely rewritten for TCPDF compatibility — all styles are now inline attributes (`style="..."`), `bgcolor` is used directly on `<td>` elements instead of CSS `background-color` on `<div>`, eliminating empty grey boxes that appeared in rendered PDFs.
* FIXED: Grey background boxes on "Veranstaltung" and "Rechnungsdetails" sections removed from PDF invoices.
* IMPROVED: PDF invoice header — company name and address are now vertically aligned with the logo using line breaks for correct visual centering in TCPDF.
* IMPROVED: PDF invoice layout — added spacing between the header/logo area and the billing recipient section, and between the event detail block and the invoice items table.
* IMPROVED: "Rechnungsdetails" heading in PDF invoices is now bold and larger for better visual hierarchy.
* FIXED: Stray "?" character at top of PDF invoices caused by UTF-8 BOM — template file is now written without BOM.
* FIXED: Grey separator line between header and billing recipient section removed.

= 1.2.5 =
* IMPROVED: Custom Payment Methods moved out of the Ticket-Event box and into the right column of the Price & Capacity section, below the Global Discount Codes. The option is now always visible even when the Tickets add-on is inactive or disabled.
* FIXED: Translation string "Private (usable but not shown in frontend)" was missing from the POT file and German PO file. Both files now include the string with the German translation "Privat (verwendbar, aber nicht im Frontend angezeigt)".

= 1.2.4 =
* FIXED: Plugin update indicator not clearing after a WordPress update — after installing an update, WordPress re-ran the update check within the same PHP request using the old in-memory version, causing the stale API response ("update_available: true") to be re-cached. The updater now detects when the cached "latest version" is already <= the currently installed version, discards the stale transient immediately, and reports no pending update to WordPress.

= 1.2.3 =
* FIXED: Frontend modal overflow on mobile devices — modal now uses `100dvh` instead of `100vh` for correct viewport handling on iOS Safari and other mobile browsers. Changed overlay `overflow: hidden` to `overflow-y: auto` for proper scrollability when content exceeds viewport.
* FIXED: Mobile modal positioning — added `align-items: flex-start` and safe-area insets (`env(safe-area-inset-top/bottom)`) to prevent modals from being cut off on notched/rounded corner devices.
* FIXED: Event edit page redirect — after saving an event, users now remain on the edit page instead of being redirected to the events list. Redirect now correctly uses `pocketemp_aktion=bearbeiten&pocketemp_id={ID}` parameters.
* FIXED: Event edit save button — save button is now positioned outside the tab container and remains always visible, regardless of which tab is active. Removed `.pocketemp-bottom-save` wrapper and associated CSS styling.
* FIXED: Payment status updates now reflect immediately in the booking details modal without requiring a page refresh. Modal automatically re-renders when payment status is changed via AJAX, and the `data-booking` attribute on the table row is updated for consistency.
* IMPROVED: Payment status labels now consistently display in German — `Paid` shows as `Bezahlt` and `Pending` shows as `Ausstehend` in modal details, status badges, and action buttons throughout the invoices page.

= 1.2.2 =
* NEW: Expired ticket status — tickets with status `gueltig` whose event date lies in the past are now treated as "Abgelaufen" (expired) throughout the entire plugin. Expiry is computed at render time (no new database column needed) by comparing the event date with today.
* NEW: Ticket list (page=pocketemp-ticket-list): expired tickets show an ⏰ Abgelaufen badge (grey) in the card view instead of the green Gültig badge.
* NEW: Ticket list: date display on ticket cards — event date is now shown next to the event name in both solo and group card bodies (via JOIN on `anmeldungen` → `event_termine`).
* NEW: Ticket list: "Abgelaufen" filter tab in the tab bar with a live count badge. The tab is only shown when at least one expired ticket exists. Expired tickets are excluded from the "Alle" and "Gültig" counts so the numbers reflect only scannable tickets.
* NEW: Digital ticket view page (`?pocketemp_ticket=`): expired tickets now display a grey ⏰ Abgelaufen status badge instead of the green Gültig badge.
* NEW: QR code scanner: expired tickets are rejected at scan time — the scanner shows an ⏰ Abgelaufen result card (no "Mark as Used" button) and pauses in auto-scan mode, identical to the behaviour for invalid/tampered tickets.
* NEW: Frontend registration modal: past event dates are hidden from the date-chip selector — only upcoming dates are shown when a visitor opens the registration form.
* FIXED: Tooltip on ticket cancel button showed English "Cancel" — corrected to German "Stornieren".
* FIXED: Ticket view page status badges used literal `\u23f0` escape sequences in PHP single-quoted strings — replaced with the actual ⏰ ✅ ☑️ ❌ emoji characters.
* UPDATED: German translations (`.po` and `.l10n.php`) updated with new strings: `Expired` → `Abgelaufen`.

= 1.2.1 =
* NEW: Auto-cancelled event dates are no longer removed from the Registrations view. When a date is auto-cancelled (minimum participants not reached), it remains visible as a card with a red border (`pocketemp-event-card--auto-cancelled`) and a red "Auto-cancelled" badge next to the participant count pill, so admins can review affected registrations at a glance.
* NEW: Added `auto_cancelled` column (tinyint) to the `pocketemp_event_termine` database table (migration 2.7.0). Auto-cancellations set both `aktiv = 0` and `auto_cancelled = 1`, distinguishing them from manually deactivated dates.
* NEW: All three SQL queries in the Registrations list now use `WHERE (t.aktiv = 1 OR t.auto_cancelled = 1)` so auto-cancelled dates with existing registrations are always shown.
* IMPROVED: In the Event editor, auto-cancelled dates only show Duplicate and Delete buttons — the Edit button was already hidden for inactive dates. The plugin user must manually delete the date from the Events editor after reviewing registrations.
* IMPROVED: Renamed "Test auto-cancellation" to "Auto-cancellation" in Settings → Cancellations. The feature is no longer framed as a test tool but as a standard emergency cancellation action. Description updated accordingly.
* UPDATED: German translations (`.po` and `.l10n.php`) updated with new and renamed strings.

= 1.2.0 =
* NEW: HTML email builder for admin notifications — new-registration emails sent to the admin are now rendered with the same styled card layout as guest emails, using the plugin’s design colors (header background + text color) and the configured email logo.
* NEW: Email preview panel in the Email settings tab now hides automatically when switching to any other settings tab (Design, General, etc.) instead of remaining visible and overlapping other UI elements.
* NEW: Email settings tab — clicking an email card now opens a preview panel on the right side without shrinking the content area. The preview panel is positioned fixed to the right of the admin wrap, correctly offset for both normal and folded WordPress sidebars.
* IMPROVED: Admin email rows include custom form-editor fields after the standard booking data rows, in the order defined in the form editor.
* IMPROVED: Guest confirmation emails — custom fields from the form editor are inserted directly after the standard booking data lines instead of being appended after the sign-off.
* FIXED: Double-colon bug in custom field labels — labels ending with ":" no longer produce duplicate colons (e.g. “Allergene wählen:: Nüsse”) in emails.

= 1.1.9 =
* Fixed registration count in the Registrations overview (page=pocketemp-anmeldungen) — the header badge "X Anmeldung(en)" previously included Cancelled and Rejected registrations. It now counts only Pending and Confirmed registrations, matching the status logic used elsewhere in the plugin.
* Auto-cancellation system upgraded from WP-Cron best-effort to per-termin precise scheduling: when an event date is saved with a cancellation deadline, a dedicated `wp_schedule_single_event` is registered for 00:05 on that exact deadline date. This ensures cancellation emails are sent reliably on the deadline day regardless of visitor traffic.
* Added `pocketemp_auto_cancel_single_termin` cron hook and handler — fires at the precise deadline time for a specific date, triggering the auto-cancellation and email dispatch for that termin only.
* On plugin activation/update, all existing active termine with a future stornierungsdatum are automatically enrolled into the per-termin cron queue.
* Stale cron events are removed when a termin's deadline changes or when a termin is deleted.
* Reduced the throttled init-hook safety-net interval from 1 hour to 5 minutes — if the scheduled cron event is missed for any reason, the system recovers within 5 minutes of the next page visit.
* Cancelled Termine in the backend event editor now hide the Edit (pencil) button — only Duplicate and Delete remain available for auto-cancelled dates.
* Registrations overview: fixed SQL queries to exclude STORNIERT and ABGELEHNT from `anmeldungen_count` by replacing `a.storniert = 0` with `a.status IN ('AUSSTEHEND','BESTAETIGT')`.

= 1.1.8 =
* Ticket list (page=pocketemp-ticket-list): removed table view entirely — tickets are now always displayed as cards. The toggle button and desktop table block have been removed.
* Ticket list: replaced the old `<ul class="subsubsub">` filter tabs and floating search form with a unified filter bar — pill-style tabs (dark blue background, white active pill, count badges) on the left, search field and Search button on the right, all in a single row inside the ticket card container alongside the sidebar.
* Ticket list: all previously hardcoded German source strings replaced with English i18n strings: All, Valid, Used, Cancelled, Search tickets…, Events & Dates, All Tickets, %d Tickets (Group), No tickets found.
* Ticket list: fixed date and time formatting — replaced `get_option('date_format') . get_option('time_format')` calls with a new `pocketemp_user_date()` helper that uses `switch_to_locale( get_user_locale() )` so month names and date order reflect the active user language. The time format now correctly reads `get_option('time_format')`, honouring the 12 h/24 h setting configured in WordPress Settings → General instead of always appending AM/PM.
* Invoices (page=pocketemp-payment-invoices): replaced old outline-style filter buttons and icon search input with the same pill-tab filterbar style used in the ticket list (`.pocketemp-tkt-*` classes). Shared CSS added to `payment-invoices.css`.
* Added German translations to `.po` file for all new English source strings; updated `.pot` file accordingly.

= 1.1.7 =
* Moved email attachment settings (invoice / ticket) from the Email Templates tab to the Payment Settings page — attachments are now configured per payment method instead of globally per email template.
* Each payment method (Pay by Invoice, Stripe, and all custom payment methods) now has four individual checkboxes in its edit panel: "Invoice → Registration Email", "Invoice → Confirmation Email", "Ticket → Registration Email", "Ticket → Confirmation Email".
* Stripe: when the global "Auto-Accept" setting (General Settings) is enabled, the invoice is always attached to the confirmation email regardless of the Stripe-specific toggles — this existing behavior is preserved and shown with a notice banner in the Stripe edit panel.
* Custom payment methods: attachment settings are saved per method in the existing JSON store and are shown in both the edit panel and the "Add New Method" form.
* Added `pocketemp_get_pm_attach_setting()` helper function to resolve per-payment-method attachment preferences from WordPress options (system methods) or the custom methods JSON array.

= 1.1.6 =
* Replaced static PDF ticket attachments in emails with live digital ticket links — when the "Attach Ticket" toggle is enabled in email settings, confirmation emails now include a styled "🎫 Ticket anzeigen" button linking to the real-time digital ticket page (`/?pocketemp_ticket={token}`) instead of attaching a PDF file. The digital ticket shows live status (valid/used/cancelled), preventing fraud from reused static PDFs.
* Replaced "Download PDF" buttons in the admin ticket list and ticket detail modal with "Show Ticket" links that open the digital ticket view page in a new tab.
* Removed the "Download Ticket PDF" button from the public ticket view page — wallet buttons (Apple/Google) remain as the primary actions.
* Fixed ticket status badge colors on the digital ticket view page — the status map previously used uppercase umlauted keys (`GÜLTIG`) that never matched the lowercase database values (`gueltig`), causing all badges to fall through to the gray default. Status badges now display correctly: green for valid (✅ Gültig), red for used (☑️ Verwendet), dark red for cancelled (❌ Storniert).
* Digital ticket links are now included in all email flows: frontend registration, admin confirmation, Stripe payment webhook (auto-accept and non-auto-accept), manual admin registration, and group booking emails (both billing contact and individual participant emails).
* Added persistent Scan Archive to the Ticket Scanner — every scan is now written to a new database table (`pocketemp_scan_log`) so the history survives across sessions. The archive is displayed below the session history, grouped by day with a date header and scan count badge. Each entry shows time, ticket number, participant name, event, and status at the time of scan. New scans appear live at the top without a page reload. Up to 500 recent entries are loaded on page open.

= 1.1.5 =
* Implemented native wallet integration — the "Add to Wallet" button now directly opens the iOS native Wallet app (via Apple PKPass) or the Android Google Wallet app (via signed JWT redirect), instead of just opening the ticket view webpage.
* Added Apple Wallet PKPass generation — when Apple Wallet credentials are configured (Pass Type ID, Team ID, certificate PEM + private key PEM + WWDR PEM), a download of a signed `.pkpass` file is served at `?pocketemp_apple_pass={token}`. iOS automatically opens this in the native Wallet app.
* Added platform detection in the thank-you modal — on iOS the "Add to Wallet" button links directly to the PKPass download; on Android it triggers an AJAX request to generate a Google Wallet JWT and redirects to `pay.google.com/gp/v/save/{jwt}` which opens the native Google Wallet app. Desktop/fallback shows the ticket view page.
* Added Wallet settings UI in Payments → Wallet Integration — admin can now configure Apple Wallet (Pass Type ID, Team ID, certificate, private key, WWDR cert) and Google Wallet (Issuer ID, service account JSON) with inline setup instructions and download links.
* The ticket view page (`?pocketemp_ticket=`) now shows platform-aware wallet buttons — iOS users see "Add to Apple Wallet", Android users see "Add to Google Wallet" (both hidden on desktop to avoid confusion), with proper platform detection via JavaScript `navigator.userAgent`.
* Added `apple_pass_url` field to AJAX responses for Stripe and regular registration confirmation so the JS modal can link directly to the PKPass download without a second round-trip.
* Google Wallet handler now redirects with `window.location.href` (same window) instead of opening a new tab, which is required for the Android Google Wallet deeplink to work correctly.

= 1.1.4 =
* Added "Add to Wallet" feature — after a successful Stripe payment for a ticket event, a new "🎫 Add to Wallet" button appears in the booking confirmation modal. Clicking it opens a full-screen, mobile-optimized digital ticket page showing the event details, QR code, ticket number, and holder name.
* The ticket view page is accessible via a unique URL (`?pocketemp_ticket={token}`) and can be saved to the iOS/Android home screen for quick offline access.
* Added Google Wallet integration — when a Google Wallet issuer ID and service account JSON are configured in plugin settings, a "Save to Google Wallet" button is shown on the ticket page that generates a signed JWT pass and saves the event ticket directly to Google Wallet on Android.
* Ticket data (`ticket_nummer`, `qr_token`, `ticket_view_url`) is now included in the Stripe payment confirmation AJAX response so the frontend can conditionally display the wallet button without an extra round-trip.

= 1.1.3 =
* Fixed plugin updates not being available on fresh installs or installations without an activated license — the update checker now uses `pocketemp_has_full_access()` instead of `pocketemp_is_trial_period_active()`, covering valid license, active trial, and fresh installs (trial never started) alike. Only expired-trial / no-license installs are blocked from downloading updates.
* Fixed "Add to Calendar" dropdown not opening on some themes — added `!important` to the `display: none / display: flex` rules on `.pocketemp-ty-cal-options` so theme stylesheets can no longer override the show/hide toggle.

= 1.1.2 =
* Improved mobile list layout — replaced flex-wrap with a 2-column CSS grid (72 px badge column + fluid content column). Date badge and time now stack vertically in the left column; status badge and location pill are now visible on mobile in the right column below the event title.
* Added "Add to Calendar" dropdown on the booking confirmation overlay — clicking the button now opens a picker with three options: Google Calendar (opens in a new tab with event name, date, time and timezone pre-filled), Apple Calendar (downloads ICS, opens natively in Calendar on macOS/iOS), and Export as ICS (downloads an ICS file compatible with any calendar app).
* Admin notification email widened from 560 px to 640 px for improved readability.
* Fixed admin notification email losing rounded corners on the bottom edge — footer button row now has `border-radius: 0 0 10px 10px` matching the header.
* Removed `word-break: break-word` from admin notification email detail rows — eliminates 22 CSS compatibility warnings in email client checks. Long labels now wrap naturally via `table-layout: fixed` and the `width` HTML attribute.

= 1.1.1 =
* Fixed frontend category filter not working in list layout — CSS cascade issue caused `display: flex !important` on `.pocketemp-list-row` to override the `display: none !important` from `.pocketemp-filter-hidden` (equal specificity, later rule wins). Fixed by adding `[data-kategorie-id].pocketemp-filter-hidden` with higher specificity.
* Added automatic booking confirmation prompt when marking an invoice as paid — if the linked registration is still in "Pending" status, a dialog asks whether to also confirm the registration. Confirming sends the guest notification email (with invoice and/or ticket attached per configured email template settings).
* Added settings tabs redesign: pill-style navigation bar with dark blue background (`#1a4a8a`), white active pill, and Dashicons for each tab (General, Cancellations, Emails, Design, Form, Language, App).
* Fixed email preview panel remaining visible when switching away from the Emails settings tab.
* Fixed toggle controls inheriting tab label styles due to overly broad CSS selector — scoped to `.pocketemp-tabs-bar label` to prevent leakage.
* Fixed per-event custom payment method override not filtering correctly — custom payment methods were always rendered regardless of the per-event override selection. Added `pmList` check before rendering each custom method in the checkout.
* Fixed ticket type name not appearing in the registration popup modal on the registrations page — the AJAX query was fetching `ticket_typ_id` but not joining the ticket types table. Added `LEFT JOIN pocketemp_ticket_typen` to retrieve the ticket type name alongside each registration.

= 1.1.0 =
* Fixed QR code scanner not finding tickets when scanning — camera scan sent the full verification URL instead of the extracted token. Added client-side URL parsing (`parseQrCode()`) and a server-side fallback that extracts the token from verification URLs before database lookup.
* Fixed plugin ZIP packaging — archive now includes `event-manager-pro/` root folder so WordPress always extracts to the correct directory name instead of a random temporary folder.

= 1.0.9 =
* **Ticket Scanner** — Full camera-based QR code scanner for validating tickets at events. Uses the native BarcodeDetector API (Chrome/Edge/Android) with jsQR fallback for Safari and Firefox.
* Scanner supports front/rear camera toggle, animated scan overlay, haptic feedback (vibrate) and audible beep on successful scan.
* Scanned tickets are validated via AJAX against the database with integrity check (HMAC-SHA256 verification). Color-coded result cards: green (valid), yellow (already used), red (cancelled/tampered).
* One-click "Mark as Used" button to instantly mark a valid ticket as used directly from the scan result.
* Session-based scan history table showing all scans with time, ticket number, event name and status badge.
* Manual ticket lookup — enter a ticket number or QR token to check its validity without a camera.
* **PWA offline support** — Validated ticket data is cached in IndexedDB for offline lookup. Mark-as-used actions are queued in localStorage when offline and auto-flushed when the connection is restored.
* Service worker updated to v6 with pre-cache support for scanner assets on first visit.
* Fixed tile description translations not loading on Ticket System and Payment System hub pages — root cause was `&amp;` in `esc_html_e()` source strings not matching the `&` in PO msgid entries.
* All new scanner strings added to POT and German PO translation files (27 new strings).

= 1.0.8 =
* New Ticket System Addon — modeled after thWenn wir bei uns im Frontend eine Registrierung abschließen, habe ich die Option, es zum Kalender hinzuzufügen, was aber bei uns passiert ist, dass nur eine ics-Datei Zum Download zur Verfügung steht. Ich würde gerne, dass wenn man auf den Button klickt, sich drei weitere Buttons öffnen mit Google Kalender, Apple Kalender oder exportieren als ICS Datei.e Payments addon with its own license key, 14-day trial, and four admin pages (Tickets hub, Ticket Settings, Ticket Manager, Ticket List, Ticket Scanner).
* Ticket Settings page with global enable/disable toggle and ticket type CRUD management using the existing ticket_typen table.
* Ticket Manager page with configurable ticket number pattern (prefix, digit count, year inclusion, suffix), PDF branding settings (company name, logo, footer text), and a live ticket preview card.
* Ticket List page with filterable tabs (All, Valid, Used, Cancelled), search by ticket number/name/email, pagination, and status management actions (mark used, cancel, reactivate, download PDF).
* Ticket Scanner page with placeholder for future QR camera scanning and a working manual ticket lookup by number or QR token.
* Per-event "Ticket Event" toggle in the event editor sidebar — only shown when the ticket addon is active and enabled.
* PDF ticket generation via TCPDF with company header/logo, event details, participant info, centered QR code (QRCODE,H error correction), ticket number, and footer.
* Auto-generate tickets when a registration is confirmed (BESTAETIGT) — works from both the admin status change and Stripe auto-accept webhook flows.
* Frontend buttons show "Select Ticket" ("Ticket auswählen" in German) instead of "Register Now" for events marked as ticket events — applies to all layouts (cards, masonry, table, single event, and modal).
* New DB migration (1.9.0): added pocketemp_tickets table (ticket_nummer, qr_token, anmeldung_id, event_id, status, etc.) and ist_ticket_event column on events table.

= 1.0.7 =
* Comprehensive mobile CSS overhaul for the Dashboard page (non-PWA) — grid, cards, stats, upcoming events, recent registrations, card footer buttons and shortcode sections now all render correctly on screens ≤ 782 px, matching the quality of the PWA view.
* Dashboard stats grid collapses to 2 columns on mobile with the revenue stat spanning full width; stat numbers scaled down from 48 px to 26 px for better readability.
* Card footer buttons become full-width and themed with the plugin button colour on mobile.
* Category-specific shortcode rows now stack vertically on mobile (category badge → shortcode code → copy button) with badge and button centred; copy button is compact (26 px height) to reduce visual weight.
* Added mobile breakpoint to Statistics page — chart height reduced and admin wrap constrained to full width on small screens.
* Added mobile breakpoint to Participants page — search input becomes full-width and the participant card grid collapses to a single column on small screens.

= 1.0.6 =
* Registrations modal on the Anmeldungen page now switches to a mobile card layout on screens ≤ 782 px and in PWA mode, matching the card view already used in the calendar — includes booking type, payment badge, group members, and confirm/reject action buttons per card.
* Card layout added to the calendar's participants modal as well, with dispatcher logic that selects card or table view based on screen width and PWA mode.
* Confirm, reject, and delete quick-actions in the participants modal now work correctly for both card and table layouts — status badge on the card updates live, card fades out on delete, no page reload required.
* Applied plugin theme colours (--pocketemp-container-bg, --pocketemp-text-color, --pocketemp-button-color) to the participants modal header, modal title, close button, table header, and mobile card header.
* Event date cards on the Anmeldungen page now show a yellow border (#ffe79b) when there are pending (unconfirmed) registrations for that date; the border is applied server-side on page load and updated live via JS after confirming or rejecting registrations from the modal.

= 1.0.5 =
* Fixed "headers already sent" warning on plugin activation when the server has output_buffering disabled — moved the setup wizard auto-redirect from the page render callback to an early admin_init hook so it runs before any HTML output.
* Added per-event payment method override — a new global toggle in Payment Settings ("Per-Event Payment Override") allows configuring which payment methods are available for each event individually.
* When per-event override is enabled, events show a new "Custom Payment Methods" toggle in the Price & Capacity section of the event editor, listing all globally active payment methods (Invoice, Stripe, and custom methods) as checkboxes.
* Frontend checkout now respects per-event payment method overrides — only the selected methods are displayed to guests during registration.
* Backend registration handler validates that the submitted payment method is allowed for the event, preventing bypasses.
* New DB migration (1.8.0): added `zahlungsmethoden_override` column to the events table for storing per-event payment method selections as JSON.

= 1.0.4 =
* Fixed date picker in the manual registration form ("New Registration" admin page) — custom date fields of type "Date / Birthday" now use the jQuery UI datepicker widget instead of the native browser date input, matching the style of all other datepickers in the plugin.
* Custom field type is now stored alongside the field value when saving manual registrations, so the registration detail view correctly formats date fields as DD.MM.YYYY (or locale equivalent) instead of the raw database format YYYY-MM-DD.
* Fixed participant date fields in the frontend company/group registration form — date fields dynamically rendered for each participant row now use the jQuery UI datepicker widget instead of the native browser date input.
* Fixed date fields added as participant fields in the form builder (backend settings) not using the custom datepicker style in the frontend company/group section.
* Added phone country code prefix selector to frontend registration forms — phone number fields now include a flag-based country code dropdown with auto-detection, matching the booking calendar plugin.
* Added field type selection modal to the form builder — clicking "Add Field" now opens a modal with visual type cards (Text, Text Area, Phone, Email, Date/Birthday, Number, Dropdown, Checkbox, URL) instead of immediately adding a blank text field.
* Replaced native HTML5 date inputs throughout the frontend registration form with jQuery UI datepicker widgets (text + hidden input pair) for a consistent cross-browser calendar experience.
* Fixed date values in registration detail and email outputs displaying in raw YYYY-MM-DD database format — dates are now formatted according to the active locale (DD.MM.YYYY for EU, MM/DD/YYYY for US).
* Fixed month and year select dropdowns in backend datepickers being too narrow to display full month names — applied flex layout with minimum widths matching the frontend.
* Fixed month name abbreviations in datepicker header dropdowns — all month selects now display full month names (e.g. "März" instead of "Mär") in both frontend and backend.

= 1.0.3 =
* Fixed setup wizard "Skip" button not working correctly — skipping now sets the wizard as completed and prevents the dashboard from looping back to the wizard.
* Fixed garbled replacement character displayed in the version check area of the settings page.
* Added backend setting to show multi-day events with only the start date tile in the frontend overview (full start→end date pair always shown in the detail view).
* Fixed dark filled background on active multi-day date chips in the event detail modal — active chip pairs now render as a single unified rounded tile with white text.
* Plugin updates can now be installed during the 14-day trial period without requiring a license key.
* Fixed registration search not returning results due to incorrect database column names (vorname/nachname used instead of the actual name column).
* Fixed layout order in the Registrations admin page — "New Registration" button is now consistently on the left, search field on the right.
* Fixed German month names displayed in English (e.g. "March" instead of "März") on all frontend and backend date outputs.
* Fixed German day names shown in English throughout the datepicker and calendar views.
* Fixed 12-hour time format (AM/PM) incorrectly applying to non-English languages when switching from US locale — 12h format is now exclusive to the US (en_US) language setting and is automatically reset to 24h when switching to any other language.
* Manual registration form redesigned: event and date selection moved to the top of the form so availability is visible before filling in contact details.
* Date dropdown in manual registration now shows remaining capacity per date (e.g. "3 available") and marks fully booked dates as "Sold out" in red/italic.
* Added invoice/payment field to the manual registration form — when the Payments addon is active, admins can choose "No Invoice", "Invoice – Pending", or "Invoice – Paid" and the invoice number is generated automatically.
* Fixed custom form field names displayed as cryptic internal keys (e.g. custom_1234) in registration details — readable field labels are now saved alongside field values.
* Email header logo size doubled from 50×120 px to 100×240 px for better visibility in HTML emails.
* Highlights label inline-edit: input field and Save/Cancel buttons now each appear on their own line for better readability.

= 1.0.2 =
* Setup wizard now works correctly after plugin activation and manual reset.
* Added self-service cancellation (Stornierung) support for event registrations.
* Fixed month name on dashboard showing in English instead of the active locale.
* Fixed various legacy slug references in admin settings and update checker.

= 1.0.1 =
* Inline CSS extraction and optimization across all admin pages.
* Added dedicated CSS files for Participants and improved code organization.
* Various UI polish and minor improvements.

= 1.0.0 =
* Initial release of PocketEvents Pro.

== Upgrade Notice ==

= 1.7.2 =
More robust license check: the license stays active during license server problems.

= 1.1.9 =
Fixes registration counts in the Registrations overview (Cancelled/Rejected no longer counted as active). Upgrades auto-cancellation to per-termin precise scheduling so deadline emails are sent reliably on the exact deadline day. Recommended update for all users.

= 1.1.1 =
Fixes category filter in list layout not working. Adds automatic booking confirmation dialog when marking an invoice as paid. Settings tabs redesigned with pill-style navigation and icons. Recommended update for all users.

= 1.1.0 =
Fixes QR code scanner failing to find tickets scanned via camera. Fixes ZIP packaging so the plugin installs to the correct directory. Recommended update for all users using the Ticket addon.

= 1.0.9 =
Adds full camera-based ticket QR code scanner with instant validation, mark-as-used, scan history, and offline PWA support. Fixes tile description translations on Tickets and Payments hub pages. Recommended update for all users using the Ticket addon.

= 1.0.4 =
Fixes datepicker style in manual registration form, participant date fields in company/group frontend forms, and date display formatting in registration detail view. Adds phone country code selector and field type modal to the form builder. Recommended update for all users.

= 1.0.3 =
Fixes wizard skip, multi-day event display, modal chip styling, trial-period updates, registration search, and several localization bugs (German month/day names, time format reset on language switch). Adds availability info and invoice field to manual registration form, fixes custom field label display, doubles email header logo size, and improves the highlights label editor layout. Recommended update for all users.

= 1.0.2 =
Fixes setup wizard, adds cancellation support, and corrects localization issues. Recommended update for all users.

= 1.0.1 =
Performance and code quality improvements. Recommended update for all users.

== Support ==

For support, questions, or feedback, please visit https://mrstudio.codes

== Credits ==

Developed by Maxim Ristow / maksim85 for MRStudio.