<?php
/**
 * Admin dashboard and settings.
 *
 * @package Vachr Short-Term Rental Reservations
 * @license GPLv2 or later
 */
if (!defined('ABSPATH')) exit;

class VSTRB_Admin {
	// Custom plugin tables are queried directly throughout this admin class.
	// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value, PluginCheck.Security.DirectDB.UnescapedDBParameter
	private static $invoice_translation_locale = '';
	private static $invoice_translation_maps = [];

	private static function add_inline_admin_script($script) {
		if (!is_string($script) || trim($script) === '') {
			return;
		}
		wp_enqueue_script('vstrb-admin');
		$script = preg_replace('#</?script[^>]*>#i', '', $script);
		wp_add_inline_script('vstrb-admin', trim((string) $script));
	}

	private static function get_param_int($key, $default = 0) {
		$value = filter_input(INPUT_GET, $key, FILTER_SANITIZE_NUMBER_INT);
		if (null === $value || false === $value || '' === $value) {
			return (int) $default;
		}
		if (!preg_match('/^-?\d+$/', (string) $value)) {
			return (int) $default;
		}
		return (int) $value;
	}

	private static function get_param_text($key, $default = '') {
		$value = filter_input(INPUT_GET, $key, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		if (null === $value || false === $value) {
			return (string) $default;
		}
		return sanitize_text_field((string) $value);
	}

	private static function request_param_int($key, $default = 0) {
		$value = filter_input(INPUT_POST, $key, FILTER_SANITIZE_NUMBER_INT);
		if (null === $value || false === $value || '' === $value) {
			$value = filter_input(INPUT_GET, $key, FILTER_SANITIZE_NUMBER_INT);
		}
		if (null === $value || false === $value || '' === $value) {
			return (int) $default;
		}
		if (!preg_match('/^-?\d+$/', (string) $value)) {
			return (int) $default;
		}
		return (int) $value;
	}

	private static function post_array($key) {
		$value = filter_input(INPUT_POST, $key, FILTER_SANITIZE_FULL_SPECIAL_CHARS, ['flags' => FILTER_REQUIRE_ARRAY]);
		if (!is_array($value)) {
			return array();
		}
		return (array) $value;
	}

	private static function post_checkbox_enabled($key) {
		$value = filter_input(INPUT_POST, $key, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		if (null === $value || false === $value) {
			$array_value = filter_input(INPUT_POST, $key, FILTER_SANITIZE_FULL_SPECIAL_CHARS, ['flags' => FILTER_REQUIRE_ARRAY]);
			return is_array($array_value) ? 1 : 0;
		}
		$val = strtolower(trim((string) sanitize_text_field((string) $value)));
		return in_array($val, ['1', 'on', 'yes', 'true'], true) ? 1 : 0;
	}

	private static function parse_decimal_input($raw, $default = null) {
		if (is_array($raw)) {
			return $default;
		}
		$val = trim((string) $raw);
		if ($val === '') {
			return $default;
		}
		$val = str_replace(' ', '', $val);
		$val = str_replace(',', '.', $val);
		if (!is_numeric($val)) {
			return $default;
		}
		return (float) $val;
	}

	private static function sanitize_invoice_prefix($prefix) {
		$prefix = sanitize_text_field((string) $prefix);
		$prefix = preg_replace('/[^A-Za-z0-9_\-\/\.]/', '', $prefix);
		$prefix = trim((string) $prefix);
		if ($prefix === '') {
			return 'INV-';
		}
		return substr($prefix, 0, 32);
	}

	private static function sanitize_invoice_number_format($format) {
		$format = sanitize_text_field((string) $format);
		$format = preg_replace('/[^A-Za-z0-9_\-\/\.\{\}]/', '', $format);
		$format = trim((string) $format);
		if ($format === '') {
			return '{yyyy}{id}';
		}
		return substr($format, 0, 120);
	}

	private static function po_unquote($value) {
		$value = trim((string) $value);
		if (strlen($value) >= 2 && $value[0] === '"' && substr($value, -1) === '"') {
			$value = substr($value, 1, -1);
		}
		return stripcslashes($value);
	}

	private static function get_invoice_translation_map($locale) {
		$locale = sanitize_text_field((string) $locale);
		if ($locale === '') {
			return [];
		}
		if (isset(self::$invoice_translation_maps[$locale])) {
			return self::$invoice_translation_maps[$locale];
		}

		$domain = 'vachr-short-term-rental-reservations';
		$po_file = VSTRB_PLUGIN_DIR . 'languages/' . $domain . '-' . $locale . '.po';
		if (!is_readable($po_file)) {
			self::$invoice_translation_maps[$locale] = [];
			return [];
		}

		$map = [];
		$lines = file($po_file, FILE_IGNORE_NEW_LINES);
		$entry = null;
		$current = null;
		$finish_entry = static function() use (&$entry, &$map) {
			if (!is_array($entry) || !isset($entry['msgid'])) {
				$entry = null;
				return;
			}
			$msgid = (string) ($entry['msgid'] ?? '');
			$msgstr = (string) ($entry['msgstr'] ?? '');
			if ($msgid !== '' && $msgstr !== '') {
				$map[$msgid] = $msgstr;
			}
			$entry = null;
		};

		foreach ((array) $lines as $line) {
			$line = trim((string) $line);
			if ($line === '') {
				$finish_entry();
				$current = null;
				continue;
			}
			if ($line[0] === '#') {
				continue;
			}
			if (preg_match('/^msgid\s+(".*")$/', $line, $m)) {
				if (is_array($entry) && array_key_exists('msgstr', $entry)) {
					$finish_entry();
				}
				$entry = ['msgid' => self::po_unquote($m[1])];
				$current = 'msgid';
			} elseif (preg_match('/^msgstr\s+(".*")$/', $line, $m)) {
				if (!is_array($entry)) {
					$entry = [];
				}
				$entry['msgstr'] = self::po_unquote($m[1]);
				$current = 'msgstr';
			} elseif (preg_match('/^(".*")$/', $line, $m) && is_array($entry) && $current !== null) {
				$entry[$current] = (string) ($entry[$current] ?? '') . self::po_unquote($m[1]);
			}
		}
		$finish_entry();

		self::$invoice_translation_maps[$locale] = $map;
		return $map;
	}

	private static function invoice_text($msgid) {
		$msgid = (string) $msgid;
		$locale = (string) self::$invoice_translation_locale;
		if ($locale !== '') {
			$map = self::get_invoice_translation_map($locale);
			if (isset($map[$msgid]) && $map[$msgid] !== '') {
				return (string) $map[$msgid];
			}
		}
		return $msgid;
	}

	private static function sanitize_invoice_supplier_country_code($country_code) {
		$country_code = strtoupper(sanitize_text_field((string) $country_code));
		$country_code = preg_replace('/[^A-Z0-9_\-]/', '', $country_code);
		return substr((string) $country_code, 0, 12);
	}

	private static function get_invoice_supplier_details() {
		$country_code = self::sanitize_invoice_supplier_country_code((string) get_option('vstrb_invoice_supplier_country', ''));
		$defaults = [
			'country' => $country_code,
			'name' => wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES),
			'company_id_label' => self::invoice_text('Business / Tax ID'),
			'vat_id_label' => self::invoice_text('VAT / Sales Tax ID'),
			'country_name' => '',
		];

		$company_id_label = sanitize_text_field((string) get_option('vstrb_invoice_supplier_company_id_label', $defaults['company_id_label']));
		if ($company_id_label === '') {
			$company_id_label = $defaults['company_id_label'];
		}
		$vat_id_label = sanitize_text_field((string) get_option('vstrb_invoice_supplier_vat_id_label', $defaults['vat_id_label']));
		if ($vat_id_label === '') {
			$vat_id_label = $defaults['vat_id_label'];
		}

		return [
			'country' => $country_code,
			'name' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_name', $defaults['name'])),
			'company_id_label' => $company_id_label,
			'company_id' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_company_id', '')),
			'vat_id_label' => $vat_id_label,
			'vat_id' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_vat_id', '')),
			'street' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_street', '')),
			'city' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_city', '')),
			'zip' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_zip', '')),
			'country_name' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_country_name', $defaults['country_name'])),
			'bank_account' => sanitize_text_field((string) get_option('vstrb_invoice_supplier_bank_account', '')),
			'tax_note' => sanitize_textarea_field((string) get_option('vstrb_invoice_supplier_tax_note', '')),
		];
	}

	private static function render_invoice_party_block($title, array $party) {
		echo '<strong>' . esc_html($title) . '</strong><br>';
		if (!empty($party['name'])) {
			echo esc_html((string) $party['name']) . '<br>';
		}
		if (!empty($party['company_id'])) {
			echo esc_html((string) ($party['company_id_label'] ?? self::invoice_text('Company ID'))) . ': ' . esc_html((string) $party['company_id']) . '<br>';
		}
		if (!empty($party['vat_id'])) {
			echo esc_html((string) ($party['vat_id_label'] ?? self::invoice_text('VAT ID'))) . ': ' . esc_html((string) $party['vat_id']) . '<br>';
		}
		if (!empty($party['street'])) {
			echo esc_html((string) $party['street']) . '<br>';
		}
		$zip_city = trim((string) ($party['zip'] ?? '') . ' ' . (string) ($party['city'] ?? ''));
		if ($zip_city !== '') {
			echo esc_html($zip_city) . '<br>';
		}
		if (!empty($party['country_name'])) {
			echo esc_html((string) $party['country_name']) . '<br>';
		}
		if (!empty($party['bank_account'])) {
			echo esc_html__('IBAN', 'vachr-short-term-rental-reservations') . ': ' . esc_html((string) $party['bank_account']) . '<br>';
		}
	}

	private static function build_guest_invoice_number($rep) {
		$prefix = self::sanitize_invoice_prefix((string) get_option('vstrb_invoice_prefix', 'INV-'));
		$format = self::sanitize_invoice_number_format((string) get_option('vstrb_invoice_number_format', '{yyyy}{id}'));
		$ts = current_time('timestamp');

		$invoice_no = strtr($format, [
			'{prefix}'    => $prefix,
			'{id}'        => (string) (int) ($rep->id ?? 0),
			'{group_id}'  => (string) (int) ($rep->id ?? 0),
			'{yyyy}'      => date_i18n('Y', $ts),
			'{yy}'        => date_i18n('y', $ts),
			'{mm}'        => date_i18n('m', $ts),
			'{dd}'        => date_i18n('d', $ts),
			'{date}'      => date_i18n('Ymd', $ts),
			'{date_dash}' => date_i18n('Y-m-d', $ts),
		]);
		$invoice_no = trim((string) $invoice_no);

		if ($invoice_no === '') {
			$invoice_no = gmdate('Y') . (int) ($rep->id ?? 0);
		}
		return $invoice_no;
	}

	private static function build_booking_invoice_number($booking_id) {
		$prefix = self::sanitize_invoice_prefix((string) get_option('vstrb_invoice_prefix', 'INV-'));
		$format = self::sanitize_invoice_number_format((string) get_option('vstrb_invoice_number_format', '{yyyy}{id}'));
		$id = (int) $booking_id;
		$ts = current_time('timestamp');

		$invoice_no = strtr($format, [
			'{prefix}'    => $prefix,
			'{id}'        => (string) $id,
			'{group_id}'  => (string) $id,
			'{yyyy}'      => date_i18n('Y', $ts),
			'{yy}'        => date_i18n('y', $ts),
			'{mm}'        => date_i18n('m', $ts),
			'{dd}'        => date_i18n('d', $ts),
			'{date}'      => date_i18n('Ymd', $ts),
			'{date_dash}' => date_i18n('Y-m-d', $ts),
		]);
		$invoice_no = trim((string) $invoice_no);

		if ($invoice_no === '') {
			$invoice_no = gmdate('Y') . $id;
		}
		return $invoice_no;
	}

	private static function booking_mark_paid($booking_id, $provider = 'manual') {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$booking_id = (int) $booking_id;
		if ($booking_id <= 0) {
			return false;
		}
		$provider = sanitize_text_field((string) $provider);
		if ($provider === '') {
			$provider = 'manual';
		}

		$booking = $wpdb->get_row($wpdb->prepare("SELECT id, total_price, currency FROM {$t['bookings']} WHERE id=%d", $booking_id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		if (!$booking) {
			return false;
		}
		$amount = isset($booking['total_price']) ? (float) $booking['total_price'] : 0.0;
		$currency = strtoupper((string) ($booking['currency'] ?? 'EUR'));
		if (!preg_match('/^[A-Z]{3}$/', $currency)) {
			$currency = 'EUR';
		}
		$paid_at = current_time('mysql');

		if (!empty($t['invoices'])) {
			$wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				"UPDATE {$t['invoices']} SET status='paid', paid_at=%s WHERE booking_id=%d",
				$paid_at,
				$booking_id
			));
		}

		if (!empty($t['payments'])) {
			$payment_exists = (int) $wpdb->get_var($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				"SELECT id FROM {$t['payments']} WHERE booking_id=%d ORDER BY id DESC LIMIT 1",
				$booking_id
			));
			if ($payment_exists > 0) {
				$wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
					"UPDATE {$t['payments']}
					 SET status='paid', provider=%s, amount=%s, currency=%s, paid_at=%s
					 WHERE id=%d",
					$provider,
					number_format($amount, 2, '.', ''),
					$currency,
					$paid_at,
					$payment_exists
				));
			} else {
				$wpdb->insert($t['payments'], [ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
					'booking_id' => $booking_id,
					'invoice_id' => null,
					'method' => 'manual',
					'provider' => $provider,
					'status' => 'paid',
					'amount' => number_format($amount, 2, '.', ''),
					'currency' => $currency,
					'provider_reference' => '',
					'gateway_session_id' => '',
					'paid_at' => $paid_at,
				]);
			}
		}

		$autoconfirm = (int) get_option('vstrb_payment_autoconfirm_on_paid', 1);
		if ($autoconfirm === 1) {
			$wpdb->update($t['bookings'], ['status' => 'confirmed'], ['id' => $booking_id], ['%s'], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		}

		$meta_rows = [
			'payment_status' => 'paid',
			'payment_paid_at' => $paid_at,
			'payment_provider' => $provider,
			'payment_amount' => number_format($amount, 2, '.', ''),
			'payment_currency' => $currency,
			'invoice_status' => 'paid',
			'invoice_paid_at' => $paid_at,
		];
		foreach ($meta_rows as $meta_key => $meta_value) {
			$existing_meta = (int) $wpdb->get_var($wpdb->prepare(
				"SELECT id FROM {$t['booking_meta']} WHERE booking_id=%d AND meta_key=%s ORDER BY id DESC LIMIT 1",
				$booking_id,
				$meta_key
			));
			if ($existing_meta > 0) {
				$wpdb->update($t['booking_meta'], ['meta_value' => $meta_value], ['id' => $existing_meta], ['%s'], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			} else {
				$wpdb->insert($t['booking_meta'], ['booking_id' => $booking_id, 'meta_key' => $meta_key, 'meta_value' => $meta_value], ['%d', '%s', '%s']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			}
		}

		return true;
	}

	private static function booking_meta_upsert($booking_id, $meta_key, $meta_value) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$booking_id = (int) $booking_id;
		$meta_key = sanitize_text_field((string) $meta_key);
		if ($booking_id <= 0 || $meta_key === '') {
			return false;
		}

		$existing_meta = (int) $wpdb->get_var($wpdb->prepare(
			"SELECT id FROM {$t['booking_meta']} WHERE booking_id=%d AND meta_key=%s ORDER BY id DESC LIMIT 1",
			$booking_id,
			$meta_key
		));
		if ($existing_meta > 0) {
			return false !== $wpdb->update($t['booking_meta'], ['meta_value' => (string) $meta_value], ['id' => $existing_meta], ['%s'], ['%d']);
		}
		return false !== $wpdb->insert($t['booking_meta'], ['booking_id' => $booking_id, 'meta_key' => $meta_key, 'meta_value' => (string) $meta_value], ['%d', '%s', '%s']);
	}

	private static function booking_meta_value($booking_id, $meta_key, $default = '') {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$booking_id = (int) $booking_id;
		$meta_key = sanitize_text_field((string) $meta_key);
		if ($booking_id <= 0 || $meta_key === '') {
			return (string) $default;
		}
		$value = $wpdb->get_var($wpdb->prepare(
			"SELECT meta_value FROM {$t['booking_meta']} WHERE booking_id=%d AND meta_key=%s ORDER BY id DESC LIMIT 1",
			$booking_id,
			$meta_key
		));
		return null === $value ? (string) $default : (string) $value;
	}

	private static function ensure_offer_invoice_number($booking_id, $total, $currency, $payment_amount_due = null) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$booking_id = (int) $booking_id;
		if ($booking_id <= 0) {
			return '';
		}
		$currency = strtoupper((string) $currency);
		if (!preg_match('/^[A-Z]{3}$/', $currency)) {
			$currency = 'EUR';
		}
		$total = max(0, (float) $total);
		$payment_amount_due = ($payment_amount_due === null) ? $total : max(0, (float) $payment_amount_due);
		$invoice_number = self::booking_meta_value($booking_id, 'invoice_number', '');
		if ($invoice_number === '') {
			$invoice_number = self::build_booking_invoice_number($booking_id);
		}
		self::booking_meta_upsert($booking_id, 'invoice_number', $invoice_number);
		self::booking_meta_upsert($booking_id, 'invoice_status', 'issued');
		self::booking_meta_upsert($booking_id, 'invoice_issued_at', current_time('mysql'));
		self::booking_meta_upsert($booking_id, 'invoice_total', number_format($total, 2, '.', ''));
		self::booking_meta_upsert($booking_id, 'invoice_currency', $currency);
		self::booking_meta_upsert($booking_id, 'payment_amount_due', number_format($payment_amount_due, 2, '.', ''));
		self::booking_meta_upsert($booking_id, 'payment_currency', $currency);

		if (!empty($t['invoices'])) {
			$invoice_id = (int) $wpdb->get_var($wpdb->prepare(
				"SELECT id FROM {$t['invoices']} WHERE booking_id=%d ORDER BY id DESC LIMIT 1",
				$booking_id
			)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$data = [
				'booking_id' => $booking_id,
				'invoice_number' => $invoice_number,
				'status' => 'issued',
				'total_amount' => number_format($total, 2, '.', ''),
				'currency' => $currency,
			];
			if ($invoice_id > 0) {
				$wpdb->update($t['invoices'], $data, ['id' => $invoice_id], ['%d', '%s', '%s', '%s', '%s'], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			} else {
				$data['invoice_language'] = self::sanitize_invoice_language((string) get_option('vstrb_invoice_language', ''));
				$wpdb->insert($t['invoices'], $data, ['%d', '%s', '%s', '%s', '%s', '%s']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			}
		}

		return $invoice_number;
	}

	public static function issue_invoice_for_booking($booking_id) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$booking_id = (int) $booking_id;
		if ($booking_id <= 0) {
			return '';
		}

		$booking = $wpdb->get_row($wpdb->prepare(
			"SELECT id, total_price, currency FROM {$t['bookings']} WHERE id=%d",
			$booking_id
		), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		if (!$booking) {
			return '';
		}

		$total = isset($booking['total_price']) ? (float) $booking['total_price'] : 0.0;
		$currency = strtoupper((string) ($booking['currency'] ?? 'EUR'));
		return self::ensure_offer_invoice_number($booking_id, $total, $currency);
	}

	private static function booking_service_ids($booking_id) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$booking_id = (int) $booking_id;
		if ($booking_id <= 0) {
			return [];
		}
		return array_map('intval', (array) $wpdb->get_col($wpdb->prepare(
			"SELECT service_id FROM {$t['booking_services']} WHERE booking_id=%d ORDER BY service_id ASC",
			$booking_id
		)));
	}

	private static function unit_service_options($unit_id, $currency = 'EUR') {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$unit_id = (int) $unit_id;
		$currency = strtoupper((string) $currency);
		if ($unit_id <= 0 || !preg_match('/^[A-Z]{3}$/', $currency)) {
			return [];
		}
		$rows = $wpdb->get_results($wpdb->prepare(
			"SELECT s.id, s.name, s.pricing_type AS global_pricing_type, s.price AS global_price,
					us.is_optional, us.price_override, us.pricing_type AS unit_pricing_type, us.currency
			 FROM {$t['services']} s
			 INNER JOIN {$t['unit_services']} us ON us.service_id=s.id
			 WHERE us.unit_id=%d AND s.is_active=1
			 ORDER BY s.sort_order ASC, s.name ASC, (us.currency=%s) DESC, (us.currency='EUR') DESC",
			$unit_id,
			$currency
		), ARRAY_A);
		$options = [];
		foreach ((array) $rows as $row) {
			$id = (int) ($row['id'] ?? 0);
			if ($id <= 0 || isset($options[$id])) {
				continue;
			}
			$options[$id] = [
				'id' => $id,
				'name' => (string) ($row['name'] ?? ''),
				'is_optional' => (int) ($row['is_optional'] ?? 1),
				'pricing_type' => (string) (($row['unit_pricing_type'] ?? '') ?: ($row['global_pricing_type'] ?? '')),
				'price' => ($row['currency'] === $currency && $row['price_override'] !== null) ? (float) $row['price_override'] : (float) ($row['global_price'] ?? 0),
			];
		}
		return array_values($options);
	}

	private static function apply_manual_discount_to_snapshot(array $snapshot, $discount_type, $discount_value) {
		$discount_type = in_array($discount_type, ['none', 'amount', 'percent'], true) ? $discount_type : 'none';
		$discount_value = max(0, (float) $discount_value);
		$subtotal = max(0, (float) ($snapshot['total'] ?? 0));
		$manual_discount = 0.0;

		if ($discount_type === 'amount') {
			$manual_discount = min($subtotal, $discount_value);
		} elseif ($discount_type === 'percent') {
			$manual_discount = min($subtotal, round($subtotal * min(100, $discount_value) / 100, 2));
		}

		$snapshot['manual_discount_type'] = $discount_type;
		$snapshot['manual_discount_value'] = round($discount_value, 2);
		$snapshot['manual_discount_total'] = round($manual_discount, 2);
		$snapshot['total_before_manual_discount'] = round($subtotal, 2);
		$snapshot['total'] = round(max(0, $subtotal - $manual_discount), 2);
		$snapshot['total_with_deposit'] = round((float) $snapshot['total'] + max(0, (float) ($snapshot['refundable_deposit'] ?? 0)), 2);
		if ($manual_discount > 0) {
			if (!isset($snapshot['discount_lines']) || !is_array($snapshot['discount_lines'])) {
				$snapshot['discount_lines'] = [];
			}
			$snapshot['discount_lines'][] = [
				'label' => __('Manual discount', 'vachr-short-term-rental-reservations'),
				'amount' => round($manual_discount, 2),
			];
			$snapshot['discount_total'] = round((float) ($snapshot['discount_total'] ?? 0) + $manual_discount, 2);
		}

		return $snapshot;
	}

	private static function refresh_booking_price_offer($booking, $discount_type, $discount_value, $note = '', $services = null, $include_payment_details = false) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$booking_id = (int) ($booking['id'] ?? 0);
		$currency = strtoupper((string) ($booking['currency'] ?? 'EUR'));
		if (!preg_match('/^[A-Z]{3}$/', $currency)) {
			$currency = 'EUR';
		}
		$services = is_array($services) ? array_values(array_filter(array_map('intval', $services))) : self::booking_service_ids($booking_id);
		$locale = self::booking_meta_value($booking_id, 'customer_locale', '');
		$snapshot = VSTRB_REST::calculate_price_snapshot(
			(int) ($booking['unit_id'] ?? 0),
			(string) ($booking['date_from'] ?? ''),
			(string) ($booking['date_to'] ?? ''),
			(int) ($booking['guests'] ?? 1),
			$currency,
			$services,
			$locale
		);
		if (!is_array($snapshot)) {
			return false;
		}
		$snapshot = self::apply_manual_discount_to_snapshot($snapshot, $discount_type, $discount_value);

		$updated = $wpdb->update(
			$t['bookings'],
			[
				'total_price' => (float) $snapshot['total'],
				'currency' => $currency,
				'guests' => max(1, (int) ($booking['guests'] ?? 1)),
				'discount_type' => (string) $snapshot['manual_discount_type'],
				'discount_value' => (float) $snapshot['manual_discount_value'],
			],
			['id' => $booking_id],
			['%f', '%s', '%d', '%s', '%f'],
			['%d']
		);
		if (false === $updated) {
			return false;
		}
		$wpdb->delete($t['booking_services'], ['booking_id' => $booking_id], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		foreach ((array) ($snapshot['services'] ?? []) as $service_line) {
			if (!is_array($service_line) || empty($service_line['id'])) {
				continue;
			}
			$wpdb->insert($t['booking_services'], [ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
				'booking_id' => $booking_id,
				'service_id' => (int) $service_line['id'],
				'qty' => (float) ($service_line['qty'] ?? 1),
				'price' => (float) ($service_line['unit_price'] ?? 0),
			], ['%d', '%d', '%f', '%f']);
		}
		self::booking_meta_upsert($booking_id, 'price_breakdown', wp_json_encode($snapshot));
		self::booking_meta_upsert($booking_id, 'refundable_deposit', number_format((float) ($snapshot['refundable_deposit'] ?? 0), 2, '.', ''));
		self::booking_meta_upsert($booking_id, 'total_with_deposit', number_format((float) ($snapshot['total_with_deposit'] ?? $snapshot['total']), 2, '.', ''));
		self::booking_meta_upsert($booking_id, 'price_offer_include_payment_details', $include_payment_details ? '1' : '0');
		if ($include_payment_details) {
			$payment_amount_due = ((int) get_option('vstrb_payment_include_refundable_deposit', 0) === 1)
				? (float) ($snapshot['total_with_deposit'] ?? $snapshot['total'])
				: (float) ($snapshot['total'] ?? 0);
			self::ensure_offer_invoice_number($booking_id, (float) ($snapshot['total'] ?? 0), $currency, $payment_amount_due);
		}
		self::booking_meta_upsert($booking_id, 'price_offer_updated_at', current_time('mysql'));
		self::booking_meta_upsert($booking_id, 'price_offer_note', sanitize_textarea_field((string) $note));
		return $snapshot;
	}

	private static function suggested_price_offer_email(array $booking) {
		$stored = self::booking_meta_value((int) ($booking['id'] ?? 0), 'price_offer_recipient', '');
		if (is_email($stored)) {
			return $stored;
		}
		$customer_email = sanitize_email((string) ($booking['customer_email'] ?? ''));
		if (is_email($customer_email)) {
			return $customer_email;
		}
		foreach (['ical_description', 'ical_summary', 'note'] as $field) {
			$text = (string) ($booking[$field] ?? '');
			if ($text !== '' && preg_match('/[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}/i', $text, $m)) {
				$email = sanitize_email((string) $m[0]);
				if (is_email($email)) {
					return $email;
				}
			}
		}
		return '';
	}

	private static function i18n_with_locale_fallback($msgid, $fallbacks = []) {
		$locale = function_exists('determine_locale') ? (string) determine_locale() : (string) get_locale();
		$locale = strtolower($locale);
		$locale_fallback = '';
		foreach ((array) $fallbacks as $prefix => $value) {
			$prefix = strtolower((string) $prefix);
			if ($prefix !== '' && strpos($locale, $prefix) === 0) {
				$locale_fallback = (string) $value;
				break;
			}
		}

		// If locale-specific fallback exists (e.g. Czech), prefer it to avoid broken/invalid gettext entries.
		if ($locale_fallback !== '') {
			return $locale_fallback;
		}

		// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText -- Dynamic lookup is intentional in this fallback helper.
		$translated = (string) __($msgid, 'vachr-short-term-rental-reservations');
		$translated = (string) wp_check_invalid_utf8($translated, false);
		if (trim($translated) !== '' && $translated !== $msgid) {
			return $translated;
		}

		$msgid_safe = (string) wp_check_invalid_utf8((string) $msgid, false);
		return (trim($msgid_safe) !== '') ? $msgid_safe : (string) $msgid;
	}

	private static function admin_code_label($value, $type = 'status') {
		$value = strtolower(trim((string) $value));
		if ($value === '') {
			return '';
		}

		$status_labels = [
			'pending' => ['Pending', ['cs' => 'Čekající']],
			'confirmed' => ['Confirmed', ['cs' => 'Potvrzeno']],
			'cancelled' => ['Cancelled', ['cs' => 'Zrušeno']],
			'blocked' => ['Blocked', ['cs' => 'Blokováno']],
			'issued' => ['Issued', ['cs' => 'Vystaveno']],
			'paid' => ['Paid', ['cs' => 'Zaplaceno']],
			'failed' => ['Failed', ['cs' => 'Selhalo']],
			'refunded' => ['Refunded', ['cs' => 'Vráceno']],
		];
		$method_labels = [
			'card' => ['Card', ['cs' => 'Karta']],
			'card_stripe' => ['Payment card (Stripe)', ['cs' => 'Platební karta (Stripe)']],
			'card_mollie' => ['Payment card (Mollie)', ['cs' => 'Platební karta (Mollie)']],
			'bank_transfer' => ['Bank transfer', ['cs' => 'Bankovní převod']],
			'cash' => ['Cash', ['cs' => 'Hotově']],
			'manual' => ['Manual', ['cs' => 'Ručně']],
		];

		$labels = ($type === 'method') ? $method_labels : $status_labels;
		if (isset($labels[$value])) {
			return self::i18n_with_locale_fallback($labels[$value][0], $labels[$value][1]);
		}

		return ucwords(str_replace('_', ' ', $value));
	}

	private static function admin_status_label($status) {
		return self::admin_code_label($status, 'status');
	}

	private static function admin_payment_method_label($method) {
		return self::admin_code_label($method, 'method');
	}

	private static function is_valid_ymd($value) {
		if (!is_string($value) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
			return false;
		}
		$dt = DateTime::createFromFormat('Y-m-d', $value);
		return $dt && $dt->format('Y-m-d') === $value;
	}

	private static function sanitize_rate_apply_mode($value) {
		$mode = sanitize_text_field((string) $value);
		$allowed = ['always', 'date_range', 'months_of_year', 'weekdays', 'annual_dates', 'days_of_year'];
		return in_array($mode, $allowed, true) ? $mode : 'date_range';
	}

	private static function sanitize_rate_months_mask($value) {
		$months = [];
		if (is_array($value)) {
			foreach ($value as $month) {
				$m = (int) $month;
				if ($m >= 1 && $m <= 12 && !in_array($m, $months, true)) {
					$months[] = $m;
				}
			}
		} else {
			$raw = sanitize_text_field((string) $value);
			if ($raw !== '') {
				foreach (explode(',', $raw) as $month) {
					$m = (int) trim((string) $month);
					if ($m >= 1 && $m <= 12 && !in_array($m, $months, true)) {
						$months[] = $m;
					}
				}
			}
		}
		sort($months, SORT_NUMERIC);
		return implode(',', $months);
	}

	private static function parse_rate_months_mask($mask) {
		$out = [];
		$mask = sanitize_text_field((string) $mask);
		if ($mask === '') {
			return $out;
		}
		foreach (explode(',', $mask) as $month) {
			$m = (int) trim((string) $month);
			if ($m >= 1 && $m <= 12 && !in_array($m, $out, true)) {
				$out[] = $m;
			}
		}
		sort($out, SORT_NUMERIC);
		return $out;
	}

	private static function sanitize_rate_weekdays_mask($value) {
		$days = [];
		if (is_array($value)) {
			foreach ($value as $weekday) {
				$d = (int) $weekday;
				if ($d >= 1 && $d <= 7 && !in_array($d, $days, true)) {
					$days[] = $d;
				}
			}
		} else {
			$raw = sanitize_text_field((string) $value);
			if ($raw !== '') {
				foreach (explode(',', $raw) as $weekday) {
					$d = (int) trim((string) $weekday);
					if ($d >= 1 && $d <= 7 && !in_array($d, $days, true)) {
						$days[] = $d;
					}
				}
			}
		}
		sort($days, SORT_NUMERIC);
		return implode(',', $days);
	}

	private static function parse_rate_weekdays_mask($mask) {
		$out = [];
		$mask = sanitize_text_field((string) $mask);
		if ($mask === '') {
			return $out;
		}
		foreach (explode(',', $mask) as $weekday) {
			$d = (int) trim((string) $weekday);
			if ($d >= 1 && $d <= 7 && !in_array($d, $out, true)) {
				$out[] = $d;
			}
		}
		sort($out, SORT_NUMERIC);
		return $out;
	}

	private static function normalize_annual_date_part($value) {
		$value = trim((string) $value);
		if (!preg_match('/^(\d{1,2})\s*[\.\-\/]\s*(\d{1,2})\.?$/', $value, $m)) {
			return '';
		}
		$day = (int) $m[1];
		$month = (int) $m[2];
		if (!checkdate($month, $day, 2024)) {
			return '';
		}
		return sprintf('%02d-%02d', $month, $day);
	}

	private static function sanitize_rate_annual_dates_mask($value) {
		$raw = sanitize_textarea_field((string) $value);
		$raw = str_replace(["\r\n", "\r", "\n", ';'], ',', $raw);
		$entries = [];
		foreach (explode(',', $raw) as $entry) {
			$entry = trim((string) $entry);
			if ($entry === '') {
				continue;
			}
			preg_match_all('/\d{1,2}\s*[\.\-\/]\s*\d{1,2}\.?/', $entry, $matches, PREG_OFFSET_CAPTURE);
			$dates = $matches[0] ?? [];
			if (count($dates) >= 2) {
				$first = $dates[0][0];
				$second = $dates[1][0];
				$between = substr($entry, (int) $dates[0][1] + strlen($first), (int) $dates[1][1] - ((int) $dates[0][1] + strlen($first)));
				$is_range = (strpos($between, '-') !== false || preg_match('/\b(to|az|až)\b/i', $between));
				if ($is_range) {
					$from = self::normalize_annual_date_part($first);
					$to = self::normalize_annual_date_part($second);
					if ($from !== '' && $to !== '') {
						$entries[] = $from . ':' . $to;
					}
				} else {
					foreach ($dates as $date_match) {
						$date = self::normalize_annual_date_part($date_match[0]);
						if ($date !== '') {
							$entries[] = $date;
						}
					}
				}
			} elseif (count($dates) === 1) {
				$date = self::normalize_annual_date_part($dates[0][0]);
				if ($date !== '') {
					$entries[] = $date;
				}
			}
		}
		$entries = array_values(array_unique($entries));
		sort($entries, SORT_STRING);
		return implode(',', $entries);
	}

	private static function format_rate_annual_dates_mask($mask) {
		$items = [];
		foreach (explode(',', sanitize_text_field((string) $mask)) as $entry) {
			$entry = trim((string) $entry);
			if (preg_match('/^(\d{2})-(\d{2})$/', $entry, $m)) {
				$items[] = (int) $m[2] . '.' . (int) $m[1] . '.';
			} elseif (preg_match('/^(\d{2})-(\d{2}):(\d{2})-(\d{2})$/', $entry, $m)) {
				$items[] = (int) $m[2] . '.' . (int) $m[1] . '.-' . (int) $m[4] . '.' . (int) $m[3] . '.';
			}
		}
		return implode(', ', $items);
	}

	private static function sanitize_day_of_year($value) {
		$day = (int) sanitize_text_field((string) $value);
		if ($day < 1 || $day > 366) {
			return null;
		}
		return $day;
	}

	private static function date_range_days($date_from, $date_to) {
		$days = [];
		$from_ts = strtotime($date_from);
		$to_ts = strtotime($date_to);
		if (!$from_ts || !$to_ts || $to_ts <= $from_ts) {
			return $days;
		}

		for ($ts = $from_ts; $ts < $to_ts; $ts += DAY_IN_SECONDS) {
			$days[] = gmdate('Y-m-d', $ts);
		}
		return $days;
	}

	private static function bump_calendar_cache_version($unit_id) {
		$unit_id = (int) $unit_id;
		if ($unit_id <= 0) {
			return;
		}
		$key = 'vstrb_v_' . $unit_id;
		$v = wp_cache_get($key, 'vstrb_calendar');
		if (false === $v) {
			wp_cache_set($key, 2, 'vstrb_calendar', 3600);
			return;
		}
		wp_cache_incr($key, 1, 'vstrb_calendar');
	}

	private static function get_available_currencies() {
		$raw = (string) get_option('vstrb_available_currencies', 'EUR');
		$parts = array_map('trim', explode(',', strtoupper($raw)));
		$out = [];
		foreach ($parts as $c) {
			$c = preg_replace('/[^A-Z]/', '', $c);
			if (strlen($c) !== 3) {
				continue;
			}
			if (!in_array($c, $out, true)) {
				$out[] = $c;
			}
		}
		if (!$out) {
			$out = ['EUR'];
		}
		return $out;
	}

	private static function get_base_currency($currencies = []) {
		if (!$currencies) {
			$currencies = self::get_available_currencies();
		}
		$base = strtoupper(trim((string) get_option('vstrb_base_currency', 'EUR')));
		if (!in_array($base, $currencies, true)) {
			$base = (string) $currencies[0];
		}
		return $base;
	}

	private static function get_currency_rates_map($currencies = [], $base_currency = '') {
		if (!$currencies) {
			$currencies = self::get_available_currencies();
		}
		if ($base_currency === '') {
			$base_currency = self::get_base_currency($currencies);
		}

		$rates = [$base_currency => 1.0];
		$raw = (string) get_option('vstrb_currency_rates', '');
		$pairs = array_map('trim', explode(',', strtoupper($raw)));
		foreach ($pairs as $pair) {
			if ($pair === '' || strpos($pair, '=') === false) {
				continue;
			}
			[$code, $val] = array_map('trim', explode('=', $pair, 2));
			$code = preg_replace('/[^A-Z]/', '', $code);
			if (strlen($code) !== 3) {
				continue;
			}
			$rate = self::parse_decimal_input($val, null);
			if ($rate === null || $rate <= 0) {
				continue;
			}
			$rates[$code] = (float) $rate;
		}

		// Guarantee base is always 1.
		$rates[$base_currency] = 1.0;
		return $rates;
	}

	private static function convert_price_by_rate($amount, $from_currency, $to_currency, $rates) {
		$amt = self::parse_decimal_input($amount, null);
		$from = strtoupper(trim((string) $from_currency));
		$to = strtoupper(trim((string) $to_currency));
		if ($amt === null || $from === '' || $to === '') {
			return null;
		}
		if ($from === $to) {
			return round((float) $amt, 2);
		}
		$from_rate = isset($rates[$from]) ? (float) $rates[$from] : 0.0;
		$to_rate = isset($rates[$to]) ? (float) $rates[$to] : 0.0;
		if ($from_rate <= 0 || $to_rate <= 0) {
			return null;
		}
		return round((((float) $amt / $from_rate) * $to_rate), 2);
	}

	private static function bump_services_cache_version() {
		$v = wp_cache_get('vstrb_v_services', 'vstrb_services');
		if (false === $v) {
			$v = 1;
		}
		$v = ((int) $v) + 1;
		wp_cache_set('vstrb_v_services', $v, 'vstrb_services', 3600);
	}

	private static function bump_amenities_cache_version() {
		$v = wp_cache_get('vstrb_v_amenities', 'vstrb_amenities');
		if (false === $v) {
			$v = 1;
		}
		$v = ((int) $v) + 1;
		wp_cache_set('vstrb_v_amenities', $v, 'vstrb_amenities', 3600);
	}

	private static function bump_rates_cache_version() {
		$v = wp_cache_get('vstrb_v_rates', 'vstrb_rates');
		if (false === $v) {
			$v = 1;
		}
		$v = ((int) $v) + 1;
		wp_cache_set('vstrb_v_rates', $v, 'vstrb_rates', 3600);
	}

	private static function normalize_language_code($locale) {
		$locale = strtolower(trim((string) $locale));
		if ($locale === '') {
			return '';
		}
		if (preg_match('/^([a-z]{2})/', $locale, $m)) {
			return $m[1];
		}
		return '';
	}

	private static function get_default_language_codes() {
		static $codes = null;
		if (is_array($codes)) {
			return $codes;
		}

		$codes = explode(' ', 'cs en sk de pl fr es it nl pt ru uk ja zh');
		return $codes;
	}

	private static function get_configured_language_codes() {
		$raw = (string) get_option('vstrb_available_languages', '');
		$raw = trim($raw);
		if ($raw === '') {
			return self::get_default_language_codes();
		}

		$parts = array_map('trim', explode(',', strtolower($raw)));
		$codes = [];
		foreach ($parts as $part) {
			$code = self::normalize_language_code($part);
			if ($code === '') {
				continue;
			}
			if (!in_array($code, $codes, true)) {
				$codes[] = $code;
			}
		}

		return $codes ?: self::get_default_language_codes();
	}

	private static function get_language_options($include_global = false) {
		$codes = self::get_configured_language_codes();

		$codes = array_values(array_unique(array_filter($codes, static function($code) {
			return is_string($code) && preg_match('/^[a-z]{2}$/', $code);
		})));
		sort($codes, SORT_STRING);

		$options = [];
		if ($include_global) {
			$options[''] = esc_html__('All (Global)', 'vachr-short-term-rental-reservations');
		}
		foreach ($codes as $code) {
			$options[$code] = strtoupper($code);
		}

		return $options;
	}

	private static function get_invoice_language_codes() {
		$codes = array_merge(self::get_default_language_codes(), self::get_configured_language_codes());
		$domain = 'vachr-short-term-rental-reservations';
		foreach ((array) glob(VSTRB_PLUGIN_DIR . 'languages/' . $domain . '-*.mo') as $mofile) {
			$base = basename((string) $mofile, '.mo');
			$locale = substr($base, strlen($domain . '-'));
			$code = self::normalize_language_code($locale);
			if ($code !== '') {
				$codes[] = $code;
			}
		}
		$codes = array_values(array_unique(array_filter($codes, static function($code) {
			return is_string($code) && preg_match('/^[a-z]{2}$/', $code);
		})));
		sort($codes, SORT_STRING);
		return $codes;
	}

	private static function get_invoice_language_options() {
		$options = [];
		foreach (self::get_invoice_language_codes() as $code) {
			$options[$code] = strtoupper($code);
		}
		return $options;
	}

	private static function resolve_wp_locale($language) {
		$language = sanitize_text_field((string) $language);
		$language = str_replace('-', '_', $language);
		if ($language === '') {
			return '';
		}
		if (function_exists('PLL') && PLL()->model) {
			$lang = PLL()->model->get_language($language);
			if ($lang && !empty($lang->locale)) {
				return (string) $lang->locale;
			}
		}
		if (preg_match('/^[a-z]{2}_[A-Z]{2}$/', $language)) {
			return $language;
		}
		$map = [
			'cs' => 'cs_CZ',
			'de' => 'de_DE',
			'en' => 'en_US',
			'es' => 'es_ES',
			'fr' => 'fr_FR',
			'it' => 'it_IT',
			'nl' => 'nl_NL',
			'pl' => 'pl_PL',
			'pt' => 'pt_PT',
			'ru' => 'ru_RU',
			'sk' => 'sk_SK',
			'uk' => 'uk_UA',
			'zh' => 'zh_CN',
		];
		$key = strtolower(substr($language, 0, 2));
		return $map[$key] ?? $language;
	}

	private static function load_textdomain_for_locale($locale) {
		$locale = sanitize_text_field((string) $locale);
		if ($locale === '') {
			return;
		}
		$domain = 'vachr-short-term-rental-reservations';
		unload_textdomain($domain, true);
		$mofile = VSTRB_PLUGIN_DIR . 'languages/' . $domain . '-' . $locale . '.mo';
		if (is_readable($mofile)) {
			load_textdomain($domain, $mofile);
		}
	}

	private static function sanitize_invoice_language($language) {
		$language = self::normalize_language_code($language);
		if ($language === '') {
			return '';
		}
		return in_array($language, self::get_invoice_language_codes(), true) ? $language : '';
	}

	private static function get_effective_invoice_language($language = null) {
		$language = self::sanitize_invoice_language((string) $language);
		if ($language !== '') {
			return $language;
		}
		return self::sanitize_invoice_language((string) get_option('vstrb_invoice_language', ''));
	}

	private static function switch_to_invoice_locale($language = null) {
		$language = self::get_effective_invoice_language($language);
		$locale = self::resolve_wp_locale($language);
		if ($locale === '') {
			self::$invoice_translation_locale = '';
			return false;
		}
		self::$invoice_translation_locale = $locale;
		$switched = false;
		if (function_exists('switch_to_locale')) {
			$switched = (bool) switch_to_locale($locale);
		}
		self::load_textdomain_for_locale($locale);
		return $switched;
	}

	public static function register() {
		add_action('admin_init', [__CLASS__, 'handle_export']);
		add_menu_page(
			__('Bookings', 'vachr-short-term-rental-reservations'),
			__('Bookings', 'vachr-short-term-rental-reservations'),
			'manage_options',
			'vstrb',
			[__CLASS__, 'page_dashboard'],
			'dashicons-calendar-alt',
			26
		);
        add_submenu_page('vstrb', __('Guest Registrations', 'vachr-short-term-rental-reservations'), __('Guest Registrations', 'vachr-short-term-rental-reservations'), 'manage_options', 'vstrb-guests', [__CLASS__, 'page_guest_registrations']);
		add_submenu_page('vstrb', __('Calendar', 'vachr-short-term-rental-reservations'), __('Calendar', 'vachr-short-term-rental-reservations'), 'manage_options', 'vstrb-calendar', [__CLASS__, 'page_calendar']);
		add_submenu_page('vstrb', __('Rates', 'vachr-short-term-rental-reservations'), __('Rates', 'vachr-short-term-rental-reservations'), 'manage_options', 'vstrb-rates', [__CLASS__, 'page_rates']);
		add_submenu_page('vstrb', __('Apartment Content', 'vachr-short-term-rental-reservations'), __('Apartment Content', 'vachr-short-term-rental-reservations'), 'manage_options', 'vstrb-unit-content', [__CLASS__, 'page_unit_content']);
		add_submenu_page('vstrb', __('iCal Imports', 'vachr-short-term-rental-reservations'), __('iCal Imports', 'vachr-short-term-rental-reservations'), 'manage_options', 'vstrb-ical', [__CLASS__, 'page_ical']);
        add_submenu_page('vstrb', __('Settings', 'vachr-short-term-rental-reservations'), __('Settings', 'vachr-short-term-rental-reservations'), 'manage_options', 'vstrb-general', [__CLASS__, 'page_general']);
		add_submenu_page('vstrb', __('Pro Features', 'vachr-short-term-rental-reservations'), __('Pro Features', 'vachr-short-term-rental-reservations'), 'manage_options', 'vstrb-pro-features', [__CLASS__, 'page_pro_features']);
	}

	public static function page_pro_features() {
		$locale = function_exists('determine_locale') ? determine_locale() : get_locale();
		$language = strtolower(substr((string) $locale, 0, 2));
		$pro_urls = [
			'cs' => 'https://vachr.cz/cs/vachr-short-term-rental-reservations-pro/',
			'de' => 'https://vachr.cz/de/vachr-short-term-rental-reservations-pro/',
		];
		$pro_url = apply_filters(
			'vstrb_pro_features_url',
			$pro_urls[$language] ?? 'https://vachr.cz/vachr-short-term-rental-reservations-pro/',
			$locale
		);
		?>
		<div class="wrap">
			<h1><?php esc_html_e('Vachr Reservations Pro', 'vachr-short-term-rental-reservations'); ?></h1>
			<p style="max-width:760px;font-size:15px">
				<?php esc_html_e('The free plugin covers the core reservation workflow. The Pro add-on is designed for owners who want to save admin time, publish fresh local content, and keep billing records connected to every booking.', 'vachr-short-term-rental-reservations'); ?>
			</p>
			<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;max-width:980px;margin-top:18px">
				<div style="background:#fff;border:1px solid #dcdcde;padding:18px">
					<h2 style="margin-top:0"><?php esc_html_e('AI content generator', 'vachr-short-term-rental-reservations'); ?></h2>
					<p><?php esc_html_e('Create WordPress posts from source URLs or pasted text, then publish them in selected languages as drafts or live posts.', 'vachr-short-term-rental-reservations'); ?></p>
					<ul style="list-style:disc;margin-left:20px">
						<li><?php esc_html_e('Turn local event pages, attraction pages, and travel tips into guest-friendly articles.', 'vachr-short-term-rental-reservations'); ?></li>
						<li><?php esc_html_e('Schedule daily, weekly, or monthly generation profiles for regular fresh content.', 'vachr-short-term-rental-reservations'); ?></li>
						<li><?php esc_html_e('Avoid duplicate posts with processed-source history and reuse your preferred categories and languages.', 'vachr-short-term-rental-reservations'); ?></li>
					</ul>
				</div>
				<div style="background:#fff;border:1px solid #dcdcde;padding:18px">
					<h2 style="margin-top:0"><?php esc_html_e('Invoicing and transactions', 'vachr-short-term-rental-reservations'); ?></h2>
					<p><?php esc_html_e('Keep invoices, payment references, billing details, payment status, and paid timestamps together with each booking.', 'vachr-short-term-rental-reservations'); ?></p>
					<ul style="list-style:disc;margin-left:20px">
						<li><?php esc_html_e('Generate invoice numbers and printable invoice pages for saved booking records.', 'vachr-short-term-rental-reservations'); ?></li>
						<li><?php esc_html_e('Track card, bank transfer, cash, and manual payment statuses in one transaction view.', 'vachr-short-term-rental-reservations'); ?></li>
						<li><?php esc_html_e('Use billing details from guest registrations and bookings without copying data between tools.', 'vachr-short-term-rental-reservations'); ?></li>
					</ul>
				</div>
			</div>
			<p style="margin-top:20px">
				<a class="button button-primary" href="<?php echo esc_url($pro_url); ?>" target="_blank" rel="noopener noreferrer">
					<?php esc_html_e('Learn more about Pro', 'vachr-short-term-rental-reservations'); ?>
				</a>
			</p>
		</div>
		<?php
	}

	public static function page_dashboard() {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$edit_booking = null;
		$editing_booking_id = 0;

		// Handle Actions
		$action = self::get_param_text('action', '');
		$id = self::get_param_int('id', 0);
		$nonce = self::get_param_text('_wpnonce', '');
		if ($action !== '' && $id > 0 && $nonce !== '') {
			if (wp_verify_nonce($nonce, 'vstrb_booking_action_' . $id)) {
				if ($action === 'confirm') {
					$wpdb->update($t['bookings'], ['status'=>'confirmed'], ['id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					VSTRB_Emails::notify_customer_confirmed($id);
					echo '<div class="notice notice-success"><p>' . esc_html__('Booking confirmed.', 'vachr-short-term-rental-reservations') . '</p></div>';
				}
				if ($action === 'unconfirm') {
					$wpdb->update($t['bookings'], ['status'=>'pending'], ['id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					echo '<div class="notice notice-success"><p>' . esc_html__('Booking set to pending.', 'vachr-short-term-rental-reservations') . '</p></div>';
				}
				if ($action === 'mark_paid') {
					$paid_ok = self::booking_mark_paid($id, 'manual');
					if ($paid_ok) {
						echo '<div class="notice notice-success"><p>' . esc_html__('Payment marked as paid and invoice matched.', 'vachr-short-term-rental-reservations') . '</p></div>';
					} else {
						echo '<div class="notice notice-error"><p>' . esc_html__('Unable to mark payment as paid.', 'vachr-short-term-rental-reservations') . '</p></div>';
					}
				}
				if ($action === 'delete') {
					// Delete dependencies
					$wpdb->delete($t['booking_days'], ['booking_id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					$wpdb->delete($t['booking_services'], ['booking_id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					$wpdb->delete($t['booking_meta'], ['booking_id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					if (!empty($t['payments'])) {
						$wpdb->delete($t['payments'], ['booking_id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					}
					if (!empty($t['invoices'])) {
						$wpdb->delete($t['invoices'], ['booking_id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					}
					$wpdb->delete($t['bookings'], ['id'=>$id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					echo '<div class="notice notice-success"><p>' . esc_html__('Booking deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
				}
			} else {
				 echo '<div class="notice notice-error"><p>' . esc_html__('Nonce verification failed.', 'vachr-short-term-rental-reservations') . '</p></div>';
			}
		}

		if (isset($_POST['vstrb_save_price_offer']) || isset($_POST['vstrb_send_price_offer'])) {
			check_admin_referer('vstrb_price_offer', 'vstrb_price_offer_nonce');
			$offer_id = isset($_POST['booking_id']) ? (int) sanitize_text_field(wp_unslash($_POST['booking_id'])) : 0;
			$editing_booking_id = $offer_id;
			$booking = $offer_id > 0 ? $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['bookings']} WHERE id=%d", $offer_id), ARRAY_A) : null; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			if (!$booking) {
				echo '<div class="notice notice-error"><p>' . esc_html__('Booking not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$discount_type = sanitize_text_field(wp_unslash($_POST['price_offer_discount_type'] ?? 'none'));
				$discount_value = self::parse_decimal_input(sanitize_text_field(wp_unslash($_POST['price_offer_discount_value'] ?? '')), 0);
				$offer_note = sanitize_textarea_field(wp_unslash($_POST['price_offer_note'] ?? ''));
				$offer_recipient = sanitize_email(wp_unslash($_POST['price_offer_recipient'] ?? ''));
				$offer_currency = strtoupper(sanitize_text_field(wp_unslash($_POST['price_offer_currency'] ?? ($booking['currency'] ?? 'EUR'))));
				$offer_currencies = self::get_available_currencies();
				if (!in_array($offer_currency, $offer_currencies, true)) {
					$offer_currency = (string) ($booking['currency'] ?? 'EUR');
				}
				$booking['currency'] = $offer_currency;
				$offer_guests = max(1, (int) sanitize_text_field(wp_unslash($_POST['price_offer_guests'] ?? ($booking['guests'] ?? 1))));
				$booking['guests'] = $offer_guests;
				$offer_services = isset($_POST['price_offer_services'])
					? (array) map_deep(wp_unslash($_POST['price_offer_services']), 'sanitize_text_field')
					: [];
				$offer_services = array_values(array_unique(array_filter(array_map('intval', $offer_services))));
				$include_payment_details = isset($_POST['price_offer_include_payment_details']);
				$snapshot = self::refresh_booking_price_offer($booking, $discount_type, $discount_value, $offer_note, $offer_services, $include_payment_details);
				if (!is_array($snapshot)) {
					echo '<div class="notice notice-error"><p>' . esc_html__('Unable to generate the price offer.', 'vachr-short-term-rental-reservations') . '</p></div>';
				} elseif (isset($_POST['vstrb_send_price_offer'])) {
					if ($offer_recipient === '' || !is_email($offer_recipient)) {
						echo '<div class="notice notice-error"><p>' . esc_html__('Please enter a valid email address for the price offer.', 'vachr-short-term-rental-reservations') . '</p></div>';
					} else {
						self::booking_meta_upsert($offer_id, 'price_offer_recipient', $offer_recipient);
						$sent = VSTRB_Emails::notify_customer_price_offer($offer_id, $offer_recipient);
						if ($sent) {
							echo '<div class="notice notice-success"><p>' . esc_html__('Price offer saved and sent to the selected email address.', 'vachr-short-term-rental-reservations') . '</p></div>';
						} else {
							echo '<div class="notice notice-error"><p>' . esc_html__('Price offer was saved, but the email could not be sent. Check the guest email address.', 'vachr-short-term-rental-reservations') . '</p></div>';
						}
					}
				} else {
					if ($offer_recipient !== '' && is_email($offer_recipient)) {
						self::booking_meta_upsert($offer_id, 'price_offer_recipient', $offer_recipient);
					}
					echo '<div class="notice notice-success"><p>' . esc_html__('Price offer saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
				}
			}
		}

		if (isset($_POST['vstrb_save_booking'])) {
			check_admin_referer('vstrb_save_booking', 'vstrb_booking_nonce');
			$save_id = isset($_POST['booking_id']) ? (int) sanitize_text_field(wp_unslash($_POST['booking_id'])) : 0;

			if ($save_id <= 0) {
				echo '<div class="notice notice-error"><p>' . esc_html__('Booking not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$current = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['bookings']} WHERE id=%d", $save_id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				if (!$current) {
					echo '<div class="notice notice-error"><p>' . esc_html__('Booking not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
				} else {
					$editing_booking_id = $save_id;
					$is_imported = strpos((string) $current['source'], 'ical_') === 0;
					$note = sanitize_textarea_field(wp_unslash($_POST['note'] ?? ''));

					if ($is_imported) {
						$updated = $wpdb->update($t['bookings'], ['note' => $note], ['id' => $save_id], ['%s'], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
						if ($updated === false) {
							echo '<div class="notice notice-error"><p>' . esc_html__('Unable to save changes.', 'vachr-short-term-rental-reservations') . '</p></div>';
						} else {
							echo '<div class="notice notice-success"><p>' . esc_html__('Note updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
						}
					} else {
						$unit_id = isset($_POST['unit_id']) ? (int) sanitize_text_field(wp_unslash($_POST['unit_id'])) : (int) $current['unit_id'];
						$status = sanitize_text_field(wp_unslash($_POST['status'] ?? $current['status']));
						$source = sanitize_text_field(wp_unslash($_POST['source'] ?? $current['source']));
						$date_from = sanitize_text_field(wp_unslash($_POST['date_from'] ?? $current['date_from']));
						$date_to = sanitize_text_field(wp_unslash($_POST['date_to'] ?? $current['date_to']));
						$adults = max(1, (int) sanitize_text_field(wp_unslash($_POST['adults'] ?? $current['adults'] ?? $current['guests'])));
						$children = max(0, (int) sanitize_text_field(wp_unslash($_POST['children'] ?? $current['children'] ?? 0)));
						$guests = $adults + $children;
						$customer_name = sanitize_text_field(wp_unslash($_POST['customer_name'] ?? ''));
						$customer_lastname = sanitize_text_field(wp_unslash($_POST['customer_lastname'] ?? ''));
						$customer_gender = sanitize_text_field(wp_unslash($_POST['customer_gender'] ?? ''));
						$customer_country = strtoupper(sanitize_text_field(wp_unslash($_POST['customer_country'] ?? '')));
						$customer_email = sanitize_email(wp_unslash($_POST['customer_email'] ?? ''));
						$customer_phone = sanitize_text_field(wp_unslash($_POST['customer_phone'] ?? ''));
						$total_price = self::parse_decimal_input(sanitize_text_field(wp_unslash($_POST['total_price'] ?? '')), null);
						$currency = strtoupper(sanitize_text_field(wp_unslash($_POST['currency'] ?? $current['currency'])));
						$external_booking_id = sanitize_text_field(wp_unslash($_POST['external_booking_id'] ?? ''));

						if (!in_array($status, ['pending', 'confirmed', 'cancelled', 'blocked'], true)) {
							$status = 'pending';
						}
						if (!preg_match('/^[A-Z]{3}$/', $currency)) {
							$currency = (string) $current['currency'];
						}
						if ($source === '') {
							$source = (string) $current['source'];
						}
						if ($customer_country !== '' && !VSTRB_Countries::is_valid($customer_country)) {
							$customer_country = '';
						}

						$has_error = false;
						if (!VSTRB_DB::unit_exists($unit_id)) {
							$has_error = true;
							echo '<div class="notice notice-error"><p>' . esc_html__('Invalid unit selected.', 'vachr-short-term-rental-reservations') . '</p></div>';
						}
						if (!self::is_valid_ymd($date_from) || !self::is_valid_ymd($date_to) || strtotime($date_to) <= strtotime($date_from)) {
							$has_error = true;
							echo '<div class="notice notice-error"><p>' . esc_html__('Invalid date range.', 'vachr-short-term-rental-reservations') . '</p></div>';
						}

						if (!$has_error) {
							$old_unit_id = (int) $current['unit_id'];
							$old_date_from = (string) $current['date_from'];
							$old_date_to = (string) $current['date_to'];
							$rebuild_days = ($old_unit_id !== $unit_id || $old_date_from !== $date_from || $old_date_to !== $date_to);

							$update_data = [
								'unit_id' => $unit_id,
								'status' => $status,
								'source' => $source,
								'date_from' => $date_from,
								'date_to' => $date_to,
								'guests' => $guests,
								'adults' => $adults,
								'children' => $children,
								'customer_name' => $customer_name,
								'customer_lastname' => $customer_lastname,
								'customer_gender' => $customer_gender,
								'customer_country' => $customer_country,
								'customer_email' => $customer_email,
								'customer_phone' => $customer_phone,
								'note' => $note,
								'discount_type' => (string) ($current['discount_type'] ?? 'none'),
								'discount_value' => (float) ($current['discount_value'] ?? 0),
								'total_price' => $total_price,
								'currency' => $currency,
								'external_booking_id' => $external_booking_id,
							];

							$wpdb->query('START TRANSACTION'); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
							$failed = false;
							if ($rebuild_days) {
								$deleted = $wpdb->delete($t['booking_days'], ['booking_id' => $save_id], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
								if ($deleted === false) {
									$failed = true;
								} else {
									$days = self::date_range_days($date_from, $date_to);
									foreach ($days as $day) {
										$ins = $wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
											"INSERT INTO {$t['booking_days']} (unit_id, day, booking_id) VALUES (%d, %s, %d)",
											$unit_id, $day, $save_id
										));
										if ($ins === false) {
											$failed = true;
											break;
										}
									}
								}
							}

							if (!$failed) {
								$updated = $wpdb->update($t['bookings'], $update_data, ['id' => $save_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
								if ($updated === false) {
									$failed = true;
								}
							}

							if ($failed) {
								$wpdb->query('ROLLBACK'); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
								echo '<div class="notice notice-error"><p>' . esc_html__('Unable to save changes. The selected dates may conflict with another booking.', 'vachr-short-term-rental-reservations') . '</p></div>';
							} else {
								$wpdb->query('COMMIT'); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
								self::bump_calendar_cache_version($old_unit_id);
								if ($old_unit_id !== $unit_id) {
									self::bump_calendar_cache_version($unit_id);
								}
								echo '<div class="notice notice-success"><p>' . esc_html__('Booking updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
							}
						}
					}
				}
			}
		}

		if ($action === 'edit' && $id > 0) {
			$editing_booking_id = $id;
		}
		if ($editing_booking_id > 0) {
			$edit_booking = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['bookings']} WHERE id=%d", $editing_booking_id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		}

		echo '<div class="wrap"><h1>' . esc_html__('Bookings', 'vachr-short-term-rental-reservations') . '</h1>';
		if ($editing_booking_id > 0 && !$edit_booking) {
			echo '<div class="notice notice-error"><p>' . esc_html__('Booking not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}
		if ($edit_booking) {
			$is_imported_edit = strpos((string) $edit_booking['source'], 'ical_') === 0;
			$edit_action_url = add_query_arg(
				['page' => 'vstrb', 'action' => 'edit', 'id' => (int) $edit_booking['id']],
				admin_url('admin.php')
			);
			$cancel_url = admin_url('admin.php?page=vstrb');
			$all_units = get_posts(['post_type'=>'vstrb_unit','numberposts'=>-1,'post_status'=>['publish','draft']]);

			echo '<form method="post" action="' . esc_url($edit_action_url) . '" style="background:#fff;padding:15px;margin:15px 0;border:1px solid #ccd0d4">';
			wp_nonce_field('vstrb_save_booking', 'vstrb_booking_nonce');
			echo '<input type="hidden" name="booking_id" value="' . (int) $edit_booking['id'] . '">';
			echo '<h3 style="margin-top:0;margin-bottom:10px">' . esc_html($is_imported_edit ? __('Edit imported booking note', 'vachr-short-term-rental-reservations') : __('Edit booking', 'vachr-short-term-rental-reservations')) . '</h3>';

			if ($is_imported_edit) {
				echo '<p style="margin-top:0;color:#666">' . esc_html__('Imported reservations can only edit note.', 'vachr-short-term-rental-reservations') . '</p>';
				echo '<p><label>' . esc_html__('Note', 'vachr-short-term-rental-reservations') . '<br><textarea name="note" rows="5" style="width:100%">' . esc_textarea((string) ($edit_booking['note'] ?? '')) . '</textarea></label></p>';
			} else {
				echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px">';
				echo '<div><label>' . esc_html__('Unit', 'vachr-short-term-rental-reservations') . '<br><select name="unit_id" style="width:100%">';
				foreach ($all_units as $u) {
					printf('<option value="%1$d" %2$s>%3$s</option>', (int) $u->ID, selected((int) $edit_booking['unit_id'], (int) $u->ID, false), esc_html($u->post_title));
				}
				echo '</select></label></div>';
				echo '<div><label>' . esc_html__('Status', 'vachr-short-term-rental-reservations') . '<br><select name="status" style="width:100%">';
				foreach (['pending', 'confirmed', 'cancelled', 'blocked'] as $st) {
					printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($st), selected((string) $edit_booking['status'], $st, false), esc_html(self::admin_status_label($st)));
				}
				echo '</select></label></div>';
				echo '<div><label>' . esc_html__('Source', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="source" value="' . esc_attr((string) ($edit_booking['source'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Adults', 'vachr-short-term-rental-reservations') . '<br><input type="number" min="1" name="adults" value="' . (int) ($edit_booking['adults'] ?? $edit_booking['guests'] ?? 1) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Children', 'vachr-short-term-rental-reservations') . '<br><input type="number" min="0" name="children" value="' . (int) ($edit_booking['children'] ?? 0) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Date From', 'vachr-short-term-rental-reservations') . '<br><input type="date" name="date_from" value="' . esc_attr((string) ($edit_booking['date_from'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Date To', 'vachr-short-term-rental-reservations') . '<br><input type="date" name="date_to" value="' . esc_attr((string) ($edit_booking['date_to'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('First Name', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="customer_name" value="' . esc_attr((string) ($edit_booking['customer_name'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Last Name', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="customer_lastname" value="' . esc_attr((string) ($edit_booking['customer_lastname'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Gender', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="customer_gender" value="' . esc_attr((string) ($edit_booking['customer_gender'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Country', 'vachr-short-term-rental-reservations') . '<br><select name="customer_country" style="width:100%"><option value="">' . esc_html__('Select country', 'vachr-short-term-rental-reservations') . '</option>';
				foreach (VSTRB_Countries::all() as $country_code => $country_name) {
					printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($country_code), selected((string) ($edit_booking['customer_country'] ?? ''), $country_code, false), esc_html($country_name . ' (' . $country_code . ')'));
				}
				echo '</select></label></div>';
				echo '<div><label>' . esc_html__('Email', 'vachr-short-term-rental-reservations') . '<br><input type="email" name="customer_email" value="' . esc_attr((string) ($edit_booking['customer_email'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Phone', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="customer_phone" value="' . esc_attr((string) ($edit_booking['customer_phone'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Reservation #', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="external_booking_id" value="' . esc_attr((string) ($edit_booking['external_booking_id'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Total', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="total_price" value="' . esc_attr((string) ($edit_booking['total_price'] ?? '')) . '" style="width:100%"></label></div>';
				echo '<div><label>' . esc_html__('Currency', 'vachr-short-term-rental-reservations') . '<br><input type="text" maxlength="3" name="currency" value="' . esc_attr((string) ($edit_booking['currency'] ?? 'EUR')) . '" style="width:100%"></label></div>';
				echo '</div>';
				echo '<p><label>' . esc_html__('Note', 'vachr-short-term-rental-reservations') . '<br><textarea name="note" rows="4" style="width:100%">' . esc_textarea((string) ($edit_booking['note'] ?? '')) . '</textarea></label></p>';
			}
			echo '<p style="margin-bottom:0"><button type="submit" name="vstrb_save_booking" class="button button-primary">' . esc_html__('Save Changes', 'vachr-short-term-rental-reservations') . '</button> <a class="button" href="' . esc_url($cancel_url) . '">' . esc_html__('Cancel', 'vachr-short-term-rental-reservations') . '</a></p>';
			echo '</form>';

			$offer_discount_type = (string) ($edit_booking['discount_type'] ?? 'none');
			if (!in_array($offer_discount_type, ['none', 'amount', 'percent'], true)) {
				$offer_discount_type = 'none';
			}
			$offer_discount_value = (float) ($edit_booking['discount_value'] ?? 0);
			$offer_note = self::booking_meta_value((int) $edit_booking['id'], 'price_offer_note', '');
			$offer_snapshot_raw = self::booking_meta_value((int) $edit_booking['id'], 'price_breakdown', '');
			$offer_snapshot = $offer_snapshot_raw !== '' ? json_decode($offer_snapshot_raw, true) : null;
			$offer_total = isset($edit_booking['total_price']) ? (float) $edit_booking['total_price'] : 0.0;
			$offer_currency = (string) ($edit_booking['currency'] ?? 'EUR');
			$offer_currencies = self::get_available_currencies();
			if (!in_array(strtoupper($offer_currency), $offer_currencies, true)) {
				$offer_currencies[] = strtoupper($offer_currency);
			}
			$offer_guests = max(1, (int) ($edit_booking['guests'] ?? 1));
			$offer_selected_services = self::booking_service_ids((int) $edit_booking['id']);
			$offer_service_options = self::unit_service_options((int) ($edit_booking['unit_id'] ?? 0), $offer_currency);
			$include_payment_details_value = self::booking_meta_value((int) $edit_booking['id'], 'price_offer_include_payment_details', '');
			$include_payment_details = ($include_payment_details_value === '1');
			if ($include_payment_details_value === '' && self::booking_meta_value((int) $edit_booking['id'], 'invoice_number', '') !== '') {
				$include_payment_details = true;
			}
			$offer_recipient = self::suggested_price_offer_email($edit_booking);
			$offer_public_url = VSTRB_Emails::price_offer_url((int) $edit_booking['id']);
			$offer_action_url = add_query_arg(
				['page' => 'vstrb', 'action' => 'edit', 'id' => (int) $edit_booking['id']],
				admin_url('admin.php')
			);
			echo '<form id="vstrb-price-offer" method="post" action="' . esc_url($offer_action_url) . '#vstrb-price-offer" style="background:#fff;padding:15px;margin:15px 0;border:1px solid #ccd0d4;border-left:4px solid #2271b1">';
			wp_nonce_field('vstrb_price_offer', 'vstrb_price_offer_nonce');
			echo '<input type="hidden" name="booking_id" value="' . (int) $edit_booking['id'] . '">';
			echo '<h3 style="margin-top:0;margin-bottom:10px">' . esc_html__('Price offer', 'vachr-short-term-rental-reservations') . '</h3>';
			echo '<p style="margin-top:0;color:#666">' . esc_html__('Generate a price from the current reservation dates, apply an optional one-time manual discount, and send the offer to the guest by email.', 'vachr-short-term-rental-reservations') . '</p>';
			echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;align-items:end">';
			echo '<div><label>' . esc_html__('Manual discount', 'vachr-short-term-rental-reservations') . '<br><select name="price_offer_discount_type" style="width:100%">';
			foreach (['none' => __('No discount', 'vachr-short-term-rental-reservations'), 'amount' => __('Fixed amount', 'vachr-short-term-rental-reservations'), 'percent' => __('Percent', 'vachr-short-term-rental-reservations')] as $dt => $label) {
				printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($dt), selected($offer_discount_type, $dt, false), esc_html($label));
			}
			echo '</select></label></div>';
			echo '<div><label>' . esc_html__('Discount value', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="price_offer_discount_value" value="' . esc_attr((string) $offer_discount_value) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Guests', 'vachr-short-term-rental-reservations') . '<br><input type="number" min="1" name="price_offer_guests" value="' . (int) $offer_guests . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Currency', 'vachr-short-term-rental-reservations') . '<br><select name="price_offer_currency" style="width:100%">';
			foreach ($offer_currencies as $currency_code) {
				printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($currency_code), selected(strtoupper($offer_currency), $currency_code, false), esc_html($currency_code));
			}
			echo '</select></label></div>';
			echo '<div><label>' . esc_html__('Send offer to email', 'vachr-short-term-rental-reservations') . '<br><input type="email" name="price_offer_recipient" value="' . esc_attr($offer_recipient) . '" style="width:100%" placeholder="' . esc_attr__('guest@example.com', 'vachr-short-term-rental-reservations') . '"></label></div>';
			echo '<div><strong>' . esc_html__('Current offer total', 'vachr-short-term-rental-reservations') . '</strong><br>' . ($offer_total > 0 ? esc_html(number_format_i18n($offer_total, 2) . ' ' . $offer_currency) : esc_html__('Not generated yet', 'vachr-short-term-rental-reservations')) . '</div>';
			if (is_array($offer_snapshot) && isset($offer_snapshot['manual_discount_total']) && (float) $offer_snapshot['manual_discount_total'] > 0) {
				echo '<div><strong>' . esc_html__('Applied manual discount', 'vachr-short-term-rental-reservations') . '</strong><br>-' . esc_html(number_format_i18n((float) $offer_snapshot['manual_discount_total'], 2) . ' ' . $offer_currency) . '</div>';
			}
			echo '</div>';
			if (!empty($offer_service_options)) {
				echo '<div style="margin-top:12px;padding:10px;background:#f6f7f7;border:1px solid #dcdcde">';
				echo '<strong>' . esc_html__('Services', 'vachr-short-term-rental-reservations') . '</strong>';
				echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:8px;margin-top:8px">';
				foreach ($offer_service_options as $service_option) {
					$service_id = (int) $service_option['id'];
					$is_required = ((int) $service_option['is_optional'] === 0);
					$is_checked = $is_required || in_array($service_id, $offer_selected_services, true);
					if ($is_required) {
						echo '<input type="hidden" name="price_offer_services[]" value="' . (int) $service_id . '">';
					}
					echo '<label style="display:flex;gap:6px;align-items:flex-start">';
					echo '<input type="checkbox" name="price_offer_services[]" value="' . (int) $service_id . '" ' . checked($is_checked, true, false) . ($is_required ? ' disabled' : '') . '>';
					echo '<span>' . esc_html((string) $service_option['name']) . ' <small style="color:#666">(' . esc_html((string) $service_option['pricing_type']) . ', ' . esc_html(number_format_i18n((float) $service_option['price'], 2) . ' ' . $offer_currency) . ($is_required ? ', ' . esc_html__('required', 'vachr-short-term-rental-reservations') : '') . ')</small></span>';
					echo '</label>';
				}
				echo '</div></div>';
			}
			echo '<p><label><input type="checkbox" name="price_offer_include_payment_details" value="1" ' . checked($include_payment_details, true, false) . '> ' . esc_html__('Include payment details and generate invoice number', 'vachr-short-term-rental-reservations') . '</label><br>';
			echo '<span class="description">' . esc_html__('Enable when you want to send bank/card payment details with this price offer. Leave disabled for a price-only offer.', 'vachr-short-term-rental-reservations') . '</span></p>';
			if ($offer_public_url !== '') {
				echo '<p><label>' . esc_html__('Public price offer link', 'vachr-short-term-rental-reservations') . '<br><input type="url" readonly value="' . esc_attr($offer_public_url) . '" style="width:100%" onclick="this.select()"></label></p>';
			}
			echo '<p><label>' . esc_html__('Offer note for email', 'vachr-short-term-rental-reservations') . '<br><textarea name="price_offer_note" rows="3" style="width:100%">' . esc_textarea($offer_note) . '</textarea></label></p>';
			echo '<p style="margin-bottom:0"><button type="submit" name="vstrb_save_price_offer" class="button">' . esc_html__('Generate price offer', 'vachr-short-term-rental-reservations') . '</button> <button type="submit" name="vstrb_send_price_offer" class="button button-primary">' . esc_html__('Generate and send by email', 'vachr-short-term-rental-reservations') . '</button></p>';
			echo '</form>';
		}

		// Get filter parameters
		$filter_unit = self::get_param_int('filter_unit', 0);
		$filter_status = self::get_param_text('filter_status', '');
		$filter_source = self::get_param_text('filter_source', '');
		$filter_date_from = self::get_param_text('filter_date_from', '');
		$filter_date_to = self::get_param_text('filter_date_to', '');
		$filter_guest = self::get_param_text('filter_guest', '');
		$filter_booking_id = self::get_param_text('filter_booking_id', '');
		if ($filter_date_from === '') {
			$filter_date_from = current_time('Y-m-d');
		}

		// Get sorting parameters
		$orderby = self::get_param_text('orderby', 'date_from');
		$order = strtoupper(self::get_param_text('order', 'ASC')) === 'DESC' ? 'DESC' : 'ASC';

		// Get pagination parameters
		$per_page = self::get_param_int('per_page', 50);
		$paged = max(1, self::get_param_int('paged', 1));
		$offset = ($paged - 1) * $per_page;

		// Display filter form
		echo '<form method="get" class="vstrb-filters" style="background:#fff;padding:15px;margin:15px 0;border:1px solid #ccd0d4">';
		echo '<input type="hidden" name="page" value="vstrb" />';
		echo '<h3 style="margin-top:0">' . esc_html__('Filters', 'vachr-short-term-rental-reservations') . '</h3>';
		echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;margin-bottom:10px">';

		// Unit filter
		echo '<div><label>' . esc_html__('Unit', 'vachr-short-term-rental-reservations') . '<br>';
		$units = get_posts(['post_type'=>'vstrb_unit','numberposts'=>-1,'post_status'=>['publish','draft']]);
		echo '<select name="filter_unit" style="width:100%">';
		echo '<option value="0">' . esc_html__('All Units', 'vachr-short-term-rental-reservations') . '</option>';
		foreach ($units as $u) {
			printf('<option value="%1$d" %2$s>%3$s</option>', (int)$u->ID, selected($filter_unit, $u->ID, false), esc_html($u->post_title));
		}
		echo '</select></label></div>';

		// Status filter
		echo '<div><label>' . esc_html__('Status', 'vachr-short-term-rental-reservations') . '<br>';
		echo '<select name="filter_status" style="width:100%">';
		echo '<option value="">' . esc_html__('All Statuses', 'vachr-short-term-rental-reservations') . '</option>';
		$statuses = ['pending'=>esc_html__('Pending', 'vachr-short-term-rental-reservations'), 'confirmed'=>esc_html__('Confirmed', 'vachr-short-term-rental-reservations'), 'cancelled'=>esc_html__('Cancelled', 'vachr-short-term-rental-reservations'), 'blocked'=>esc_html__('Blocked', 'vachr-short-term-rental-reservations')];
		foreach ($statuses as $k=>$v) {
			printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($k), selected($filter_status, $k, false), esc_html($v));
		}
		echo '</select></label></div>';

		// Source filter
		echo '<div><label>' . esc_html__('Source', 'vachr-short-term-rental-reservations') . '<br>';
		echo '<select name="filter_source" style="width:100%">';
		echo '<option value="">' . esc_html__('All Sources', 'vachr-short-term-rental-reservations') . '</option>';
		$sources = [
			'direct' => esc_html__('Direct', 'vachr-short-term-rental-reservations'),
			'admin' => esc_html__('Admin', 'vachr-short-term-rental-reservations'),
			'ical_airbnb' => 'iCal Airbnb',
			'ical_booking' => 'iCal Booking.com',
			'ical_e_chalupy' => 'iCal e-chalupy.cz',
			'ical_megaubytovanie' => 'iCal Megaubytovanie',
			'ical_hauzi' => 'iCal Hauzi',
			'ical_bezrealitky' => 'iCal BezRealitky',
		];
		foreach ($sources as $k=>$v) {
			printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($k), selected($filter_source, $k, false), esc_html($v));
		}
		echo '</select></label></div>';

		// Date from filter
		echo '<div><label>' . esc_html__('Date From', 'vachr-short-term-rental-reservations') . '<br>';
		echo '<input type="date" name="filter_date_from" value="'.esc_attr($filter_date_from).'" style="width:100%"></label></div>';

		// Date to filter
		echo '<div><label>' . esc_html__('Date To', 'vachr-short-term-rental-reservations') . '<br>';
		echo '<input type="date" name="filter_date_to" value="'.esc_attr($filter_date_to).'" style="width:100%"></label></div>';

		// Guest name filter
		echo '<div><label>' . esc_html__('Guest Name', 'vachr-short-term-rental-reservations') . '<br>';
		echo '<input type="text" name="filter_guest" value="'.esc_attr($filter_guest).'" placeholder="'.esc_attr__('Search...', 'vachr-short-term-rental-reservations').'" style="width:100%"></label></div>';

		// Booking ID filter
		echo '<div><label>' . esc_html__('Booking/Reservation ID', 'vachr-short-term-rental-reservations') . '<br>';
		echo '<input type="text" name="filter_booking_id" value="'.esc_attr($filter_booking_id).'" placeholder="'.esc_attr__('ID or Reservation #', 'vachr-short-term-rental-reservations').'" style="width:100%"></label></div>';

		echo '</div>';
		echo '<button type="submit" class="button button-primary">' . esc_html__('Apply Filters', 'vachr-short-term-rental-reservations') . '</button> ';
		echo '<a href="'.esc_url(admin_url('admin.php?page=vstrb')).'" class="button">' . esc_html__('Clear Filters', 'vachr-short-term-rental-reservations') . '</a>';
		echo '</form>';

		// Build WHERE clause
		$where = ['1=1'];
		$where_values = [];

		if ($filter_unit > 0) {
			$where[] = 'b.unit_id = %d';
			$where_values[] = $filter_unit;
		}
		if ($filter_status) {
			$where[] = 'b.status = %s';
			$where_values[] = $filter_status;
		}
		if ($filter_source) {
			$where[] = 'b.source = %s';
			$where_values[] = $filter_source;
		}
		if ($filter_date_from) {
			// Include ongoing bookings by checking the reservation end date.
			$where[] = 'b.date_to >= %s';
			$where_values[] = $filter_date_from;
		}
		if ($filter_date_to) {
			$where[] = 'b.date_to <= %s';
			$where_values[] = $filter_date_to;
		}
		if ($filter_guest) {
			$where[] = '(b.customer_name LIKE %s OR b.customer_lastname LIKE %s OR b.customer_email LIKE %s)';
			$like_guest = '%' . $wpdb->esc_like($filter_guest) . '%';
			$where_values[] = $like_guest;
			$where_values[] = $like_guest;
			$where_values[] = $like_guest;
		}
		if ($filter_booking_id) {
			$where[] = '(b.id = %d OR b.external_booking_id LIKE %s OR b.ical_uid LIKE %s)';
			$where_values[] = (int)$filter_booking_id;
			$like_id = '%' . $wpdb->esc_like($filter_booking_id) . '%';
			$where_values[] = $like_id;
			$where_values[] = $like_id;
		}

		$where_clause = implode(' AND ', $where);

		// Build ORDER BY clause
		$allowed_orderby = ['id', 'status', 'source', 'unit_title', 'date_from', 'date_to', 'customer_name', 'created_at'];
		if (!in_array($orderby, $allowed_orderby)) $orderby = 'date_from';
		$order_clause = "b.$orderby $order";
		if ($orderby === 'unit_title') $order_clause = "p.post_title $order";

		// Get total count
		$count_sql = "SELECT COUNT(*) FROM {$t['bookings']} b 
					 LEFT JOIN {$wpdb->posts} p ON p.ID = b.unit_id 
					 WHERE $where_clause";
		if ($where_values) {
			$count_sql = $wpdb->prepare($count_sql, ...$where_values); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		}
		$total_items = (int)$wpdb->get_var($count_sql); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.NotPrepared -- Custom table query.
		$total_pages = ceil($total_items / $per_page);

		// Get bookings
		$sql = "SELECT b.*, p.post_title as unit_title 
				FROM {$t['bookings']} b 
				LEFT JOIN {$wpdb->posts} p ON p.ID = b.unit_id 
				WHERE $where_clause
				ORDER BY $order_clause
				LIMIT %d OFFSET %d";
		
		$query_values = array_merge($where_values, [$per_page, $offset]);
		$rows = $wpdb->get_results($wpdb->prepare($sql, ...$query_values), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.NotPrepared -- Custom table query.
		$meta_count_map = [];
		$meta_rows_map = [];
		$service_count_map = [];
		$service_rows_map = [];
		$field_id_by_key = [];
		$field_label_locale = [];
		$field_label_global = [];
		$invoice_map = [];
		$payment_map = [];
		$service_label_locale = [];
		$service_label_global = [];
		$admin_lang = self::normalize_language_code(get_user_locale());

		if (!empty($rows)) {
			$booking_ids = array_values(array_unique(array_map('intval', array_column($rows, 'id'))));
			if (!empty($booking_ids)) {
				$booking_id_placeholders = implode(', ', array_fill(0, count($booking_ids), '%d'));

				if (!empty($t['invoices'])) {
					$invoice_rows = $wpdb->get_results(
						$wpdb->prepare(
							"SELECT id, booking_id, invoice_number, status, total_amount, currency, issued_at, paid_at
							 FROM {$t['invoices']}
							 WHERE booking_id IN ({$booking_id_placeholders})
							 ORDER BY id DESC", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
							...$booking_ids
						),
						ARRAY_A
					);
					foreach ($invoice_rows as $ir) {
						$bid = (int) $ir['booking_id'];
						if (!isset($invoice_map[$bid])) {
							$invoice_map[$bid] = $ir;
						}
					}
				}

				if (!empty($t['payments'])) {
					$payment_rows = $wpdb->get_results(
						$wpdb->prepare(
							"SELECT id, booking_id, invoice_id, method, provider, status, amount, currency, provider_reference, gateway_session_id, gateway_status, gateway_payment_status, payment_method_type, card_brand, card_last4, card_country, gateway_created_at, gateway_paid_at, paid_at
							 FROM {$t['payments']}
							 WHERE booking_id IN ({$booking_id_placeholders})
							 ORDER BY id DESC", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
							...$booking_ids
						),
						ARRAY_A
					);
					foreach ($payment_rows as $pr) {
						$bid = (int) $pr['booking_id'];
						if (!isset($payment_map[$bid])) {
							$payment_map[$bid] = $pr;
						}
					}
				}

				$meta_counts = $wpdb->get_results(
					$wpdb->prepare(
						"SELECT booking_id, COUNT(*) AS cnt
						 FROM {$t['booking_meta']}
						 WHERE booking_id IN ({$booking_id_placeholders})
						 GROUP BY booking_id", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
						...$booking_ids
					),
					ARRAY_A
				);
				foreach ($meta_counts as $mc) {
					$meta_count_map[(int) $mc['booking_id']] = (int) $mc['cnt'];
				}

				$service_rows = $wpdb->get_results(
					$wpdb->prepare(
						"SELECT bs.booking_id, bs.service_id, bs.qty, bs.price, s.name AS service_name
						 FROM {$t['booking_services']} bs
						 LEFT JOIN {$t['services']} s ON s.id = bs.service_id
						 WHERE bs.booking_id IN ({$booking_id_placeholders})
						 ORDER BY bs.booking_id ASC, bs.service_id ASC", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
						...$booking_ids
					),
					ARRAY_A
				);
				$service_ids = array_values(array_unique(array_filter(array_map(static function($row) {
					return (int) ($row['service_id'] ?? 0);
				}, (array) $service_rows))));
				if (!empty($service_ids)) {
					$service_id_placeholders = implode(', ', array_fill(0, count($service_ids), '%d'));
					$service_meta_rows = $wpdb->get_results(
						$wpdb->prepare(
							"SELECT service_id, meta_value, locale
							 FROM {$t['service_meta']}
							 WHERE meta_key IN ('label', 'name') AND service_id IN ({$service_id_placeholders})
							 ORDER BY id ASC", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
							...$service_ids
						),
						ARRAY_A
					);
					foreach ($service_meta_rows as $smr) {
						$sid = (int) ($smr['service_id'] ?? 0);
						$name = sanitize_text_field((string) ($smr['meta_value'] ?? ''));
						if ($sid <= 0 || $name === '') {
							continue;
						}
						$meta_lang = self::normalize_language_code($smr['locale'] ?? '');
						if ($admin_lang !== '' && $meta_lang === $admin_lang && !isset($service_label_locale[$sid])) {
							$service_label_locale[$sid] = $name;
						} elseif ($meta_lang === '' && !isset($service_label_global[$sid])) {
							$service_label_global[$sid] = $name;
						}
					}
				}
				foreach ($service_rows as $sr) {
					$booking_id = (int) $sr['booking_id'];
					$service_id = (int) $sr['service_id'];
					if (!isset($service_rows_map[$booking_id])) {
						$service_rows_map[$booking_id] = [];
					}
					$service_rows_map[$booking_id][] = [
						'service_id' => $service_id,
						'service_name' => $service_label_locale[$service_id] ?? ($service_label_global[$service_id] ?? $sr['service_name']),
						'qty' => (float) $sr['qty'],
						'price' => (float) $sr['price'],
					];
					if (!isset($service_count_map[$booking_id])) {
						$service_count_map[$booking_id] = 0;
					}
					$service_count_map[$booking_id]++;
				}

				$all_meta_rows = $wpdb->get_results(
					$wpdb->prepare(
						"SELECT booking_id, meta_key, meta_value
						 FROM {$t['booking_meta']}
						 WHERE booking_id IN ({$booking_id_placeholders})
						 ORDER BY id ASC", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
						...$booking_ids
					),
					ARRAY_A
				);
				$field_keys = [];
				foreach ($all_meta_rows as $mr) {
					$booking_id = (int) $mr['booking_id'];
					if (!isset($meta_rows_map[$booking_id])) {
						$meta_rows_map[$booking_id] = [];
					}
					$meta_rows_map[$booking_id][] = [
						'meta_key' => $mr['meta_key'],
						'meta_value' => $mr['meta_value'],
					];
					$field_keys[] = $mr['meta_key'];
				}

				$field_keys = array_values(array_unique(array_filter($field_keys)));
				if (!empty($field_keys)) {
					$field_key_placeholders = implode(', ', array_fill(0, count($field_keys), '%s'));
					$field_rows = $wpdb->get_results(
						$wpdb->prepare(
							"SELECT id, field_key
							 FROM {$t['contact_fields']}
							 WHERE field_key IN ({$field_key_placeholders})", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
							...$field_keys
						),
						ARRAY_A
					);

					$field_ids = [];
					foreach ($field_rows as $fr) {
						$field_id = (int) $fr['id'];
						$field_id_by_key[$fr['field_key']] = $field_id;
						$field_ids[] = $field_id;
					}

					$field_ids = array_values(array_unique($field_ids));
					if (!empty($field_ids)) {
						$field_id_placeholders = implode(', ', array_fill(0, count($field_ids), '%d'));
						$user_lang = $admin_lang;

						$label_locale_rows = $wpdb->get_results(
							$wpdb->prepare(
								"SELECT field_id, meta_value
								 FROM {$t['contact_field_meta']}
								 WHERE meta_key='label' AND locale=%s AND field_id IN ({$field_id_placeholders})", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
								...array_merge(array($user_lang), $field_ids)
							),
							ARRAY_A
						);
						foreach ($label_locale_rows as $lr) {
							$field_label_locale[(int) $lr['field_id']] = $lr['meta_value'];
						}

						$label_global_rows = $wpdb->get_results(
							$wpdb->prepare(
								"SELECT field_id, meta_value
								 FROM {$t['contact_field_meta']}
								 WHERE meta_key='label' AND (locale IS NULL OR locale='') AND field_id IN ({$field_id_placeholders})", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
								...$field_ids
							),
							ARRAY_A
						);
						foreach ($label_global_rows as $lr) {
							if (!isset($field_label_locale[(int) $lr['field_id']])) {
								$field_label_global[(int) $lr['field_id']] = $lr['meta_value'];
							}
						}
					}
				}
			}
		}

		// Helper function for sortable column headers
		$sort_link = function($column, $label) use ($orderby, $order) {
			$new_order = ($orderby === $column && $order === 'ASC') ? 'DESC' : 'ASC';
			$arrow = '';
			if ($orderby === $column) {
				$arrow = $order === 'ASC' ? ' ^' : ' v';
			}
			$url = add_query_arg(['orderby'=>$column, 'order'=>$new_order]);
			return '<a href="'.esc_url($url).'" style="text-decoration:none;color:inherit">'.$label.$arrow.'</a>';
		};

		// Display results info
		/* translators: 1: start index, 2: end index, 3: total count */
		echo '<p>' . esc_html(sprintf(esc_html__('Showing %1$d-%2$d of %3$d bookings', 'vachr-short-term-rental-reservations'), 
			min($offset + 1, $total_items), 
			min($offset + $per_page, $total_items), 
			$total_items)) . '</p>';

		echo '<table class="widefat striped" id="vstrb-bookings-table">';
		echo '<thead><tr>
				<th style="width:50px">' . wp_kses_post($sort_link('id', 'ID')) . '</th>
				<th>' . wp_kses_post($sort_link('status', esc_html__('Status', 'vachr-short-term-rental-reservations'))) . '</th>
				<th>' . wp_kses_post($sort_link('source', esc_html__('Source', 'vachr-short-term-rental-reservations'))) . '</th>
				<th>' . wp_kses_post($sort_link('unit_title', esc_html__('Unit', 'vachr-short-term-rental-reservations'))) . '</th>
				<th>' . wp_kses_post($sort_link('date_from', esc_html__('From', 'vachr-short-term-rental-reservations'))) . '</th>
				<th>' . wp_kses_post($sort_link('date_to', esc_html__('To', 'vachr-short-term-rental-reservations'))) . '</th>
				<th>' . wp_kses_post($sort_link('customer_name', esc_html__('Guest', 'vachr-short-term-rental-reservations'))) . '</th>
				<th>'.esc_html__('Contact', 'vachr-short-term-rental-reservations').'</th>
				<th>'.esc_html__('Reservation #', 'vachr-short-term-rental-reservations').'</th>
				<th>'.esc_html__('Invoice', 'vachr-short-term-rental-reservations').'</th>
				<th>'.esc_html__('Payment', 'vachr-short-term-rental-reservations').'</th>
				<th>'.esc_html__('Total', 'vachr-short-term-rental-reservations').'</th>
				<th>' . wp_kses_post($sort_link('created_at', esc_html__('Created', 'vachr-short-term-rental-reservations'))) . '</th>
				<th>'.esc_html__('Actions', 'vachr-short-term-rental-reservations').'</th>
			  </tr></thead>';
		echo '<tbody>';
		
		foreach ($rows as $r) {
			$guestFull = esc_html(trim($r['customer_name'] . ' ' . ($r['customer_lastname'] ?? '')));
			if ($r['customer_gender']) $guestFull .= ' (' . esc_html($r['customer_gender']) . ')';
			$customer_country = (string) ($r['customer_country'] ?? '');
			if ($customer_country !== '') $guestFull .= ', ' . esc_html($customer_country);

			$confirmUrl = wp_nonce_url(admin_url('admin.php?page=vstrb&action=confirm&id=' . $r['id']), 'vstrb_booking_action_' . $r['id']);
			$unconfirmUrl = wp_nonce_url(admin_url('admin.php?page=vstrb&action=unconfirm&id=' . $r['id']), 'vstrb_booking_action_' . $r['id']);
			$deleteUrl = wp_nonce_url(admin_url('admin.php?page=vstrb&action=delete&id=' . $r['id']), 'vstrb_booking_action_' . $r['id']);
			$markPaidUrl = wp_nonce_url(admin_url('admin.php?page=vstrb&action=mark_paid&id=' . $r['id']), 'vstrb_booking_action_' . $r['id']);
			$editUrl = admin_url('admin.php?page=vstrb&action=edit&id=' . $r['id']);
			$is_imported_row = strpos((string) $r['source'], 'ical_') === 0;
			$invoice_row = $invoice_map[(int) $r['id']] ?? null;
			$payment_row = $payment_map[(int) $r['id']] ?? null;
			
			// Source badge
			$source_display = '';
			if (strpos($r['source'], 'ical_') === 0) {
				$provider_key = str_replace('ical_', '', $r['source']);
				$provider_labels = [
					'airbnb' => 'Airbnb',
					'booking' => 'Booking.com',
					'e_chalupy' => 'e-chalupy.cz',
					'megaubytovanie' => 'Megaubytovanie',
					'hauzi' => 'Hauzi',
					'bezrealitky' => 'BezRealitky',
				];
				$provider_colors = [
					'airbnb' => '#FF5A5F',
					'booking' => '#003580',
					'e_chalupy' => '#4CAF50',
					'megaubytovanie' => '#FF9800',
					'hauzi' => '#2196F3',
					'bezrealitky' => '#9C27B0',
				];

				$source_color = $provider_colors[$provider_key] ?? '#666';
				// For unknown providers, display the domain name nicely formatted
				if (!isset($provider_labels[$provider_key])) {
					// If it looks like a domain (contains a dot), display as-is
					// Otherwise capitalize it
					$source_label = strpos($provider_key, '.') !== false ? $provider_key : ucfirst($provider_key);
				} else {
					$source_label = $provider_labels[$provider_key];
				}
				$source_display = sprintf(
					'<span style="display:inline-block;padding:3px 8px;background:%1$s;color:#fff;border-radius:3px;font-size:11px;font-weight:bold" title="%2$s">%3$s</span>',
					esc_attr($source_color),
					esc_attr($r['source']),
					esc_html($source_label)
				);
			} else {
				// For direct/admin, show text
				$source_display = esc_html($r['source']);
			}

			echo '<tr>';
			echo '<td>' . esc_html($r['id']) . '</td>';
			echo '<td>' . esc_html(self::admin_status_label($r['status'])) . '</td>';
			echo '<td>' . wp_kses_post($source_display) . '</td>';
			echo '<td>' . esc_html($r['unit_title'] ?: $r['unit_id']) . '</td>';
			echo '<td>' . esc_html($r['date_from']) . '</td>';
			echo '<td>' . esc_html($r['date_to']) . '</td>';
			echo '<td>' . esc_html($guestFull) . '</td>';
			echo '<td>';
			if ($r['customer_email']) echo esc_html($r['customer_email']) . '<br>';
			if ($r['customer_phone']) echo esc_html($r['customer_phone']);
			
			// Show count of custom fields if any
			$custom_meta_rows = array_values(array_filter(
				$meta_rows_map[(int) $r['id']] ?? [],
				static function($row) use ($field_id_by_key) {
					$key = (string) ($row['meta_key'] ?? '');
					return isset($field_id_by_key[$key]);
				}
			));
			$meta_count = count($custom_meta_rows);
			$service_count = (int) ($service_count_map[(int) $r['id']] ?? 0);
			$booking_note = trim((string) ($r['note'] ?? ''));
			$has_ical_details = !empty($r['ical_summary']) || !empty($r['ical_description']) || !empty($r['ical_uid']);
			$has_details = $has_ical_details || $meta_count > 0 || $service_count > 0 || $booking_note !== '';
			if ($meta_count > 0) {
				/* translators: %d: number of custom fields */
				echo '<br><span style="background:#e5f3ff;color:#0073aa;padding:2px 5px;border-radius:3px;font-size:10px;margin-top:5px;display:inline-block">'.esc_html(sprintf(esc_html__('%d custom fields', 'vachr-short-term-rental-reservations'), $meta_count)).'</span>';
			}
			if ($service_count > 0) {
				/* translators: %d: number of selected services */
				echo '<br><span style="background:#edf7ed;color:#2e7d32;padding:2px 5px;border-radius:3px;font-size:10px;margin-top:5px;display:inline-block">'.esc_html(sprintf(esc_html__('%d selected services', 'vachr-short-term-rental-reservations'), $service_count)).'</span>';
			}
			echo '</td>';
			echo '<td>';
			if ($r['external_booking_id']) {
				echo '<strong style="color:#0073aa">' . esc_html($r['external_booking_id']) . '</strong>';
			}
			if ($has_details) {
				echo '<br><a href="#" class="vstrb-toggle-details" data-booking-id="'.(int)$r['id'].'" style="font-size:11px">'.esc_html__('Show details', 'vachr-short-term-rental-reservations').'</a>';
			}
			echo '</td>';
			echo '<td>';
			if (is_array($invoice_row)) {
				echo '<strong>' . esc_html((string) ($invoice_row['invoice_number'] ?? '')) . '</strong><br>';
				echo '<span style="font-size:11px">' . esc_html(self::admin_status_label((string) ($invoice_row['status'] ?? 'issued'))) . '</span>';
			} else {
				echo '-';
			}
			echo '</td>';
			echo '<td>';
			if (is_array($payment_row)) {
				echo '<strong>' . esc_html(self::admin_status_label((string) ($payment_row['status'] ?? 'pending'))) . '</strong><br>';
				$payment_method = trim((string) ($payment_row['method'] ?? ''));
				$payment_method_text = trim(self::admin_payment_method_label($payment_method) . ' ' . (string) ($payment_row['provider'] ?? ''));
				if ($payment_method_text !== '') {
					echo '<span style="font-size:11px">' . esc_html($payment_method_text) . '</span>';
				}
				$gateway_status_text = trim((string) (($payment_row['gateway_status'] ?? '') . ' ' . ($payment_row['gateway_payment_status'] ?? '')));
				if ($gateway_status_text !== '') {
					echo '<br><span style="font-size:11px">' . esc_html__('Gateway:', 'vachr-short-term-rental-reservations') . ' ' . esc_html($gateway_status_text) . '</span>';
				}
				$card_bits = [];
				if (!empty($payment_row['payment_method_type'])) {
					$card_bits[] = (string) $payment_row['payment_method_type'];
				}
				if (!empty($payment_row['card_brand'])) {
					$card_bits[] = (string) $payment_row['card_brand'];
				}
				if (!empty($payment_row['card_last4'])) {
					$card_bits[] = '**** ' . (string) $payment_row['card_last4'];
				}
				if (!empty($payment_row['card_country'])) {
					$card_bits[] = (string) $payment_row['card_country'];
				}
				if (!empty($card_bits)) {
					echo '<br><span style="font-size:11px">' . esc_html(implode(' / ', $card_bits)) . '</span>';
				}
			} else {
				$fallback_payment_status = '';
				$meta_rows = $meta_rows_map[(int) $r['id']] ?? [];
				foreach ($meta_rows as $mr_status) {
					if (($mr_status['meta_key'] ?? '') === 'payment_status') {
						$fallback_payment_status = (string) ($mr_status['meta_value'] ?? '');
					}
				}
				echo ($fallback_payment_status !== '' ? esc_html(self::admin_status_label($fallback_payment_status)) : '-');
			}
			echo '</td>';
			echo '<td>' . ($r['total_price'] ? esc_html($r['total_price'] . ' ' . $r['currency']) : '-') . '</td>';
			echo '<td>' . esc_html(substr($r['created_at'], 0, 16)) . '</td>';
			echo '<td>';
			if ($r['status'] === 'pending') {
				echo '<a href="' . esc_url($confirmUrl) . '" class="button button-small button-primary">' . esc_html__('Confirm', 'vachr-short-term-rental-reservations') . '</a> ';
			}
			if ($r['status'] === 'confirmed') {
				echo '<a href="' . esc_url($unconfirmUrl) . '" class="button button-small">' . esc_html__('Set to Pending', 'vachr-short-term-rental-reservations') . '</a> ';
			}
			$current_payment_status = '';
			if (is_array($payment_row)) {
				$current_payment_status = (string) ($payment_row['status'] ?? '');
			}
			if ($current_payment_status !== 'paid') {
				echo '<a href="' . esc_url($markPaidUrl) . '" class="button button-small">' . esc_html__('Mark Paid', 'vachr-short-term-rental-reservations') . '</a> ';
			}
			echo '<a href="' . esc_url($editUrl) . '" class="button button-small">' . esc_html($is_imported_row ? __('Edit note', 'vachr-short-term-rental-reservations') : __('Edit', 'vachr-short-term-rental-reservations')) . '</a> ';
			echo '<a href="' . esc_url($editUrl) . '#vstrb-price-offer" class="button button-small">' . esc_html__('Offer', 'vachr-short-term-rental-reservations') . '</a> ';
			do_action('vstrb_booking_row_actions', $r, [
				'invoice_row' => $invoice_row,
				'payment_row' => $payment_row,
			]);
			$registrations_url = admin_url('admin.php?page=vstrb-guests&unit_id=' . (int) $r['unit_id'] . '&booking_id=' . (int) $r['id']);
			echo '<a href="' . esc_url($registrations_url) . '" class="button button-small" title="' . esc_attr__('View guest registrations', 'vachr-short-term-rental-reservations') . '">' . esc_html_x('R', 'Abbreviation for Registrations button', 'vachr-short-term-rental-reservations') . '</a> ';
			echo '<a href="' . esc_url($deleteUrl) . '" onclick="return confirm(\''.esc_js(__('Delete this booking?', 'vachr-short-term-rental-reservations')).'\')" class="button button-small button-link-delete" style="color:#a00">' . esc_html__('Delete', 'vachr-short-term-rental-reservations') . '</a>';
			echo '</td>';
			echo '</tr>';
			
			// Hidden details row
			if ($has_details) {
				echo '<tr class="vstrb-details-row" id="vstrb-details-'.(int)$r['id'].'" style="display:none">';
				echo '<td colspan="14" style="background:#f9f9f9;padding:15px">';
				if ($has_ical_details) {
					echo '<strong>' . esc_html__('iCal Details:', 'vachr-short-term-rental-reservations') . '</strong><br>';
					if (!empty($r['ical_summary'])) echo '<strong>' . esc_html__('Summary:', 'vachr-short-term-rental-reservations') . '</strong> ' . esc_html($r['ical_summary']) . '<br>';
					if (!empty($r['ical_description'])) {
						$desc = esc_html($r['ical_description']);
						$desc = nl2br($desc);
						echo '<strong>' . esc_html__('Description:', 'vachr-short-term-rental-reservations') . '</strong><br><div style="max-height:150px;overflow-y:auto;padding:5px;background:#fff;border:1px solid #ddd">' . wp_kses_post($desc) . '</div><br>';
					}
					if (!empty($r['ical_uid'])) echo '<strong>UID:</strong> <code style="font-size:10px">' . esc_html($r['ical_uid']) . '</code><br>';
				}

				if ($booking_note !== '') {
					$note = esc_html($booking_note);
					$note = nl2br($note);
					echo '<div style="margin-top:10px;padding:10px;background:#fff;border:1px solid #eee;border-left:4px solid #7e57c2">';
					echo '<strong>' . esc_html__('Note:', 'vachr-short-term-rental-reservations') . '</strong><br>';
					echo '<div style="margin-top:5px;line-height:1.45">' . wp_kses_post($note) . '</div>';
					echo '</div>';
				}

				$service_rows = $service_rows_map[(int) $r['id']] ?? [];
				if (!empty($service_rows)) {
					echo '<div style="margin-top:10px;padding:10px;background:#fff;border:1px solid #eee;border-left:4px solid #2e7d32">';
					echo '<strong>' . esc_html__('Selected Services:', 'vachr-short-term-rental-reservations') . '</strong><ul style="margin:5px 0 0 20px;list-style:disc">';
					foreach ($service_rows as $sr) {
						$line_total = (float) $sr['qty'] * (float) $sr['price'];
						$service_name = !empty($sr['service_name']) ? $sr['service_name'] : sprintf('#%d', (int) $sr['service_id']);
						$qty = number_format_i18n((float) $sr['qty'], 2);
						$price = number_format_i18n((float) $sr['price'], 2);
						$total = number_format_i18n($line_total, 2);
						$currency = !empty($r['currency']) ? ' ' . $r['currency'] : '';
						echo '<li><strong>' . esc_html($service_name) . '</strong>: ' . esc_html($qty) . ' x ' . esc_html($price . $currency) . ' = ' . esc_html($total . $currency) . '</li>';
					}
					echo '</ul></div>';
				}
				
				// Custom fields
				if ($custom_meta_rows) {
					echo '<div style="margin-top:10px;padding:10px;background:#fff;border:1px solid #eee;border-left:4px solid #0073aa">';
					echo '<strong>' . esc_html__('Custom Contact Fields:', 'vachr-short-term-rental-reservations') . '</strong><ul style="margin:5px 0 0 20px;list-style:disc">';
					foreach ($custom_meta_rows as $mr) {
						$fid = (int) ($field_id_by_key[$mr['meta_key']] ?? 0);
						$label = '';
						if ($fid > 0) {
							$label = $field_label_locale[$fid] ?? ($field_label_global[$fid] ?? '');
						}
						$display_key = $label ?: $mr['meta_key'];
						echo '<li><strong>'.esc_html($display_key).':</strong> '.esc_html($mr['meta_value']).'</li>';
					}
					echo '</ul></div>';
				}
				echo '</td></tr>';
			}
		}
		
		if (!$rows) echo '<tr><td colspan="14">' . esc_html__('No bookings found.', 'vachr-short-term-rental-reservations') . '</td></tr>';
		echo '</tbody></table>';

		// Pagination
		if ($total_pages > 1) {
			echo '<div class="tablenav" style="margin-top:15px">';
			echo '<div class="tablenav-pages">';
			/* translators: %s: number of items */
			echo '<span class="displaying-num">' . esc_html(sprintf(
			/* translators: %s: number of items */
			esc_html__('%s items', 'vachr-short-term-rental-reservations'), 
			number_format_i18n($total_items)
		)) . '</span>';
			
			$page_links = paginate_links([
				'base' => add_query_arg('paged', '%#%'),
				'format' => '',
				'prev_text' => '&laquo;',
				'next_text' => '&raquo;',
				'total' => $total_pages,
				'current' => $paged,
			]);
			echo '<span class="pagination-links">' . wp_kses_post($page_links) . '</span>';
			echo '</div>';
			
			// Per page selector
			echo '<div style="float:right;margin-top:5px">';
			echo '<label>' . esc_html__('Items per page:', 'vachr-short-term-rental-reservations') . ' ';
			echo '<select onchange="window.location.href=this.value">';
			foreach ([25, 50, 100, 200] as $pp) {
				$url = add_query_arg(['per_page'=>$pp, 'paged'=>1]);
				printf('<option value="%s" %s>%d</option>', esc_url($url), selected($per_page, $pp, false), (int)$pp);
			}
			echo '</select></label>';
			echo '</div>';
			echo '</div>';
		}

		$show_details_text = wp_json_encode(__('Show details', 'vachr-short-term-rental-reservations'));
		$hide_details_text = wp_json_encode(__('Hide details', 'vachr-short-term-rental-reservations'));
		self::add_inline_admin_script("
			document.addEventListener('DOMContentLoaded', function() {
				document.querySelectorAll('.vstrb-toggle-details').forEach(function(link) {
					link.addEventListener('click', function(e) {
						e.preventDefault();
						var bookingId = this.getAttribute('data-booking-id');
						var detailsRow = document.getElementById('vstrb-details-' + bookingId);
						if (!detailsRow) return;
						if (detailsRow.style.display === 'none') {
							detailsRow.style.display = 'table-row';
							this.textContent = {$hide_details_text};
						} else {
							detailsRow.style.display = 'none';
							this.textContent = {$show_details_text};
						}
					});
				});
			});
		");
		echo '</div>';
		self::render_donation_box();
	}

	private static function get_available_unit_ids() {
		return array_map('intval', get_posts([
			'post_type' => 'vstrb_unit',
			'numberposts' => -1,
			'post_status' => ['publish', 'draft'],
			'fields' => 'ids',
		]));
	}

	private static function remember_last_unit_id($unit_id) {
		$user_id = get_current_user_id();
		if ($user_id > 0 && $unit_id > 0) {
			update_user_meta($user_id, '_vstrb_last_unit_id', (int) $unit_id);
		}
	}

	private static function resolve_unit_selection($requested_unit_id = 0) {
		$unit_ids = self::get_available_unit_ids();
		if (!$unit_ids) {
			return 0;
		}

		$requested_unit_id = (int) $requested_unit_id;
		if ($requested_unit_id > 0 && in_array($requested_unit_id, $unit_ids, true)) {
			self::remember_last_unit_id($requested_unit_id);
			return $requested_unit_id;
		}

		$user_id = get_current_user_id();
		if ($user_id > 0) {
			$last_unit_id = (int) get_user_meta($user_id, '_vstrb_last_unit_id', true);
			if ($last_unit_id > 0 && in_array($last_unit_id, $unit_ids, true)) {
				return $last_unit_id;
			}
		}

		if (count($unit_ids) === 1) {
			$single_unit_id = (int) $unit_ids[0];
			self::remember_last_unit_id($single_unit_id);
			return $single_unit_id;
		}

		return 0;
	}

	private static function unit_select($selected=0, $include_global = false) {
		$selected = $include_global ? (int) $selected : self::resolve_unit_selection((int) $selected);
		$units = get_posts(['post_type'=>'vstrb_unit','numberposts'=>-1,'post_status'=>['publish','draft']]);
		echo '<select name="unit_id">';
		if ($include_global) {
			echo '<option value="0" ' . selected($selected, 0, false) . '>' . esc_html__('All apartments (global)', 'vachr-short-term-rental-reservations') . '</option>';
		} else {
			echo '<option value="0">- ' . esc_html__('select unit', 'vachr-short-term-rental-reservations') . ' -</option>';
		}
		foreach ($units as $u) {
			printf('<option value="%1$d" %2$s>%3$s</option>', (int)$u->ID, selected($selected, $u->ID, false), esc_html($u->post_title));
		}
		echo '</select>';
	}

	private static function settings_tabs($active_tab) {
		$tabs = [
			'general' => __('General', 'vachr-short-term-rental-reservations'),
			'notifications' => __('Notifications', 'vachr-short-term-rental-reservations'),
			'payments' => __('Payments', 'vachr-short-term-rental-reservations'),
			'invoicing' => __('Invoicing', 'vachr-short-term-rental-reservations'),
			'calendar' => __('Calendar', 'vachr-short-term-rental-reservations'),
			'appearance' => __('Appearance', 'vachr-short-term-rental-reservations'),
			'services' => __('Services', 'vachr-short-term-rental-reservations'),
			'amenities' => __('Amenities', 'vachr-short-term-rental-reservations'),
			'contact-fields' => __('Contact Fields', 'vachr-short-term-rental-reservations'),
		];
		echo '<nav class="nav-tab-wrapper vstrb-settings-tabs" aria-label="' . esc_attr__('Settings sections', 'vachr-short-term-rental-reservations') . '">';
		foreach ($tabs as $tab => $label) {
			$url = add_query_arg(['page' => 'vstrb-general', 'tab' => $tab], admin_url('admin.php'));
			echo '<a class="nav-tab ' . ($active_tab === $tab ? 'nav-tab-active' : '') . '" href="' . esc_url($url) . '">' . esc_html($label) . '</a>';
		}
		echo '</nav>';
	}

	public static function page_general() {
		$active_tab = self::get_param_text('tab', 'general');
		$allowed_tabs = ['general', 'notifications', 'payments', 'invoicing', 'calendar', 'appearance', 'services', 'amenities', 'contact-fields'];
		if (!in_array($active_tab, $allowed_tabs, true)) $active_tab = 'general';
		echo '<div class="wrap"><h1>' . esc_html__('Settings', 'vachr-short-term-rental-reservations') . '</h1>';
		self::settings_tabs($active_tab);
		if ($active_tab === 'appearance') {
			self::page_styling(true);
			echo '</div>';
			return;
		}
		if ($active_tab === 'services') {
			self::page_services(true);
			echo '</div>';
			return;
		}
		if ($active_tab === 'amenities') {
			self::page_amenities(true);
			echo '</div>';
			return;
		}
		if ($active_tab === 'contact-fields') {
			self::page_contact_fields(true);
			echo '</div>';
			return;
		}

		if (isset($_POST['vstrb_save_general'])) {
			check_admin_referer('vstrb_general', 'vstrb_general_nonce');

			$curr_raw = sanitize_text_field(wp_unslash($_POST['vstrb_available_currencies'] ?? 'EUR'));
			$curr_parts = array_map('trim', explode(',', strtoupper($curr_raw)));
			$clean_currencies = [];
			foreach ($curr_parts as $curr_part) {
				$code = preg_replace('/[^A-Z]/', '', $curr_part);
				if (strlen($code) !== 3) {
					continue;
				}
				if (!in_array($code, $clean_currencies, true)) {
					$clean_currencies[] = $code;
				}
			}
			if (!$clean_currencies) {
				$clean_currencies = ['EUR'];
			}
			update_option('vstrb_available_currencies', implode(',', $clean_currencies));

			$base_currency = strtoupper(sanitize_text_field(wp_unslash($_POST['vstrb_base_currency'] ?? 'EUR')));
			if (!in_array($base_currency, $clean_currencies, true)) {
				$base_currency = (string) $clean_currencies[0];
			}
			update_option('vstrb_base_currency', $base_currency);

			$rates_raw = sanitize_text_field(wp_unslash($_POST['vstrb_currency_rates'] ?? ''));
			$rate_pairs = array_map('trim', explode(',', strtoupper($rates_raw)));
			$normalized_rates = [];
			foreach ($rate_pairs as $pair) {
				if ($pair === '' || strpos($pair, '=') === false) {
					continue;
				}
				[$code, $raw_val] = array_map('trim', explode('=', $pair, 2));
				$code = preg_replace('/[^A-Z]/', '', $code);
				if (strlen($code) !== 3) {
					continue;
				}
				$rate = self::parse_decimal_input($raw_val, null);
				if ($rate === null || $rate <= 0) {
					continue;
				}
				$normalized_rates[] = $code . '=' . rtrim(rtrim(number_format((float) $rate, 6, '.', ''), '0'), '.');
			}
			update_option('vstrb_currency_rates', implode(',', $normalized_rates));

			$langs_default = self::get_default_language_codes();
			$langs_raw = sanitize_text_field(wp_unslash($_POST['vstrb_available_languages'] ?? implode(',', $langs_default)));
			$lang_parts = array_map('trim', explode(',', strtolower($langs_raw)));
			$clean_languages = [];
			foreach ($lang_parts as $lang_part) {
				$code = self::normalize_language_code($lang_part);
				if ($code === '') {
					continue;
				}
				if (!in_array($code, $clean_languages, true)) {
					$clean_languages[] = $code;
				}
			}
			if (!$clean_languages) {
				$clean_languages = $langs_default;
			}
			update_option('vstrb_available_languages', implode(',', $clean_languages));

			$cal_price_mode_input = sanitize_text_field(wp_unslash($_POST['vstrb_cal_price_mode'] ?? 'price'));
			if (!in_array($cal_price_mode_input, ['off', 'price', 'diff'], true)) {
				$cal_price_mode_input = 'price';
			}
			update_option('vstrb_cal_price_mode', $cal_price_mode_input);

			$invoice_prefix_input = sanitize_text_field(wp_unslash($_POST['vstrb_invoice_prefix'] ?? 'INV-'));
			$invoice_prefix = self::sanitize_invoice_prefix($invoice_prefix_input);
			update_option('vstrb_invoice_prefix', $invoice_prefix);

			$invoice_number_format_input = sanitize_text_field(wp_unslash($_POST['vstrb_invoice_number_format'] ?? '{yyyy}{id}'));
			$invoice_number_format = self::sanitize_invoice_number_format($invoice_number_format_input);
			update_option('vstrb_invoice_number_format', $invoice_number_format);

			$invoice_language = self::sanitize_invoice_language(sanitize_text_field(wp_unslash($_POST['vstrb_invoice_language'] ?? '')));
			update_option('vstrb_invoice_language', $invoice_language);

			$supplier_country = self::sanitize_invoice_supplier_country_code(sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_country'] ?? '')));
			update_option('vstrb_invoice_supplier_country', $supplier_country);
			update_option('vstrb_invoice_supplier_name', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_name'] ?? '')));
			update_option('vstrb_invoice_supplier_company_id_label', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_company_id_label'] ?? '')));
			update_option('vstrb_invoice_supplier_company_id', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_company_id'] ?? '')));
			update_option('vstrb_invoice_supplier_vat_id_label', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_vat_id_label'] ?? '')));
			update_option('vstrb_invoice_supplier_vat_id', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_vat_id'] ?? '')));
			update_option('vstrb_invoice_supplier_street', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_street'] ?? '')));
			update_option('vstrb_invoice_supplier_city', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_city'] ?? '')));
			update_option('vstrb_invoice_supplier_zip', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_zip'] ?? '')));
			update_option('vstrb_invoice_supplier_country_name', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_country_name'] ?? '')));
			update_option('vstrb_invoice_supplier_bank_account', sanitize_text_field(wp_unslash($_POST['vstrb_invoice_supplier_bank_account'] ?? '')));
			update_option('vstrb_invoice_supplier_tax_note', sanitize_textarea_field(wp_unslash($_POST['vstrb_invoice_supplier_tax_note'] ?? '')));

			$stripe_publishable_key = sanitize_text_field(wp_unslash($_POST['vstrb_payment_stripe_publishable_key'] ?? ''));
			$stripe_secret_key = sanitize_text_field(wp_unslash($_POST['vstrb_payment_stripe_secret_key'] ?? ''));
			$stripe_webhook_secret = sanitize_text_field(wp_unslash($_POST['vstrb_payment_stripe_webhook_secret'] ?? ''));
			$stripe_success_url = esc_url_raw(wp_unslash($_POST['vstrb_payment_stripe_success_url'] ?? ''));
			$stripe_cancel_url = esc_url_raw(wp_unslash($_POST['vstrb_payment_stripe_cancel_url'] ?? ''));
			$mollie_api_key = sanitize_text_field(wp_unslash($_POST['vstrb_payment_mollie_api_key'] ?? ''));
			$mollie_success_url = esc_url_raw(wp_unslash($_POST['vstrb_payment_mollie_success_url'] ?? ''));
			$bank_details = sanitize_textarea_field(wp_unslash($_POST['vstrb_payment_bank_details'] ?? ''));
			$bank_webhook_token = sanitize_text_field(wp_unslash($_POST['vstrb_payment_bank_webhook_token'] ?? ''));
			$autoconfirm_paid = isset($_POST['vstrb_payment_autoconfirm_on_paid']) ? 1 : 0;
			$include_refundable_deposit = isset($_POST['vstrb_payment_include_refundable_deposit']) ? 1 : 0;

			update_option('vstrb_payment_stripe_publishable_key', $stripe_publishable_key);
			update_option('vstrb_payment_stripe_secret_key', $stripe_secret_key);
			update_option('vstrb_payment_stripe_webhook_secret', $stripe_webhook_secret);
			update_option('vstrb_payment_stripe_success_url', $stripe_success_url);
			update_option('vstrb_payment_stripe_cancel_url', $stripe_cancel_url);
			update_option('vstrb_payment_mollie_api_key', $mollie_api_key);
			update_option('vstrb_payment_mollie_success_url', $mollie_success_url);
			update_option('vstrb_payment_bank_details', $bank_details);
			update_option('vstrb_payment_bank_webhook_token', $bank_webhook_token);
			update_option('vstrb_payment_autoconfirm_on_paid', $autoconfirm_paid);
			update_option('vstrb_payment_include_refundable_deposit', $include_refundable_deposit);

			$notification_types_input = isset($_POST['vstrb_notification_types']) && is_array($_POST['vstrb_notification_types'])
				? map_deep(wp_unslash($_POST['vstrb_notification_types']), 'sanitize_text_field')
				: [];
			VSTRB_Staff_Tasks::save_config(['types' => $notification_types_input]);

			self::bump_services_cache_version();
			echo '<div class="notice notice-success"><p>' . esc_html__('Settings saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}

		$currencies = self::get_available_currencies();
		$base_currency = self::get_base_currency($currencies);
		$rates_string = (string) get_option('vstrb_currency_rates', '');
		$languages = self::get_configured_language_codes();
		$invoice_prefix = self::sanitize_invoice_prefix((string) get_option('vstrb_invoice_prefix', 'INV-'));
		$invoice_number_format = self::sanitize_invoice_number_format((string) get_option('vstrb_invoice_number_format', '{yyyy}{id}'));
		$invoice_language = self::sanitize_invoice_language((string) get_option('vstrb_invoice_language', ''));
		$invoice_supplier = self::get_invoice_supplier_details();
		$stripe_publishable_key = (string) get_option('vstrb_payment_stripe_publishable_key', '');
		$stripe_secret_key = (string) get_option('vstrb_payment_stripe_secret_key', '');
		$stripe_webhook_secret = (string) get_option('vstrb_payment_stripe_webhook_secret', '');
		$stripe_success_url = (string) get_option('vstrb_payment_stripe_success_url', '');
		$stripe_cancel_url = (string) get_option('vstrb_payment_stripe_cancel_url', '');
		$mollie_api_key = (string) get_option('vstrb_payment_mollie_api_key', '');
		$mollie_success_url = (string) get_option('vstrb_payment_mollie_success_url', '');
		$bank_details = (string) get_option('vstrb_payment_bank_details', '');
		$bank_webhook_token = (string) get_option('vstrb_payment_bank_webhook_token', '');
		$autoconfirm_paid = (int) get_option('vstrb_payment_autoconfirm_on_paid', 1);
		$include_refundable_deposit = (int) get_option('vstrb_payment_include_refundable_deposit', 0);
		$stripe_webhook_url = esc_url(rest_url('vstrb/v1/payment/stripe/webhook'));
		$mollie_webhook_url = esc_url(rest_url('vstrb/v1/payment/mollie/webhook'));
		$bank_notify_url = esc_url(rest_url('vstrb/v1/payment/bank-notify'));

		?>
		<form method="post" action="<?php echo esc_url(add_query_arg(['page' => 'vstrb-general', 'tab' => $active_tab], admin_url('admin.php'))); ?>">
			<?php wp_nonce_field('vstrb_general', 'vstrb_general_nonce'); ?>
			<input type="hidden" name="vstrb_settings_tab" value="<?php echo esc_attr($active_tab); ?>">
			<table class="form-table">
			<tbody<?php echo $active_tab === 'general' ? '' : ' style="display:none"'; ?>>
				<tr>
					<th colspan="2" style="padding-top:6px;border-bottom:1px solid #dcdcde"><?php esc_html_e('Localization & Currency', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Available Currencies', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_available_currencies" value="<?php echo esc_attr(implode(',', $currencies)); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Comma separated list, e.g. EUR,CZK', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Available Languages', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_available_languages" value="<?php echo esc_attr(implode(',', $languages)); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Comma separated 2-letter language codes, e.g. cs,en,de,pl.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Base Currency', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<select name="vstrb_base_currency">
							<?php foreach ($currencies as $c) : ?>
								<option value="<?php echo esc_attr($c); ?>" <?php selected($base_currency, $c); ?>><?php echo esc_html($c); ?></option>
							<?php endforeach; ?>
						</select>
						<p class="description"><?php esc_html_e('Reference currency used for automatic conversion.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Currency Rates', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_currency_rates" value="<?php echo esc_attr($rates_string); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Format: EUR=1,USD=1.08,CZK=24.50. Values are relative to base currency.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
			</tbody>
			<tbody<?php echo $active_tab === 'notifications' ? '' : ' style="display:none"'; ?>>
				<tr><th scope="row"><?php esc_html_e('Staff notification automation', 'vachr-short-term-rental-reservations'); ?></th><td><?php VSTRB_Staff_Tasks::render_admin_form(); ?></td></tr>
			</tbody>
			<tbody<?php echo $active_tab === 'invoicing' ? '' : ' style="display:none"'; ?>>
				<tr>
					<th colspan="2" style="padding-top:18px;border-top:1px solid #dcdcde"><?php esc_html_e('Invoicing', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Invoice Prefix', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_invoice_prefix" value="<?php echo esc_attr($invoice_prefix); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Used as {prefix} in invoice number format. Example: INV- or 2026-.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Invoice Number Format', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_invoice_number_format" value="<?php echo esc_attr($invoice_number_format); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Supported placeholders: {prefix}, {id}, {group_id}, {yyyy}, {yy}, {mm}, {dd}, {date}, {date_dash}.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Invoice Language', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<select name="vstrb_invoice_language">
							<option value="" <?php selected($invoice_language, ''); ?>><?php esc_html_e('Site default', 'vachr-short-term-rental-reservations'); ?></option>
							<?php foreach (self::get_invoice_language_options() as $language_code => $language_label) : ?>
								<option value="<?php echo esc_attr($language_code); ?>" <?php selected($invoice_language, $language_code); ?>><?php echo esc_html($language_label); ?></option>
							<?php endforeach; ?>
						</select>
						<p class="description"><?php esc_html_e('Language used for printable invoice pages and PDF output.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Supplier Country', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;max-width:900px">
							<label><?php esc_html_e('Country / Region', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_country_name" value="<?php echo esc_attr($invoice_supplier['country_name']); ?>" style="width:100%" placeholder="<?php esc_attr_e('United States, Germany, Czech Republic...', 'vachr-short-term-rental-reservations'); ?>"></label>
							<label><?php esc_html_e('Country Code', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_country" value="<?php echo esc_attr($invoice_supplier['country']); ?>" maxlength="12" style="width:100%" placeholder="US, DE, CZ, GB"></label>
						</div>
						<p class="description"><?php esc_html_e('Use any country or region. The optional country code is shown only in settings and helps keep records consistent.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Supplier Billing Details', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;max-width:900px">
							<label><?php esc_html_e('Supplier Name', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_name" value="<?php echo esc_attr($invoice_supplier['name']); ?>" style="width:100%"></label>
							<label><?php esc_html_e('Primary Tax ID Label', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_company_id_label" value="<?php echo esc_attr($invoice_supplier['company_id_label']); ?>" style="width:100%" placeholder="<?php esc_attr_e('Business / Tax ID', 'vachr-short-term-rental-reservations'); ?>"></label>
							<label><?php esc_html_e('Primary Tax ID', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_company_id" value="<?php echo esc_attr($invoice_supplier['company_id']); ?>" style="width:100%" placeholder="<?php esc_attr_e('Company ID, EIN, Tax Number...', 'vachr-short-term-rental-reservations'); ?>"></label>
							<label><?php esc_html_e('Secondary Tax ID Label', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_vat_id_label" value="<?php echo esc_attr($invoice_supplier['vat_id_label']); ?>" style="width:100%" placeholder="<?php esc_attr_e('VAT / Sales Tax ID', 'vachr-short-term-rental-reservations'); ?>"></label>
							<label><?php esc_html_e('Secondary Tax ID', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_vat_id" value="<?php echo esc_attr($invoice_supplier['vat_id']); ?>" style="width:100%" placeholder="<?php esc_attr_e('VAT ID, Sales Tax ID...', 'vachr-short-term-rental-reservations'); ?>"></label>
							<label><?php esc_html_e('Street', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_street" value="<?php echo esc_attr($invoice_supplier['street']); ?>" style="width:100%"></label>
							<label><?php esc_html_e('City', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_city" value="<?php echo esc_attr($invoice_supplier['city']); ?>" style="width:100%"></label>
							<label><?php esc_html_e('ZIP Code', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_zip" value="<?php echo esc_attr($invoice_supplier['zip']); ?>" style="width:100%"></label>
							<label><?php esc_html_e('IBAN', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_invoice_supplier_bank_account" value="<?php echo esc_attr($invoice_supplier['bank_account']); ?>" style="width:100%" placeholder="CZ65 0800 0000 1920 0014 5399"></label>
						</div>
						<p class="description"><?php esc_html_e('Enter the bank account in IBAN format. For a Czech IBAN beginning with CZ, a QR payment code is shown automatically for CZK payments.', 'vachr-short-term-rental-reservations'); ?></p>
						<p><label><?php esc_html_e('Invoice Tax Note', 'vachr-short-term-rental-reservations'); ?><br><textarea name="vstrb_invoice_supplier_tax_note" rows="3" class="large-text"><?php echo esc_textarea($invoice_supplier['tax_note']); ?></textarea></label></p>
					</td>
				</tr>
			</tbody>
			<tbody<?php echo $active_tab === 'payments' ? '' : ' style="display:none"'; ?>>
				<tr>
					<th colspan="2" style="padding-top:18px;border-top:1px solid #dcdcde"><?php esc_html_e('Payments & Invoices', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<td colspan="2" style="padding-top:0">
						<div style="background:#fff;border:1px solid #ccd0d4;padding:12px;margin:0 0 10px 0">
							<strong><?php esc_html_e('Stripe Setup Guide', 'vachr-short-term-rental-reservations'); ?></strong>
							<ol style="margin:8px 0 0 20px">
								<li><?php esc_html_e('Open Stripe Dashboard -> Developers -> API keys and copy Publishable key (pk_...) and Secret key (sk_...).', 'vachr-short-term-rental-reservations'); ?></li>
								<li><?php esc_html_e('Paste keys below and save this page.', 'vachr-short-term-rental-reservations'); ?></li>
								<li><?php esc_html_e('Open Stripe Dashboard -> Developers -> Webhooks -> Add endpoint.', 'vachr-short-term-rental-reservations'); ?></li>
								<li><?php esc_html_e('Endpoint URL:', 'vachr-short-term-rental-reservations'); ?> <code><?php echo esc_html($stripe_webhook_url); ?></code></li>
								<li><?php esc_html_e('Select event: checkout.session.completed', 'vachr-short-term-rental-reservations'); ?></li>
								<li><?php esc_html_e('Copy Signing secret (whsec_...) from Stripe webhook details and paste it below.', 'vachr-short-term-rental-reservations'); ?></li>
							</ol>
						</div>
					</td>
				</tr>
				<tr>
					<th colspan="2" style="padding-top:10px"><?php esc_html_e('Stripe Gateway', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Stripe Publishable Key', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_payment_stripe_publishable_key" value="<?php echo esc_attr($stripe_publishable_key); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Public key from Stripe (pk_...).', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Stripe Secret Key', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_payment_stripe_secret_key" value="<?php echo esc_attr($stripe_secret_key); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Secret key from Stripe (sk_...).', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Stripe Webhook Secret', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_payment_stripe_webhook_secret" value="<?php echo esc_attr($stripe_webhook_secret); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Signing secret from Stripe webhook endpoint (whsec_...).', 'vachr-short-term-rental-reservations'); ?></p>
						<p><strong><?php esc_html_e('Webhook URL', 'vachr-short-term-rental-reservations'); ?>:</strong> <code><?php echo esc_html($stripe_webhook_url); ?></code></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Stripe Success URL', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="url" name="vstrb_payment_stripe_success_url" value="<?php echo esc_attr($stripe_success_url); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Optional. Leave empty to use current booking page URL.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Stripe Cancel URL', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="url" name="vstrb_payment_stripe_cancel_url" value="<?php echo esc_attr($stripe_cancel_url); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Optional. Leave empty to use current booking page URL.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th colspan="2" style="padding-top:12px"><?php esc_html_e('Mollie Gateway', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Mollie API Key', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_payment_mollie_api_key" value="<?php echo esc_attr($mollie_api_key); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('API key from Mollie Dashboard (test_... or live_...).', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Mollie Success URL', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="url" name="vstrb_payment_mollie_success_url" value="<?php echo esc_attr($mollie_success_url); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Optional. Leave empty to use current booking page URL.', 'vachr-short-term-rental-reservations'); ?></p>
						<p><strong><?php esc_html_e('Mollie Webhook URL', 'vachr-short-term-rental-reservations'); ?>:</strong> <code><?php echo esc_html($mollie_webhook_url); ?></code></p>
					</td>
				</tr>
				<tr>
					<th colspan="2" style="padding-top:12px"><?php esc_html_e('Bank Transfer', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Bank Transfer Instructions', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<textarea name="vstrb_payment_bank_details" rows="4" class="large-text"><?php echo esc_textarea($bank_details); ?></textarea>
						<p class="description"><?php esc_html_e('Shown after bank transfer booking submission (IBAN, variable symbol, due date, etc.).', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Bank Webhook Token', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<input type="text" name="vstrb_payment_bank_webhook_token" value="<?php echo esc_attr($bank_webhook_token); ?>" class="regular-text">
						<p class="description"><?php esc_html_e('Shared secret token for bank callback endpoint.', 'vachr-short-term-rental-reservations'); ?></p>
						<p><strong><?php esc_html_e('Bank Notify URL', 'vachr-short-term-rental-reservations'); ?>:</strong> <code><?php echo esc_html($bank_notify_url); ?></code></p>
					</td>
				</tr>
				<tr>
					<th colspan="2" style="padding-top:12px"><?php esc_html_e('Booking Behavior', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Auto-confirm Booking After Payment', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<label><input type="checkbox" name="vstrb_payment_autoconfirm_on_paid" value="1" <?php checked($autoconfirm_paid, 1); ?>> <?php esc_html_e('Automatically set booking status to confirmed when payment is matched.', 'vachr-short-term-rental-reservations'); ?></label>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Include Refundable Deposit in Online Payment', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<label><input type="checkbox" name="vstrb_payment_include_refundable_deposit" value="1" <?php checked($include_refundable_deposit, 1); ?>> <?php esc_html_e('When enabled, card and bank transfer payments are charged including refundable deposit.', 'vachr-short-term-rental-reservations'); ?></label>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Booking Form Payment Shortcode', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<code>[vstrb_booking_form unit="123" enable_stripe="1" enable_mollie="1" enable_bank_transfer="1" enable_cash="1"]</code>
						<p class="description"><?php esc_html_e('Use shortcode attributes to enable/disable payment methods per form.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
			</tbody>
			<tbody<?php echo $active_tab === 'calendar' ? '' : ' style="display:none"'; ?>>
				<tr>
					<th colspan="2" style="padding-top:18px;border-top:1px solid #dcdcde"><?php esc_html_e('Calendar', 'vachr-short-term-rental-reservations'); ?></th>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e('Calendar Price Display', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<?php $cal_price_mode_current = (string) get_option('vstrb_cal_price_mode', 'price'); ?>
						<select name="vstrb_cal_price_mode">
							<option value="off" <?php selected($cal_price_mode_current, 'off'); ?>><?php esc_html_e('Hidden', 'vachr-short-term-rental-reservations'); ?></option>
							<option value="price" <?php selected($cal_price_mode_current, 'price'); ?>><?php esc_html_e('Show nightly price (default)', 'vachr-short-term-rental-reservations'); ?></option>
							<option value="diff" <?php selected($cal_price_mode_current, 'diff'); ?>><?php esc_html_e('Show discount / surcharge only', 'vachr-short-term-rental-reservations'); ?></option>
						</select>
						<p class="description"><?php esc_html_e('Controls what is shown in each calendar day cell. Per-apartment override is available in the apartment editor.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
			</tbody>
		</table>
			<p class="submit">
				<button type="submit" name="vstrb_save_general" class="button button-primary"><?php esc_html_e('Save Changes', 'vachr-short-term-rental-reservations'); ?></button>
			</p>
		</form>
		</div>
		<?php
		self::render_donation_box();
	}

	public static function page_payments() {
		wp_safe_redirect(add_query_arg(['page' => 'vstrb-general', 'tab' => 'payments'], admin_url('admin.php')));
		exit;
	}

	private static function sanitize_unit_content_templates_from_post() {
		$posted = self::post_array('vstrb_content_templates');
		$langs = array_keys(self::get_language_options(false));
		$out = [];
		foreach ($posted as $idx => $row) {
			if (!is_array($row)) {
				continue;
			}
			$id = sanitize_key($row['id'] ?? '');
			if ($id === '') {
				$id = 'template_' . (int) $idx;
			}
			$title = sanitize_text_field((string) ($row['title'] ?? ''));
			if ($title === '') {
				continue;
			}
			$out[$id] = [
				'title' => $title,
				'description' => [],
				'amenities' => [],
				'instructions' => [],
				'bank_transfer_instructions' => [],
			];
			foreach (['description', 'amenities', 'instructions', 'bank_transfer_instructions'] as $field) {
				$values = isset($row[$field]) && is_array($row[$field]) ? $row[$field] : [];
				foreach ($values as $lang => $value) {
					$lang = self::normalize_language_code($lang);
					if ($lang === '' || !in_array($lang, $langs, true) || is_array($value)) {
						continue;
					}
					$text = wp_kses_post(html_entity_decode((string) $value, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
					if (trim(wp_strip_all_tags($text)) !== '') {
						$out[$id][$field][$lang] = $text;
					}
				}
			}
		}
		return $out;
	}

	private static function render_template_i18n_cells($idx, $field, $values, $rows = 3) {
		$values = is_array($values) ? $values : [];
		foreach (self::get_language_options(false) as $lang => $label) {
			echo '<label style="display:block;margin:6px 0"><strong>' . esc_html($label) . '</strong><br>';
			echo '<textarea name="vstrb_content_templates[' . esc_attr($idx) . '][' . esc_attr($field) . '][' . esc_attr($lang) . ']" rows="' . (int) $rows . '" style="width:100%">' . esc_textarea($values[$lang] ?? '') . '</textarea>';
			echo '</label>';
		}
	}

	public static function page_unit_content() {
		echo '<div class="wrap"><h1>' . esc_html__('Apartment Content Templates', 'vachr-short-term-rental-reservations') . '</h1>';
		if (isset($_POST['vstrb_save_unit_content'])) {
			check_admin_referer('vstrb_unit_content', 'vstrb_unit_content_nonce');
			update_option('vstrb_unit_content_templates', self::sanitize_unit_content_templates_from_post());
			echo '<div class="notice notice-success"><p>' . esc_html__('Templates saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}

		$templates = get_option('vstrb_unit_content_templates', []);
		$templates = is_array($templates) ? $templates : [];
		if (!$templates) {
			$templates = [
				'default' => [
					'title' => __('Default template', 'vachr-short-term-rental-reservations'),
					'description' => [],
					'amenities' => [],
					'instructions' => [],
					'bank_transfer_instructions' => [],
				],
			];
		}
		?>
		<div style="background:#fff;border:1px solid #ccd0d4;border-left:4px solid #2271b1;padding:14px 16px;margin:12px 0 18px 0;max-width:1100px">
			<h2 style="margin:0 0 8px 0;font-size:16px"><?php esc_html_e('How to use apartment content templates', 'vachr-short-term-rental-reservations'); ?></h2>
			<ol style="margin:8px 0 0 20px">
				<li><?php esc_html_e('Create one or more reusable templates below for descriptions, amenities, arrival instructions, and bank transfer instructions.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('Use {{iban}} for the international account format, {{account_number}} or {{bank_account}} for the domestic account number, and {{account_country}} for the two-letter country code.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('Payment templates also support {{invoice_number}}, {{variable_symbol}}, {{payment_reference}}, {{payment_amount}}, {{payment_amount_due}}, and {{payment_currency}}. Short aliases {{amount}} and {{currency}} are available.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('Open an apartment and assign the template in the Guest-facing Apartment Content section.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('If one apartment needs different wording, fill an apartment-specific language override there. Overrides have priority over the template.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('For private values such as door PINs or Wi-Fi passwords, define custom placeholders in the apartment editor, not in the shared template.', 'vachr-short-term-rental-reservations'); ?></li>
			</ol>
			<p style="margin:10px 0 0 0">
				<a class="button button-secondary" href="<?php echo esc_url(admin_url('edit.php?post_type=vstrb_unit')); ?>"><?php esc_html_e('Open Apartments', 'vachr-short-term-rental-reservations'); ?></a>
				<span style="color:#666;margin-left:8px"><?php esc_html_e('Templates are shared; apartment placeholders and overrides are edited on each apartment.', 'vachr-short-term-rental-reservations'); ?></span>
			</p>
		</div>
		<p><?php esc_html_e('Templates can be assigned to multiple apartments. Apartment-specific multilingual text overrides the selected template.', 'vachr-short-term-rental-reservations'); ?></p>
		<form method="post" action="<?php echo esc_url(admin_url('admin.php?page=vstrb-unit-content')); ?>">
			<?php wp_nonce_field('vstrb_unit_content', 'vstrb_unit_content_nonce'); ?>
			<div id="vstrb-content-templates">
				<?php $idx = 0; foreach ($templates as $template_id => $template): ?>
					<div class="vstrb-content-template" style="background:#fff;border:1px solid #ccd0d4;padding:14px;margin:0 0 16px 0">
						<div style="display:grid;grid-template-columns:minmax(160px,220px) 1fr auto;gap:10px;align-items:end">
							<label><?php esc_html_e('Template ID', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_content_templates[<?php echo (int) $idx; ?>][id]" value="<?php echo esc_attr($template_id); ?>" style="width:100%"></label>
							<label><?php esc_html_e('Template Name', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="vstrb_content_templates[<?php echo (int) $idx; ?>][title]" value="<?php echo esc_attr((string) ($template['title'] ?? '')); ?>" style="width:100%"></label>
							<button type="button" class="button" onclick="this.closest('.vstrb-content-template').remove()"><?php esc_html_e('Remove', 'vachr-short-term-rental-reservations'); ?></button>
						</div>
						<details open style="margin-top:10px"><summary><strong><?php esc_html_e('Description', 'vachr-short-term-rental-reservations'); ?></strong></summary><?php self::render_template_i18n_cells($idx, 'description', $template['description'] ?? [], 4); ?></details>
						<details style="margin-top:10px"><summary><strong><?php esc_html_e('Amenities', 'vachr-short-term-rental-reservations'); ?></strong></summary><?php self::render_template_i18n_cells($idx, 'amenities', $template['amenities'] ?? [], 3); ?></details>
						<details style="margin-top:10px"><summary><strong><?php esc_html_e('Arrival Instructions', 'vachr-short-term-rental-reservations'); ?></strong></summary><?php self::render_template_i18n_cells($idx, 'instructions', $template['instructions'] ?? [], 5); ?></details>
						<details style="margin-top:10px"><summary><strong><?php esc_html_e('Bank Transfer Instructions', 'vachr-short-term-rental-reservations'); ?></strong></summary><?php self::render_template_i18n_cells($idx, 'bank_transfer_instructions', $template['bank_transfer_instructions'] ?? [], 5); ?></details>
					</div>
				<?php $idx++; endforeach; ?>
			</div>
			<p>
				<button type="button" class="button" onclick="vstrbAddContentTemplate()"><?php esc_html_e('Add Template', 'vachr-short-term-rental-reservations'); ?></button>
				<button type="submit" name="vstrb_save_unit_content" class="button button-primary"><?php esc_html_e('Save Templates', 'vachr-short-term-rental-reservations'); ?></button>
			</p>
		</form>
		</div>
		<?php
		$langs_json = wp_json_encode(self::get_language_options(false));
		self::add_inline_admin_script("
			window.vstrbAddContentTemplate = function() {
				const wrap = document.getElementById('vstrb-content-templates');
				if (!wrap) return;
				const idx = Date.now();
				const langs = {$langs_json} || {};
				const langAreas = function(field, rows) {
					return Object.entries(langs).map(function(entry) {
						return '<label style=\"display:block;margin:6px 0\"><strong>' + entry[1] + '</strong><br><textarea name=\"vstrb_content_templates[' + idx + '][' + field + '][' + entry[0] + ']\" rows=\"' + rows + '\" style=\"width:100%\"></textarea></label>';
					}).join('');
				};
				const div = document.createElement('div');
				div.className = 'vstrb-content-template';
				div.style = 'background:#fff;border:1px solid #ccd0d4;padding:14px;margin:0 0 16px 0';
				div.innerHTML = '<div style=\"display:grid;grid-template-columns:minmax(160px,220px) 1fr auto;gap:10px;align-items:end\"><label>" . esc_js(__('Template ID', 'vachr-short-term-rental-reservations')) . "<br><input type=\"text\" name=\"vstrb_content_templates[' + idx + '][id]\" value=\"template_' + idx + '\" style=\"width:100%\"></label><label>" . esc_js(__('Template Name', 'vachr-short-term-rental-reservations')) . "<br><input type=\"text\" name=\"vstrb_content_templates[' + idx + '][title]\" value=\"\" style=\"width:100%\"></label><button type=\"button\" class=\"button\" onclick=\"this.closest(\\'.vstrb-content-template\\').remove()\">" . esc_js(__('Remove', 'vachr-short-term-rental-reservations')) . "</button></div>' +
					'<details open style=\"margin-top:10px\"><summary><strong>" . esc_js(__('Description', 'vachr-short-term-rental-reservations')) . "</strong></summary>' + langAreas('description', 4) + '</details>' +
					'<details style=\"margin-top:10px\"><summary><strong>" . esc_js(__('Amenities', 'vachr-short-term-rental-reservations')) . "</strong></summary>' + langAreas('amenities', 3) + '</details>' +
					'<details style=\"margin-top:10px\"><summary><strong>" . esc_js(__('Arrival Instructions', 'vachr-short-term-rental-reservations')) . "</strong></summary>' + langAreas('instructions', 5) + '</details>' +
					'<details style=\"margin-top:10px\"><summary><strong>" . esc_js(__('Bank Transfer Instructions', 'vachr-short-term-rental-reservations')) . "</strong></summary>' + langAreas('bank_transfer_instructions', 5) + '</details>';
				wrap.appendChild(div);
			};
		");
		self::render_donation_box();
	}

	private static function render_booking_invoice_page($invoice_id) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$invoice_id = (int) $invoice_id;
		$row = $wpdb->get_row($wpdb->prepare(
			"SELECT i.*, b.date_from, b.date_to, b.guests, b.customer_name, b.customer_lastname, b.customer_email, b.unit_id, u.post_title AS unit_title
			 FROM {$t['invoices']} i
			 LEFT JOIN {$t['bookings']} b ON b.id = i.booking_id
			 LEFT JOIN {$wpdb->posts} u ON u.ID = b.unit_id
			 WHERE i.id=%d",
			$invoice_id
		), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

		if (!$row) {
			wp_die(esc_html__('Invoice not found.', 'vachr-short-term-rental-reservations'));
		}

		self::switch_to_invoice_locale((string) ($row['invoice_language'] ?? ''));
		$supplier = self::get_invoice_supplier_details();
		$customer_name = trim((string) ($row['billing_name'] ?? ''));
		if ($customer_name === '') {
			$customer_name = trim((string) ($row['customer_name'] ?? '') . ' ' . (string) ($row['customer_lastname'] ?? ''));
		}
		$customer = [
			'name' => $customer_name,
			'company_id' => (string) ($row['billing_company_id'] ?? ''),
			'vat_id' => (string) ($row['billing_vat_id'] ?? ''),
			'street' => (string) ($row['billing_street'] ?? ''),
			'city' => (string) ($row['billing_city'] ?? ''),
			'zip' => (string) ($row['billing_zip'] ?? ''),
			'country_name' => (string) ($row['billing_country'] ?? ''),
		];
		$invoice_number = (string) ($row['invoice_number'] ?? '');
		$currency = (string) ($row['currency'] ?? 'EUR');
		$total = (float) ($row['total_amount'] ?? 0);
		$payment_amount_due = (float) self::booking_meta_value((int) ($row['booking_id'] ?? 0), 'payment_amount_due', (string) $total);
		if ($payment_amount_due <= 0) {
			$payment_amount_due = $total;
		}
		$issued_at = !empty($row['issued_at']) ? date_i18n('Y-m-d', strtotime((string) $row['issued_at'])) : date_i18n('Y-m-d');
		$unit_title = (string) ($row['unit_title'] ?: '#' . (int) ($row['unit_id'] ?? 0));
		$payment_qr_payload = VSTRB_Payment_QR::payload(
			$payment_amount_due,
			$currency,
			$invoice_number
		);

		nocache_headers();
		header('Content-Type: text/html; charset=' . get_option('blog_charset'));
		?>
		<!doctype html>
		<html>
		<head>
			<meta charset="<?php echo esc_attr(get_option('blog_charset')); ?>">
			<title><?php echo esc_html(self::invoice_text('Invoice')); ?> - <?php echo esc_html($invoice_number); ?></title>
			<style>
				body{font-family:Arial,sans-serif;color:#1d2327;margin:20px}
				.vstrb-invoice-wrap{max-width:900px;margin:0 auto}
				.vstrb-invoice-actions{display:flex;gap:8px;justify-content:flex-end;margin-bottom:16px}
				.vstrb-invoice-card{border:1px solid #dcdcde;padding:20px}
				.vstrb-invoice-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:16px}
				.vstrb-invoice-table{width:100%;border-collapse:collapse;margin-top:20px}
				.vstrb-invoice-table th,.vstrb-invoice-table td{border:1px solid #dcdcde;padding:8px;text-align:left}
				@media print {.vstrb-invoice-actions{display:none} body{margin:0}}
			</style>
		</head>
		<body>
			<div class="vstrb-invoice-wrap">
				<div class="vstrb-invoice-actions">
					<button type="button" onclick="window.print()"><?php esc_html_e('Print Invoice', 'vachr-short-term-rental-reservations'); ?></button>
					<button type="button" onclick="window.print()"><?php esc_html_e('Save as PDF', 'vachr-short-term-rental-reservations'); ?></button>
					<button type="button" onclick="window.close()"><?php esc_html_e('Close', 'vachr-short-term-rental-reservations'); ?></button>
				</div>
				<div class="vstrb-invoice-card">
					<h1 style="margin:0"><?php echo esc_html(self::invoice_text('Invoice')); ?></h1>
					<p style="margin:6px 0 0 0"><?php echo esc_html($invoice_number); ?></p>
					<div class="vstrb-invoice-grid">
						<div><?php self::render_invoice_party_block(self::invoice_text('Supplier'), $supplier); ?></div>
						<div><?php self::render_invoice_party_block(self::invoice_text('Customer'), $customer); ?></div>
					</div>
					<div class="vstrb-invoice-grid">
						<div><strong><?php echo esc_html(self::invoice_text('Issue Date')); ?>:</strong> <?php echo esc_html($issued_at); ?></div>
						<div><strong><?php echo esc_html(self::invoice_text('Stay')); ?>:</strong> <?php echo esc_html((string) ($row['date_from'] ?? '') . ' - ' . (string) ($row['date_to'] ?? '')); ?></div>
					</div>
					<table class="vstrb-invoice-table">
						<thead><tr><th><?php echo esc_html(self::invoice_text('Description')); ?></th><th><?php echo esc_html(self::invoice_text('Quantity')); ?></th><th><?php echo esc_html(self::invoice_text('Unit Price')); ?></th><th><?php echo esc_html(self::invoice_text('Total')); ?></th></tr></thead>
						<tbody><tr><td><?php echo esc_html($unit_title); ?></td><td>1</td><td><?php echo esc_html(number_format_i18n($total, 2) . ' ' . $currency); ?></td><td><?php echo esc_html(number_format_i18n($total, 2) . ' ' . $currency); ?></td></tr></tbody>
					</table>
					<?php VSTRB_Payment_QR::render($payment_qr_payload); ?>
					<?php if (!empty($supplier['tax_note'])): ?>
						<p style="margin-top:14px;color:#50575e"><?php echo nl2br(esc_html((string) $supplier['tax_note'])); ?></p>
					<?php endif; ?>
				</div>
			</div>
			<?php if ($payment_qr_payload !== '') VSTRB_Payment_QR::render_assets(); ?>
		</body>
		</html>
		<?php
		exit;
	}

	public static function page_transactions() {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$invoice_table = $t['invoices'] ?? '';
		$payment_table = $t['payments'] ?? '';
		if ($invoice_table === '' || $payment_table === '') {
			echo '<div class="wrap"><h1>' . esc_html__('Invoices & Transactions', 'vachr-short-term-rental-reservations') . '</h1>';
			echo '<div class="notice notice-error"><p>' . esc_html__('Invoice/payment tables are not available.', 'vachr-short-term-rental-reservations') . '</p></div></div>';
			return;
		}

		$action = self::get_param_text('action', '');
		$id = self::get_param_int('id', 0);
		$nonce = self::get_param_text('_wpnonce', '');
		$edit_invoice = null;

		if (isset($_POST['vstrb_save_invoice'])) {
			check_admin_referer('vstrb_save_invoice', 'vstrb_save_invoice_nonce');
			$invoice_id = isset($_POST['invoice_id']) ? (int) sanitize_text_field(wp_unslash($_POST['invoice_id'])) : 0;
			if ($invoice_id <= 0) {
				echo '<div class="notice notice-error"><p>' . esc_html__('Invoice not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$current = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$invoice_table} WHERE id=%d", $invoice_id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				if (!$current) {
					echo '<div class="notice notice-error"><p>' . esc_html__('Invoice not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
				} else {
					$status = sanitize_text_field(wp_unslash($_POST['invoice_status'] ?? (string) $current['status']));
					if (!in_array($status, ['issued', 'paid', 'cancelled'], true)) {
						$status = 'issued';
					}
					$total_amount = self::parse_decimal_input(sanitize_text_field(wp_unslash($_POST['invoice_total_amount'] ?? (string) $current['total_amount'])), (float) $current['total_amount']);
					$currency = strtoupper(sanitize_text_field(wp_unslash($_POST['invoice_currency'] ?? (string) $current['currency'])));
					if (!preg_match('/^[A-Z]{3}$/', $currency)) {
						$currency = strtoupper((string) $current['currency']);
					}
					$invoice_number = sanitize_text_field(wp_unslash($_POST['invoice_number'] ?? (string) $current['invoice_number']));
					$invoice_language = self::sanitize_invoice_language(sanitize_text_field(wp_unslash($_POST['invoice_language'] ?? (string) ($current['invoice_language'] ?? ''))));
					$billing_enabled = isset($_POST['invoice_billing_enabled']) ? 1 : 0;
					$billing_name = sanitize_text_field(wp_unslash($_POST['invoice_billing_name'] ?? ''));
					$billing_company_id = sanitize_text_field(wp_unslash($_POST['invoice_billing_company_id'] ?? ''));
					$billing_vat_id = sanitize_text_field(wp_unslash($_POST['invoice_billing_vat_id'] ?? ''));
					$billing_street = sanitize_text_field(wp_unslash($_POST['invoice_billing_street'] ?? ''));
					$billing_city = sanitize_text_field(wp_unslash($_POST['invoice_billing_city'] ?? ''));
					$billing_zip = sanitize_text_field(wp_unslash($_POST['invoice_billing_zip'] ?? ''));
					$billing_country = sanitize_text_field(wp_unslash($_POST['invoice_billing_country'] ?? ''));
					$paid_at_input = sanitize_text_field(wp_unslash($_POST['invoice_paid_at'] ?? ''));
					$paid_at = '';
					if ($status === 'paid') {
						$paid_at = ($paid_at_input !== '') ? $paid_at_input : current_time('mysql');
					}

					$update_data = [
						'invoice_number' => $invoice_number,
						'status' => $status,
						'invoice_language' => $invoice_language,
						'total_amount' => number_format((float) $total_amount, 2, '.', ''),
						'currency' => $currency,
						'billing_enabled' => $billing_enabled,
						'billing_name' => $billing_enabled ? $billing_name : '',
						'billing_company_id' => $billing_enabled ? $billing_company_id : '',
						'billing_vat_id' => $billing_enabled ? $billing_vat_id : '',
						'billing_street' => $billing_enabled ? $billing_street : '',
						'billing_city' => $billing_enabled ? $billing_city : '',
						'billing_zip' => $billing_enabled ? $billing_zip : '',
						'billing_country' => $billing_enabled ? $billing_country : '',
						'paid_at' => ($status === 'paid' ? $paid_at : null),
					];

					$updated = $wpdb->update($invoice_table, $update_data, ['id' => $invoice_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
					if ($updated === false) {
						echo '<div class="notice notice-error"><p>' . esc_html__('Unable to save invoice.', 'vachr-short-term-rental-reservations') . '</p></div>';
					} else {
						$booking_id = (int) ($current['booking_id'] ?? 0);
						if ($booking_id > 0) {
							self::booking_meta_upsert($booking_id, 'invoice_number', $invoice_number);
							self::booking_meta_upsert($booking_id, 'invoice_status', $status);
							self::booking_meta_upsert($booking_id, 'invoice_language', $invoice_language);
							self::booking_meta_upsert($booking_id, 'invoice_total', number_format((float) $total_amount, 2, '.', ''));
							self::booking_meta_upsert($booking_id, 'invoice_currency', $currency);
							self::booking_meta_upsert($booking_id, 'billing_enabled', (string) $billing_enabled);
							self::booking_meta_upsert($booking_id, 'billing_name', $billing_enabled ? $billing_name : '');
							self::booking_meta_upsert($booking_id, 'billing_company_id', $billing_enabled ? $billing_company_id : '');
							self::booking_meta_upsert($booking_id, 'billing_vat_id', $billing_enabled ? $billing_vat_id : '');
							self::booking_meta_upsert($booking_id, 'billing_street', $billing_enabled ? $billing_street : '');
							self::booking_meta_upsert($booking_id, 'billing_city', $billing_enabled ? $billing_city : '');
							self::booking_meta_upsert($booking_id, 'billing_zip', $billing_enabled ? $billing_zip : '');
							self::booking_meta_upsert($booking_id, 'billing_country', $billing_enabled ? $billing_country : '');
							if ($status === 'paid') {
								self::booking_meta_upsert($booking_id, 'payment_status', 'paid');
								self::booking_meta_upsert($booking_id, 'payment_paid_at', $paid_at);
								self::booking_meta_upsert($booking_id, 'invoice_paid_at', $paid_at);
								$autoconfirm = (int) get_option('vstrb_payment_autoconfirm_on_paid', 1);
								if ($autoconfirm === 1) {
									$wpdb->update($t['bookings'], ['status' => 'confirmed'], ['id' => $booking_id], ['%s'], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
								}
							}

							$payment_row_id = (int) $wpdb->get_var($wpdb->prepare(
								"SELECT id FROM {$payment_table} WHERE booking_id=%d ORDER BY id DESC LIMIT 1",
								$booking_id
							));
							if ($payment_row_id > 0) {
								$payment_status = ($status === 'paid') ? 'paid' : (($status === 'cancelled') ? 'cancelled' : 'pending');
								$wpdb->update(
									$payment_table,
									[
										'invoice_id' => $invoice_id,
										'status' => $payment_status,
										'amount' => number_format((float) $total_amount, 2, '.', ''),
										'currency' => $currency,
										'paid_at' => ($status === 'paid' ? $paid_at : null),
									],
									['id' => $payment_row_id]
								); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
							}
						}
						echo '<div class="notice notice-success"><p>' . esc_html__('Invoice updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
					}
				}
			}
		}

		if ($action === 'edit_invoice' && $id > 0 && $nonce !== '' && wp_verify_nonce($nonce, 'vstrb_edit_invoice_' . $id)) {
			$edit_invoice = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$invoice_table} WHERE id=%d", $id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		}

		echo '<div class="wrap"><h1>' . esc_html__('Invoices & Transactions', 'vachr-short-term-rental-reservations') . '</h1>';

		if ($edit_invoice) {
			$edit_action_url = add_query_arg(
				['page' => 'vstrb-transactions', 'action' => 'edit_invoice', 'id' => (int) $edit_invoice['id']],
				admin_url('admin.php')
			);
			$cancel_url = admin_url('admin.php?page=vstrb-transactions');
			echo '<form method="post" action="' . esc_url($edit_action_url) . '" style="background:#fff;padding:15px;margin:15px 0;border:1px solid #ccd0d4">';
			wp_nonce_field('vstrb_save_invoice', 'vstrb_save_invoice_nonce');
			echo '<input type="hidden" name="invoice_id" value="' . (int) $edit_invoice['id'] . '">';
			echo '<h3 style="margin-top:0;margin-bottom:10px">' . esc_html__('Edit Invoice', 'vachr-short-term-rental-reservations') . ' #' . (int) $edit_invoice['id'] . '</h3>';
			echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px">';
			echo '<div><label>' . esc_html__('Booking ID', 'vachr-short-term-rental-reservations') . '<br><input type="text" value="' . (int) ($edit_invoice['booking_id'] ?? 0) . '" readonly style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Invoice Number', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_number" value="' . esc_attr((string) ($edit_invoice['invoice_number'] ?? '')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Status', 'vachr-short-term-rental-reservations') . '<br><select name="invoice_status" style="width:100%">';
			foreach (['issued', 'paid', 'cancelled'] as $st) {
				printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($st), selected((string) ($edit_invoice['status'] ?? 'issued'), $st, false), esc_html(self::admin_status_label($st)));
			}
			echo '</select></label></div>';
			echo '<div><label>' . esc_html__('Invoice Language', 'vachr-short-term-rental-reservations') . '<br><select name="invoice_language" style="width:100%">';
			$edit_invoice_language = self::sanitize_invoice_language((string) ($edit_invoice['invoice_language'] ?? ''));
			echo '<option value="" ' . selected($edit_invoice_language, '', false) . '>' . esc_html__('Default invoice language', 'vachr-short-term-rental-reservations') . '</option>';
			foreach (self::get_invoice_language_options() as $language_code => $language_label) {
				printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($language_code), selected($edit_invoice_language, $language_code, false), esc_html($language_label));
			}
			echo '</select></label></div>';
			echo '<div><label>' . esc_html__('Total Amount', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_total_amount" value="' . esc_attr((string) ($edit_invoice['total_amount'] ?? '0.00')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Currency', 'vachr-short-term-rental-reservations') . '<br><input type="text" maxlength="3" name="invoice_currency" value="' . esc_attr((string) ($edit_invoice['currency'] ?? 'EUR')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Paid At', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_paid_at" value="' . esc_attr((string) ($edit_invoice['paid_at'] ?? '')) . '" placeholder="YYYY-MM-DD HH:MM:SS" style="width:100%"></label></div>';
			echo '</div>';
			echo '<p><label><input type="checkbox" name="invoice_billing_enabled" value="1" ' . checked(!empty($edit_invoice['billing_enabled']), true, false) . '> ' . esc_html__('Billing enabled', 'vachr-short-term-rental-reservations') . '</label></p>';
			echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px">';
			echo '<div><label>' . esc_html__('Billing Name', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_billing_name" value="' . esc_attr((string) ($edit_invoice['billing_name'] ?? '')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Company ID', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_billing_company_id" value="' . esc_attr((string) ($edit_invoice['billing_company_id'] ?? '')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('VAT ID', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_billing_vat_id" value="' . esc_attr((string) ($edit_invoice['billing_vat_id'] ?? '')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Billing Street', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_billing_street" value="' . esc_attr((string) ($edit_invoice['billing_street'] ?? '')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Billing City', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_billing_city" value="' . esc_attr((string) ($edit_invoice['billing_city'] ?? '')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('ZIP Code', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_billing_zip" value="' . esc_attr((string) ($edit_invoice['billing_zip'] ?? '')) . '" style="width:100%"></label></div>';
			echo '<div><label>' . esc_html__('Billing Country', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="invoice_billing_country" value="' . esc_attr((string) ($edit_invoice['billing_country'] ?? '')) . '" style="width:100%"></label></div>';
			echo '</div>';
			echo '<p style="margin-bottom:0"><button type="submit" name="vstrb_save_invoice" class="button button-primary">' . esc_html__('Save Invoice', 'vachr-short-term-rental-reservations') . '</button> <a class="button" href="' . esc_url($cancel_url) . '">' . esc_html__('Cancel', 'vachr-short-term-rental-reservations') . '</a></p>';
			echo '</form>';
		}

		$filter_status = self::get_param_text('filter_status', '');
		$filter_payment_method = self::get_param_text('filter_payment_method', '');
		$filter_booking_id = self::get_param_int('filter_booking_id', 0);
		$per_page = self::get_param_int('per_page', 50);
		if ($per_page < 10) {
			$per_page = 50;
		}
		$paged = max(1, self::get_param_int('paged', 1));
		$offset = ($paged - 1) * $per_page;

		echo '<form method="get" style="background:#fff;padding:15px;margin:15px 0;border:1px solid #ccd0d4">';
		echo '<input type="hidden" name="page" value="vstrb-transactions">';
		echo '<h3 style="margin-top:0">' . esc_html__('Filters', 'vachr-short-term-rental-reservations') . '</h3>';
		echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin-bottom:10px">';
		echo '<div><label>' . esc_html__('Invoice Status', 'vachr-short-term-rental-reservations') . '<br><select name="filter_status" style="width:100%">';
		echo '<option value="">' . esc_html__('All statuses', 'vachr-short-term-rental-reservations') . '</option>';
		foreach (['issued', 'paid', 'cancelled'] as $st) {
			printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($st), selected($filter_status, $st, false), esc_html(self::admin_status_label($st)));
		}
		echo '</select></label></div>';
		echo '<div><label>' . esc_html__('Payment Method', 'vachr-short-term-rental-reservations') . '<br><select name="filter_payment_method" style="width:100%">';
		echo '<option value="">' . esc_html__('All methods', 'vachr-short-term-rental-reservations') . '</option>';
		foreach (['card', 'bank_transfer', 'cash', 'manual'] as $method) {
			printf('<option value="%1$s" %2$s>%3$s</option>', esc_attr($method), selected($filter_payment_method, $method, false), esc_html(self::admin_payment_method_label($method)));
		}
		echo '</select></label></div>';
		echo '<div><label>' . esc_html__('Booking ID', 'vachr-short-term-rental-reservations') . '<br><input type="number" min="0" name="filter_booking_id" value="' . esc_attr((string) $filter_booking_id) . '" style="width:100%"></label></div>';
		echo '</div>';
		echo '<button type="submit" class="button button-primary">' . esc_html__('Apply Filters', 'vachr-short-term-rental-reservations') . '</button> ';
		echo '<a href="' . esc_url(admin_url('admin.php?page=vstrb-transactions')) . '" class="button">' . esc_html__('Clear Filters', 'vachr-short-term-rental-reservations') . '</a>';
		echo '</form>';

		$where = ['1=1'];
		$where_params = [];
		if ($filter_status !== '') {
			$where[] = 'i.status = %s';
			$where_params[] = $filter_status;
		}
		if ($filter_payment_method !== '') {
			$where[] = 'p.method = %s';
			$where_params[] = $filter_payment_method;
		}
		if ($filter_booking_id > 0) {
			$where[] = 'i.booking_id = %d';
			$where_params[] = $filter_booking_id;
		}
		$where_clause = implode(' AND ', $where);

		$count_sql = "SELECT COUNT(*) 
			FROM {$invoice_table} i
			LEFT JOIN {$payment_table} p ON p.id = (
				SELECT p2.id FROM {$payment_table} p2 WHERE p2.booking_id = i.booking_id ORDER BY p2.id DESC LIMIT 1
			)
			WHERE {$where_clause}";
		if (!empty($where_params)) {
			$count_sql = $wpdb->prepare($count_sql, ...$where_params); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		}
		$total_items = (int) $wpdb->get_var($count_sql); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$total_pages = max(1, (int) ceil($total_items / $per_page));

		$list_sql = "SELECT 
				i.id AS invoice_id,
				i.booking_id,
				i.invoice_number,
				i.invoice_language,
				i.status AS invoice_status,
				i.total_amount,
				i.currency AS invoice_currency,
				i.issued_at,
				i.paid_at AS invoice_paid_at,
				b.unit_id,
				b.customer_name,
				b.customer_lastname,
				b.customer_email,
				b.status AS booking_status,
				u.post_title AS unit_title,
				p.id AS payment_id,
				p.method AS payment_method,
				p.provider AS payment_provider,
				p.status AS payment_status,
				p.amount AS payment_amount,
				p.currency AS payment_currency,
				p.paid_at AS payment_paid_at,
				p.provider_reference,
				p.gateway_status,
				p.gateway_payment_status,
				p.payment_method_type,
				p.card_brand,
				p.card_last4,
				p.card_country,
				p.gateway_created_at,
				p.gateway_paid_at
			FROM {$invoice_table} i
			LEFT JOIN {$t['bookings']} b ON b.id = i.booking_id
			LEFT JOIN {$wpdb->posts} u ON u.ID = b.unit_id
			LEFT JOIN {$payment_table} p ON p.id = (
				SELECT p2.id FROM {$payment_table} p2 WHERE p2.booking_id = i.booking_id ORDER BY p2.id DESC LIMIT 1
			)
			WHERE {$where_clause}
			ORDER BY i.id DESC
			LIMIT %d OFFSET %d";
		$list_params = array_merge($where_params, [$per_page, $offset]);
		$rows = $wpdb->get_results($wpdb->prepare($list_sql, ...$list_params), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

		echo '<table class="widefat striped">';
		echo '<thead><tr>';
		echo '<th>ID</th>';
		echo '<th>' . esc_html__('Invoice', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Booking', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Unit', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Customer', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Invoice Status', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Invoice Total', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Payment', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Payment Status', 'vachr-short-term-rental-reservations') . '</th>';
		echo '<th>' . esc_html__('Actions', 'vachr-short-term-rental-reservations') . '</th>';
		echo '</tr></thead><tbody>';

		if (empty($rows)) {
			echo '<tr><td colspan="10">' . esc_html__('No invoices or payments found.', 'vachr-short-term-rental-reservations') . '</td></tr>';
		} else {
			foreach ($rows as $row) {
				$edit_url = wp_nonce_url(
					admin_url('admin.php?page=vstrb-transactions&action=edit_invoice&id=' . (int) $row['invoice_id']),
					'vstrb_edit_invoice_' . (int) $row['invoice_id']
				);
				$print_url = wp_nonce_url(
					admin_url('admin.php?page=vstrb-transactions&action=print_invoice&id=' . (int) $row['invoice_id']),
					'vstrb_print_invoice_' . (int) $row['invoice_id']
				);
				$booking_url = admin_url('admin.php?page=vstrb&filter_booking_id=' . (int) $row['booking_id']);
				echo '<tr>';
				echo '<td>' . (int) $row['invoice_id'] . '</td>';
				$row_invoice_language = self::sanitize_invoice_language((string) ($row['invoice_language'] ?? ''));
				$language_note = $row_invoice_language !== '' ? strtoupper($row_invoice_language) : __('Default language', 'vachr-short-term-rental-reservations');
				echo '<td><strong>' . esc_html((string) ($row['invoice_number'] ?? '')) . '</strong><br><small>' . esc_html((string) ($row['issued_at'] ?? '')) . '</small><br><small>' . esc_html($language_note) . '</small></td>';
				echo '<td><a href="' . esc_url($booking_url) . '">#' . (int) $row['booking_id'] . '</a><br><small>' . esc_html(self::admin_status_label((string) ($row['booking_status'] ?? ''))) . '</small></td>';
				echo '<td>' . esc_html((string) ($row['unit_title'] ?: $row['unit_id'])) . '</td>';
				$customer = trim((string) ($row['customer_name'] ?? '') . ' ' . (string) ($row['customer_lastname'] ?? ''));
				echo '<td>' . esc_html($customer) . '<br><small>' . esc_html((string) ($row['customer_email'] ?? '')) . '</small></td>';
				echo '<td>' . esc_html(self::admin_status_label((string) ($row['invoice_status'] ?? ''))) . '</td>';
				echo '<td>' . esc_html(number_format_i18n((float) ($row['total_amount'] ?? 0), 2) . ' ' . (string) ($row['invoice_currency'] ?? '')) . '</td>';
				$payment_text = '';
				if (!empty($row['payment_id'])) {
					$payment_text = '#' . (int) $row['payment_id'] . ' ' . trim(self::admin_payment_method_label((string) ($row['payment_method'] ?? '')) . ' ' . (string) ($row['payment_provider'] ?? ''));
				}
				echo '<td>';
				echo ($payment_text !== '' ? esc_html($payment_text) : '-');
				$gateway_status_text = trim((string) (($row['gateway_status'] ?? '') . ' ' . ($row['gateway_payment_status'] ?? '')));
				if ($gateway_status_text !== '') {
					echo '<br><small>' . esc_html__('Gateway:', 'vachr-short-term-rental-reservations') . ' ' . esc_html($gateway_status_text) . '</small>';
				}
				$card_bits = [];
				if (!empty($row['payment_method_type'])) {
					$card_bits[] = (string) $row['payment_method_type'];
				}
				if (!empty($row['card_brand'])) {
					$card_bits[] = (string) $row['card_brand'];
				}
				if (!empty($row['card_last4'])) {
					$card_bits[] = '**** ' . (string) $row['card_last4'];
				}
				if (!empty($row['card_country'])) {
					$card_bits[] = (string) $row['card_country'];
				}
				if (!empty($card_bits)) {
					echo '<br><small>' . esc_html(implode(' / ', $card_bits)) . '</small>';
				}
				if (!empty($row['provider_reference'])) {
					echo '<br><small>' . esc_html__('Reference:', 'vachr-short-term-rental-reservations') . ' ' . esc_html((string) $row['provider_reference']) . '</small>';
				}
				echo '</td>';
				$payment_status = (string) ($row['payment_status'] ?? '');
				if ($payment_status === '') {
					$payment_status = '-';
				}
				echo '<td>' . esc_html($payment_status === '-' ? $payment_status : self::admin_status_label($payment_status)) . (!empty($row['payment_paid_at']) ? '<br><small>' . esc_html((string) $row['payment_paid_at']) . '</small>' : '') . '</td>';
				echo '<td><a class="button button-small" href="' . esc_url($edit_url) . '">' . esc_html__('Edit Invoice', 'vachr-short-term-rental-reservations') . '</a> <a class="button button-small" target="_blank" href="' . esc_url($print_url) . '">' . esc_html__('Print / Save PDF', 'vachr-short-term-rental-reservations') . '</a></td>';
				echo '</tr>';
			}
		}

		echo '</tbody></table>';

		if ($total_pages > 1) {
			echo '<div class="tablenav" style="margin-top:15px"><div class="tablenav-pages">';
			$page_links = paginate_links([
				'base' => add_query_arg('paged', '%#%'),
				'format' => '',
				'prev_text' => '&laquo;',
				'next_text' => '&raquo;',
				'total' => $total_pages,
				'current' => $paged,
			]);
			echo '<span class="pagination-links">' . wp_kses_post($page_links) . '</span>';
			echo '</div></div>';
		}

		echo '</div>';
		self::render_donation_box();
	}

	public static function page_calendar() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only unit selector for page filtering.
		$requested_unit_id = self::get_param_int('unit_id', 0);
		$unit_id = self::resolve_unit_selection($requested_unit_id);

		echo '<div class="wrap"><h1>' . esc_html__('Calendar', 'vachr-short-term-rental-reservations') . '</h1>';
		echo '<form method="get" style="margin:10px 0 20px 0">';
		echo '<input type="hidden" name="page" value="vstrb-calendar" />';
		self::unit_select($unit_id);
		submit_button(esc_html__('Show', 'vachr-short-term-rental-reservations'), 'secondary', '', false);
		echo '</form>';

		if (!$unit_id) { 
			echo '</div>'; 
			return; 
		}

		// Render Booking Form (reuses frontend logic)
		// This accepts styling settings from 'Styling' page too.
		$html = VSTRB_Shortcodes::booking_form(['unit' => $unit_id]);
		
		// Inject isAdmin flag for JS
		$data = VSTRB_Shortcodes::get_localization_data();
		$data['isAdmin'] = true;
		wp_localize_script('vstrb-frontend', 'VSTRB', $data);

		// Render plugin-generated markup without KSES stripping so admin booking form
		// keeps required inline styles and data-* attributes.
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $html;
		echo '</div>';
		self::render_donation_box();
	}



	public static function page_services($embedded = false) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$base_url = $embedded ? add_query_arg(['page' => 'vstrb-general', 'tab' => 'services'], admin_url('admin.php')) : admin_url('admin.php?page=vstrb-services');

		if (!$embedded) echo '<div class="wrap">';
		echo ($embedded ? '<h2>' : '<h1>') . esc_html__('Services', 'vachr-short-term-rental-reservations') . ($embedded ? '</h2>' : '</h1>');

		// Delete action
		if (self::get_param_text('action', '') === 'delete' && self::get_param_int('id', 0) > 0) {
			check_admin_referer('vstrb_delete_service');
			$id = self::get_param_int('id', 0);
			$wpdb->delete($t['services'], ['id' => $id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
			$wpdb->delete($t['unit_services'], ['service_id' => $id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
			self::bump_services_cache_version();
			echo '<div class="notice notice-success"><p>' . esc_html__('Service deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}

		// Edit/Add logic
		$edit_id = self::request_param_int('edit_id', 0);
		$edit_data = null;
		if ($edit_id) {
			$edit_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['services']} WHERE id=%d", $edit_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
		}

		// Save service
		if (isset($_POST['vstrb_save_service'])) {
			check_admin_referer('vstrb_services', 'vstrb_services_nonce');
			$currencies = self::get_available_currencies();
			$default_base_currency = self::get_base_currency($currencies);
			$fx_rates = self::get_currency_rates_map($currencies, $default_base_currency);
			$price_auto_fx = self::post_checkbox_enabled('price_auto_fx');
			$price_base_currency = strtoupper(sanitize_text_field(wp_unslash($_POST['price_base_currency'] ?? $default_base_currency)));
			if (!in_array($price_base_currency, $currencies, true)) {
				$price_base_currency = $default_base_currency;
			}
			$price_base_amount = self::parse_decimal_input(sanitize_text_field(wp_unslash($_POST['price_base_amount'] ?? '')), null);
			$posted_labels = self::post_array('label_i18n');
			$posted_descs = self::post_array('desc_i18n');
			$locales_allowed = array_keys(self::get_language_options(false));

			$data = [
				'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')),
				'code' => sanitize_text_field(wp_unslash($_POST['code'] ?? '')),
				'pricing_type' => sanitize_text_field(wp_unslash($_POST['pricing_type'] ?? '')),
				'description' => sanitize_textarea_field(wp_unslash($_POST['description'] ?? '')),
				'is_active' => self::post_checkbox_enabled('is_active'),
				'is_optional' => (isset($_POST['is_optional']) && '1' === sanitize_text_field(wp_unslash($_POST['is_optional']))) ? 1 : 0,
				'sort_order' => isset($_POST['sort_order']) ? (int) sanitize_text_field(wp_unslash($_POST['sort_order'])) : 0,
			];

			$svc_id = 0;
			if ($edit_id && $edit_data) {
				// Update
				$wpdb->update($t['services'], $data, ['id'=>$edit_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				echo '<div class="notice notice-success"><p>' . esc_html__('Updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
				$edit_data = (object)$data; 
				$svc_id = $edit_id;
			} else {
				// Insert
				$wpdb->insert($t['services'], $data); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				$svc_id = $wpdb->insert_id;
				echo '<div class="notice notice-success"><p>' . esc_html__('Saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
				// Reset form
				$edit_data = null;
				$edit_id = 0;
			}

			// Save Meta
			if ($svc_id) {
				$posted_meta = self::post_array('meta');
				$wpdb->delete($t['service_meta'], ['service_id' => $svc_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.

				$wpdb->insert($t['service_meta'], [
					'service_id' => $svc_id,
					'meta_key' => 'price_auto_fx',
					'meta_value' => (string) (int) $price_auto_fx,
				]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				$wpdb->insert($t['service_meta'], [
					'service_id' => $svc_id,
					'meta_key' => 'price_base_currency',
					'meta_value' => $price_base_currency,
				]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				if ($price_base_amount !== null) {
					$wpdb->insert($t['service_meta'], [
						'service_id' => $svc_id,
						'meta_key' => 'price_base_amount',
						'meta_value' => $price_base_amount,
					]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				}

				foreach ($posted_labels as $locale => $value) {
					if (is_array($value)) {
						continue;
					}
					$locale = sanitize_text_field((string) $locale);
					if (!in_array($locale, $locales_allowed, true)) {
						continue;
					}
					$label_val = sanitize_text_field((string) $value);
					if ($label_val === '') {
						continue;
					}
					$wpdb->insert($t['service_meta'], [
						'service_id' => $svc_id,
						'meta_key' => 'label',
						'meta_value' => $label_val,
						'locale' => $locale,
					]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				}

				foreach ($posted_descs as $locale => $value) {
					if (is_array($value)) {
						continue;
					}
					$locale = sanitize_text_field((string) $locale);
					if (!in_array($locale, $locales_allowed, true)) {
						continue;
					}
					$desc_val = sanitize_textarea_field((string) $value);
					if ($desc_val === '') {
						continue;
					}
					$wpdb->insert($t['service_meta'], [
						'service_id' => $svc_id,
						'meta_key' => 'description',
						'meta_value' => $desc_val,
						'locale' => $locale,
					]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				}

				$reserved_meta_keys = ['price_auto_fx', 'price_base_currency', 'price_base_amount', 'label', 'name', 'description', 'desc'];
				foreach ($posted_meta as $pair) {
					$meta_key = sanitize_text_field($pair['key'] ?? '');
					if ($meta_key === '') {
						continue;
					}
					$meta_key_lc = strtolower($meta_key);
					$meta_locale = !empty($pair['locale']) ? sanitize_text_field($pair['locale']) : null;
					if (in_array($meta_key_lc, $reserved_meta_keys, true) || strpos($meta_key_lc, 'price_') === 0) {
						// Keep backward compatibility for global label/description overrides from old metadata rows.
						$allow_global_text_override = (
							in_array($meta_key_lc, ['label', 'name', 'description', 'desc'], true)
							&& ($meta_locale === null || $meta_locale === '')
						);
						if (!$allow_global_text_override) {
							continue;
						}
					}
					$wpdb->insert($t['service_meta'], [
						'service_id' => $svc_id,
						'meta_key' => $meta_key,
						'meta_value' => sanitize_textarea_field($pair['value'] ?? ''),
						'locale' => $meta_locale,
					]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				}
			}

			// Save Currency Prices
			if ($svc_id) {
				$resolved_prices = [];
				foreach ($currencies as $c) {
					$raw = sanitize_text_field(wp_unslash($_POST['price_' . $c] ?? ''));
					$c_price = self::parse_decimal_input($raw, null);
					if ($c_price === null && $price_auto_fx && $price_base_amount !== null) {
						$c_price = self::convert_price_by_rate($price_base_amount, $price_base_currency, $c, $fx_rates);
					}
					if ($c_price === null) {
						continue;
					}
					$resolved_prices[$c] = (float) $c_price;
					$wpdb->insert($t['service_meta'], [
						'service_id' => $svc_id,
						'meta_key' => 'price_' . $c,
						'meta_value' => $c_price,
					]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				}

				$eur_price = $resolved_prices['EUR'] ?? self::convert_price_by_rate($price_base_amount, $price_base_currency, 'EUR', $fx_rates);
				if ($eur_price !== null) {
					$wpdb->update($t['services'], ['price' => (float) $eur_price], ['id' => $svc_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				}
				self::bump_services_cache_version();
			}
		}

		self::add_inline_admin_script("
			window.fillService = function(name, code, type, price) {
				var nameInput = document.getElementById('svc_name');
				var codeInput = document.getElementById('svc_code');
				var typeInput = document.getElementById('svc_type');
				var baseInput = document.getElementById('svc_price_base');
				if (nameInput) nameInput.value = name;
				if (codeInput) codeInput.value = code;
				if (typeInput) typeInput.value = type;
				if (baseInput) baseInput.value = price;
			};
		");
		?>
		<div style="margin-bottom:20px;background:#fff;padding:10px;border:1px solid #ccd0d4">
			<strong><?php esc_html_e('Quick Add:', 'vachr-short-term-rental-reservations'); ?></strong>
			<button type="button" class="button" onclick="fillService('Cleaning','CLEANING','per_stay',0)"><?php esc_html_e('Cleaning', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Bed Linen','LINEN','per_stay',0)"><?php esc_html_e('Bed Linen', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Towels','TOWELS','per_stay',0)"><?php esc_html_e('Towels', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Parking','PARKING','per_stay',0)"><?php esc_html_e('Parking', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Garage','GARAGE','per_stay',0)"><?php esc_html_e('Garage', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Breakfast','BREAKFAST','per_person_night',0)"><?php esc_html_e('Breakfast', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Half Board','HALF_BOARD','per_person_night',0)"><?php esc_html_e('Half Board', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('All Inclusive','ALL_INCLUSIVE','per_person_night',0)"><?php esc_html_e('All Inclusive', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Netflix / Streaming','NETFLIX','per_stay',0)"><?php esc_html_e('Netflix / Streaming', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Game Console','CONSOLE','per_stay',0)"><?php esc_html_e('Game Console', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Sauna','SAUNA','per_stay',0)"><?php esc_html_e('Sauna', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Wellness / Spa','WELLNESS','per_person',0)"><?php esc_html_e('Wellness / Spa', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Pool','POOL','per_stay',0)"><?php esc_html_e('Pool', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Late Check-out','LATE_CHECKOUT','per_stay',0)"><?php esc_html_e('Late Check-out', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Pet Fee','PET_FEE','per_stay',0)"><?php esc_html_e('Pet Fee', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Bike Rental','BIKE_RENTAL','per_night',0)"><?php esc_html_e('Bike Rental', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('EV Charging','EV_CHARGE','per_stay',0)"><?php esc_html_e('EV Charging', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Wood for Fireplace','FIREWOOD','per_stay',0)"><?php esc_html_e('Wood for Fireplace', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Airport Shuttle','SHUTTLE','per_stay',0)"><?php esc_html_e('Airport Shuttle', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Extra Bed','EXTRA_BED','per_night',0)"><?php esc_html_e('Extra Bed', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Tourist Tax','TOURIST_TAX','per_person_night',0)"><?php esc_html_e('Tourist Tax', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Air Conditioning','AIR_CONDITIONING','per_stay',0)"><?php esc_html_e('Air Conditioning', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('TV','TV','per_stay',0)"><?php esc_html_e('TV', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('WIFI','WIFI','per_stay',0)"><?php esc_html_e('WIFI', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Boat Rental','BOAT_RENTAL','per_stay',0)"><?php esc_html_e('Boat Rental', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Paddleboard Rental','PADDLEBOARD_RENTAL','per_stay',0)"><?php esc_html_e('Paddleboard Rental', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Beach Access','BEACH_ACCESS','per_stay',0)"><?php esc_html_e('Beach Access', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Sunbed & Umbrella','BEACH_SET','per_stay',0)"><?php esc_html_e('Sunbed & Umbrella', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Energy','ENERGY','per_stay',0)"><?php esc_html_e('Energy', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Car Rental','CAR_RENTAL','per_stay',0)"><?php esc_html_e('Car Rental', 'vachr-short-term-rental-reservations'); ?></button>
			<button type="button" class="button" onclick="fillService('Motorcycle Rental','MOTORCYCLE_RENTAL','per_stay',0)"><?php esc_html_e('Motorcycle Rental', 'vachr-short-term-rental-reservations'); ?></button>

		</div>
		<?php

		// Form
		$lbl = $edit_id ? esc_html__('Edit Service', 'vachr-short-term-rental-reservations') : esc_html__('Add Service', 'vachr-short-term-rental-reservations');
		$btn = $edit_id ? esc_html__('Update', 'vachr-short-term-rental-reservations') : esc_html__('Add', 'vachr-short-term-rental-reservations');
		$val_name = $edit_data ? $edit_data->name : '';
		$val_code = $edit_data ? $edit_data->code : '';
		$val_type = $edit_data ? $edit_data->pricing_type : 'per_stay';
		$val_price = $edit_data ? $edit_data->price : '0';
		$val_sort = $edit_data ? $edit_data->sort_order : '0';
		$val_active = (!$edit_data || $edit_data->is_active) ? 'checked' : '';
		$val_optional = (!$edit_data || $edit_data->is_optional) ? 'checked' : '';

		$form_action = $base_url;
		echo '<form method="post" action="' . esc_url($form_action) . '">';
		wp_nonce_field('vstrb_services', 'vstrb_services_nonce');
		echo '<input type="hidden" name="edit_id" value="'.(int)$edit_id.'">';
		echo '<h2>' . esc_html($lbl) . ' ' . ($edit_id ? '<a href="'.esc_url($base_url).'" class="page-title-action">'.esc_html__('Cancel', 'vachr-short-term-rental-reservations').'</a>' : '') . '</h2>';
		echo '<div style="display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;margin-bottom:10px">';
		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:220px">' . esc_html__('Name', 'vachr-short-term-rental-reservations') . '<input id="svc_name" name="name" required value="'.esc_attr($val_name).'"></label>';
		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:220px">' . esc_html__('Code', 'vachr-short-term-rental-reservations') . '<input id="svc_code" name="code" required value="'.esc_attr($val_code).'" placeholder="CODE"></label>';
		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:180px">' . esc_html__('Type', 'vachr-short-term-rental-reservations') . '<select id="svc_type" name="pricing_type">';
		$types = VSTRB_CPT::get_service_pricing_types();
		foreach ($types as $k=>$v) echo '<option value="'.esc_attr($k).'" '.selected($val_type,$k,false).'>'.esc_html($v).'</option>';
		echo '</select></label>';

		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:120px">' . esc_html__('Order', 'vachr-short-term-rental-reservations') . '<input type="number" name="sort_order" value="'.esc_attr($val_sort).'" style="width:100%"></label>';
		echo '</div>';
		
		$service_meta_rows = [];
		$service_meta_price_map = [];
		$service_label_i18n = [];
		$service_desc_i18n = [];
		$price_auto_fx_val = 0;
		$currencies = self::get_available_currencies();
		$default_base_currency = self::get_base_currency($currencies);
		$price_base_currency_val = $default_base_currency;
		$price_base_amount_val = '';
		$meta = [];
		if ($edit_id) {
			$service_meta_rows = $wpdb->get_results($wpdb->prepare("SELECT meta_key, meta_value, locale FROM {$t['service_meta']} WHERE service_id=%d", $edit_id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
			foreach ($service_meta_rows as $mr) {
				$meta_key = strtolower(trim((string) ($mr['meta_key'] ?? '')));
				$meta_locale = sanitize_text_field((string) ($mr['locale'] ?? ''));
				$meta_value = (string) ($mr['meta_value'] ?? '');
				if (strpos($meta_key, 'price_') === 0 && $mr['locale'] === null) {
					$service_meta_price_map[$mr['meta_key']] = $mr['meta_value'];
					continue;
				}
				if ($meta_key === 'price_auto_fx') {
					$price_auto_fx_val = in_array(strtolower(trim($meta_value)), ['1', 'yes', 'true', 'on'], true) ? 1 : 0;
					continue;
				}
				if ($meta_key === 'price_base_currency') {
					$price_base_currency_val = strtoupper(sanitize_text_field($meta_value));
					continue;
				}
				if ($meta_key === 'price_base_amount') {
					$price_base_amount_val = (string) self::parse_decimal_input($meta_value, '');
					continue;
				}
				if (in_array($meta_key, ['label', 'name'], true) && $meta_locale !== '') {
					$service_label_i18n[$meta_locale] = sanitize_text_field($meta_value);
					continue;
				}
				if (in_array($meta_key, ['description', 'desc'], true) && $meta_locale !== '') {
					$service_desc_i18n[$meta_locale] = sanitize_textarea_field($meta_value);
					continue;
				}
				$meta[] = ['key' => $mr['meta_key'], 'value' => $mr['meta_value'], 'locale' => $mr['locale']];
			}
		}
		if (!in_array($price_base_currency_val, $currencies, true)) {
			$price_base_currency_val = $default_base_currency;
		}
		if ($price_base_amount_val === '' && isset($service_meta_price_map['price_' . $price_base_currency_val])) {
			$price_base_amount_val = (string) self::parse_decimal_input($service_meta_price_map['price_' . $price_base_currency_val], '');
		}

		echo '<div style="width:100%;border-top:1px solid #ccc;margin-top:10px;padding-top:10px">';
		echo '<strong>' . esc_html__('Pricing (Standard Fields)', 'vachr-short-term-rental-reservations') . '</strong>';
		echo '<p style="margin:8px 0 10px 0;color:#666">' . esc_html__('Set base price once and optionally auto-calculate currency prices using rates from General settings.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;max-width:920px;align-items:end">';
		echo '<label style="display:flex;align-items:center;gap:8px;grid-column:1 / -1"><input type="checkbox" name="price_auto_fx" value="1" ' . checked($price_auto_fx_val, 1, false) . '> ' . esc_html__('Auto convert prices from base currency', 'vachr-short-term-rental-reservations') . '</label>';
		echo '<label style="display:flex;flex-direction:column;gap:4px">' . esc_html__('Base Currency', 'vachr-short-term-rental-reservations') . '<select id="svc_price_base_currency" name="price_base_currency">';
		foreach ($currencies as $c) {
			echo '<option value="' . esc_attr($c) . '" ' . selected($price_base_currency_val, $c, false) . '>' . esc_html($c) . '</option>';
		}
		echo '</select></label>';
		echo '<label style="display:flex;flex-direction:column;gap:4px">' . esc_html__('Base Price', 'vachr-short-term-rental-reservations') . '<input type="number" step="0.01" id="svc_price_base" name="price_base_amount" value="' . esc_attr($price_base_amount_val) . '" placeholder="0.00"></label>';
		echo '</div>';
		echo '</div>';

		echo '<div style="width:100%;margin-top:12px;display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;max-width:920px">';
		foreach ($currencies as $c) {
			$c_val = $edit_id ? ($service_meta_price_map['price_' . $c] ?? '') : '';
			/* translators: %s: currency code */
			echo '<label style="display:flex;flex-direction:column;gap:4px">' . sprintf(esc_html__('Price (%s)', 'vachr-short-term-rental-reservations'), esc_html($c)) . '<input type="number" step="0.01" name="price_'.esc_attr($c).'" value="'.esc_attr($c_val).'" placeholder="'.esc_attr__('auto', 'vachr-short-term-rental-reservations').'" style="width:100%"></label>';
		}
		echo '</div>';

		echo '<label style="display:flex;flex-direction:column;gap:4px;max-width:400px;margin-top:12px">' . esc_html__('Description', 'vachr-short-term-rental-reservations') . '<textarea name="description" rows="3" style="width:100%">'.esc_textarea($edit_data->description ?? '').'</textarea></label>';
		
		$locales = self::get_language_options(false);
		$meta_locales = self::get_language_options(true);
		$meta_locales_json = wp_json_encode($meta_locales);
		?>
		<div style="width:100%;border-top:1px solid #ccc;margin-top:10px;padding-top:10px">
			<strong><?php esc_html_e('Translations (Standard Fields)', 'vachr-short-term-rental-reservations'); ?></strong>
			<p style="margin:8px 0 10px 0;color:#666"><?php esc_html_e('Global Name and Description are above. Fill locale-specific translations below if needed.', 'vachr-short-term-rental-reservations'); ?></p>
			<table class="widefat striped" style="max-width:980px">
				<thead>
					<tr>
						<th style="width:80px"><?php esc_html_e('Locale', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Name Translation', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Description Translation', 'vachr-short-term-rental-reservations'); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php foreach ($locales as $loc_key => $loc_label): ?>
						<tr>
							<td><strong><?php echo esc_html($loc_label); ?></strong></td>
							<td><input type="text" name="label_i18n[<?php echo esc_attr($loc_key); ?>]" value="<?php echo esc_attr($service_label_i18n[$loc_key] ?? ''); ?>" style="width:100%" placeholder="<?php esc_attr_e('optional', 'vachr-short-term-rental-reservations'); ?>"></td>
							<td><textarea name="desc_i18n[<?php echo esc_attr($loc_key); ?>]" rows="2" style="width:100%" placeholder="<?php esc_attr_e('optional', 'vachr-short-term-rental-reservations'); ?>"><?php echo esc_textarea($service_desc_i18n[$loc_key] ?? ''); ?></textarea></td>
						</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
		</div>
		<div style="width:100%;border-top:1px solid #ccc;margin-top:10px;padding-top:10px">
			<strong><?php esc_html_e('Additional Metadata (Advanced)', 'vachr-short-term-rental-reservations'); ?></strong>
			<div id="vstrb_meta_wrapper">
				<?php foreach ($meta as $idx => $pair): ?>
				<div class="vstrb-meta-row" style="margin-bottom:5px;display:flex;gap:5px">
					<select name="meta[<?php echo (int)$idx; ?>][locale]" style="width:80px">
						<?php foreach ($meta_locales as $k=>$v) echo '<option value="'.esc_attr($k).'" '.selected($pair['locale'] ?? '', $k, false).'>'.esc_html($v).'</option>'; ?>
					</select>
					<input type="text" name="meta[<?php echo (int)$idx; ?>][key]" placeholder="Key" value="<?php echo esc_attr($pair['key'] ?? ''); ?>" size="15">
					<input type="text" name="meta[<?php echo (int)$idx; ?>][value]" placeholder="Value" value="<?php echo esc_attr($pair['value'] ?? ''); ?>" size="30">
					<button type="button" class="button button-small" onclick="this.parentNode.remove()">x</button>
				</div>
				<?php endforeach; ?>
			</div>
			<button type="button" class="button button-small" onclick="addMetaRow()"><?php esc_html_e('+ Add Meta', 'vachr-short-term-rental-reservations'); ?></button>
		</div>
		<?php

		self::add_inline_admin_script("
			window.addMetaRow = function() {
				const w = document.getElementById('vstrb_meta_wrapper');
				if (!w) return;
				const idx = w.children.length + new Date().getTime();
				const localeOptions = {$meta_locales_json} || {};
				const optionsHtml = Object.entries(localeOptions).map(([k, v]) => `<option value=\"\${k}\">\${v}</option>`).join('');
				const div = document.createElement('div');
				div.className = 'vstrb-meta-row';
				div.style = 'margin-bottom:5px;display:flex;gap:5px';
				div.innerHTML = `<select name=\"meta[\${idx}][locale]\" style=\"width:80px\">
					\${optionsHtml}
				</select> <input type=\"text\" name=\"meta[\${idx}][key]\" placeholder=\"Key\" size=\"15\"> <input type=\"text\" name=\"meta[\${idx}][value]\" placeholder=\"Value\" size=\"30\"> <button type=\"button\" class=\"button button-small\" onclick=\"this.parentNode.remove()\">x</button>`;
				w.appendChild(div);
			};
		");

		echo '<div style="margin-top:14px;display:flex;gap:14px;align-items:center;flex-wrap:wrap">';
		echo '<label style="display:flex;align-items:center;gap:6px"><input type="checkbox" name="is_optional" '.esc_attr($val_optional).'> ' . esc_html__('Optional (default)', 'vachr-short-term-rental-reservations') . '</label>';
		echo '<label style="display:flex;align-items:center;gap:6px"><input type="checkbox" name="is_active" '.esc_attr($val_active).'> ' . esc_html__('active', 'vachr-short-term-rental-reservations') . '</label>';
		echo '<button class="button button-primary" name="vstrb_save_service" value="1">' . esc_html($btn) . '</button>';
		echo '</div>';
		echo '</form>';

		// List
		$services = $wpdb->get_results(sprintf("SELECT * FROM %s ORDER BY sort_order ASC, name ASC", $t['services']), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.NotPrepared -- Custom table query.
		$service_list_labels = [];
		$service_list_global_labels = [];
		$service_list_ids = array_map(static function($service) {
			return (int) ($service['id'] ?? 0);
		}, $services);
		$service_list_ids = array_values(array_filter($service_list_ids));
		$admin_lang = self::normalize_language_code(get_user_locale());
		if ($service_list_ids) {
			$ids_placeholders = implode(',', array_fill(0, count($service_list_ids), '%d'));
			$label_rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT service_id, meta_value, locale
					 FROM {$t['service_meta']}
					 WHERE meta_key IN ('label','name') AND service_id IN ({$ids_placeholders})", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
					...$service_list_ids
				),
				ARRAY_A
			);
			foreach ($label_rows as $label_row) {
				$service_id = (int) ($label_row['service_id'] ?? 0);
				$label = sanitize_text_field((string) ($label_row['meta_value'] ?? ''));
				if ($service_id <= 0 || $label === '') {
					continue;
				}
				$label_lang = self::normalize_language_code($label_row['locale'] ?? '');
				if ($admin_lang !== '' && $label_lang === $admin_lang && !isset($service_list_labels[$service_id])) {
					$service_list_labels[$service_id] = $label;
				} elseif ($label_lang === '' && !isset($service_list_global_labels[$service_id])) {
					$service_list_global_labels[$service_id] = $label;
				}
			}
		}
		echo '<h2>' . esc_html__('List', 'vachr-short-term-rental-reservations') . '</h2>';
		echo '<table class="widefat striped"><thead><tr><th>ID</th><th>' . esc_html__('Name', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Code', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Type', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Price', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Order', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Optional', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Active', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Actions', 'vachr-short-term-rental-reservations') . '</th></tr></thead><tbody>';
		foreach ($services as $s) {
			$del_url = wp_nonce_url(add_query_arg(['action' => 'delete', 'id' => (int) $s['id']], $base_url), 'vstrb_delete_service');
			$edit_url = add_query_arg('edit_id', (int) $s['id'], $base_url);
			$service_id = (int) $s['id'];
			// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText -- service name is user-defined data stored in DB, with gettext fallback for seeded defaults.
			$service_name = $service_list_labels[$service_id] ?? ($service_list_global_labels[$service_id] ?? __($s['name'], 'vachr-short-term-rental-reservations'));
			echo '<tr>
				<td>'.esc_html($s['id']).'</td>
				<td>'.esc_html($service_name).'</td>
				<td><code>'.esc_html($s['code']).'</code></td>
				<td>'.esc_html($types[$s['pricing_type']] ?? $s['pricing_type']).'</td>
				<td>'.esc_html($s['price']).'</td>
				<td>'.esc_html($s['sort_order']).'</td>
				<td>'.($s['is_optional'] ? esc_html__('yes', 'vachr-short-term-rental-reservations') : esc_html__('no', 'vachr-short-term-rental-reservations')).'</td>
				<td>'.($s['is_active'] ? esc_html__('yes', 'vachr-short-term-rental-reservations') : esc_html__('no', 'vachr-short-term-rental-reservations')).'</td>
				<td>
					<a href="'.esc_url($edit_url).'" class="button button-small">'.esc_html__('Edit', 'vachr-short-term-rental-reservations').'</a>
					<a href="'.esc_url($del_url).'" class="button button-small button-link-delete" onclick="return confirm(\''.esc_js(__('Are you sure?', 'vachr-short-term-rental-reservations')).'\')">'.esc_html__('Delete', 'vachr-short-term-rental-reservations').'</a>
				</td>
			</tr>';
		}
		echo '</tbody></table>';
		if (!$embedded) echo '</div>';
		self::render_donation_box();
	}

	public static function page_amenities($embedded = false) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$base_url = $embedded ? add_query_arg(['page' => 'vstrb-general', 'tab' => 'amenities'], admin_url('admin.php')) : admin_url('admin.php?page=vstrb-amenities');

		if (!$embedded) echo '<div class="wrap">';
		echo ($embedded ? '<h2>' : '<h1>') . esc_html__('Amenities', 'vachr-short-term-rental-reservations') . ($embedded ? '</h2>' : '</h1>');

		$categories = [
			'general' => __('General', 'vachr-short-term-rental-reservations'),
			'connectivity' => __('Connectivity', 'vachr-short-term-rental-reservations'),
			'entertainment' => __('Entertainment', 'vachr-short-term-rental-reservations'),
			'comfort' => __('Comfort', 'vachr-short-term-rental-reservations'),
			'bedroom' => __('Bedroom', 'vachr-short-term-rental-reservations'),
			'kitchen' => __('Kitchen', 'vachr-short-term-rental-reservations'),
			'bathroom' => __('Bathroom', 'vachr-short-term-rental-reservations'),
			'laundry' => __('Laundry', 'vachr-short-term-rental-reservations'),
			'outdoor' => __('Outdoor', 'vachr-short-term-rental-reservations'),
			'wellness' => __('Wellness', 'vachr-short-term-rental-reservations'),
			'parking' => __('Parking', 'vachr-short-term-rental-reservations'),
			'access' => __('Access', 'vachr-short-term-rental-reservations'),
			'accessibility' => __('Accessibility', 'vachr-short-term-rental-reservations'),
			'family' => __('Family', 'vachr-short-term-rental-reservations'),
			'safety' => __('Safety', 'vachr-short-term-rental-reservations'),
			'rules' => __('Rules', 'vachr-short-term-rental-reservations'),
		];

		if (self::get_param_text('action', '') === 'delete' && self::get_param_int('id', 0) > 0) {
			check_admin_referer('vstrb_delete_amenity');
			$id = self::get_param_int('id', 0);
			$wpdb->delete($t['amenities'], ['id' => $id]);
			$wpdb->delete($t['amenity_meta'], ['amenity_id' => $id]);
			$wpdb->delete($t['unit_amenities'], ['amenity_id' => $id]);
			self::bump_amenities_cache_version();
			echo '<div class="notice notice-success"><p>' . esc_html__('Amenity deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}

		$edit_id = self::request_param_int('edit_id', 0);
		$edit_data = $edit_id ? $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['amenities']} WHERE id=%d", $edit_id)) : null;

		if (isset($_POST['vstrb_save_amenity'])) {
			check_admin_referer('vstrb_amenities', 'vstrb_amenities_nonce');
			$posted_labels = self::post_array('label_i18n');
			$posted_descs = self::post_array('desc_i18n');
			$locales_allowed = array_keys(self::get_language_options(false));
			$category = sanitize_key((string) ($_POST['category'] ?? 'general'));
			if (!isset($categories[$category])) {
				$category = 'general';
			}
			$data = [
				'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')),
				'code' => sanitize_text_field(wp_unslash($_POST['code'] ?? '')),
				'category' => $category,
				'description' => sanitize_textarea_field(wp_unslash($_POST['description'] ?? '')),
				'is_active' => self::post_checkbox_enabled('is_active'),
				'sort_order' => isset($_POST['sort_order']) ? (int) sanitize_text_field(wp_unslash($_POST['sort_order'])) : 0,
			];

			$amenity_id = 0;
			if ($edit_id && $edit_data) {
				$wpdb->update($t['amenities'], $data, ['id' => $edit_id]);
				$amenity_id = $edit_id;
				$edit_data = (object) $data;
				echo '<div class="notice notice-success"><p>' . esc_html__('Updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$wpdb->insert($t['amenities'], $data);
				$amenity_id = (int) $wpdb->insert_id;
				$edit_id = 0;
				$edit_data = null;
				echo '<div class="notice notice-success"><p>' . esc_html__('Saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
			}

			if ($amenity_id > 0) {
				$wpdb->delete($t['amenity_meta'], ['amenity_id' => $amenity_id]);
				foreach ($posted_labels as $locale => $value) {
					if (is_array($value)) {
						continue;
					}
					$locale = sanitize_text_field((string) $locale);
					if (!in_array($locale, $locales_allowed, true)) {
						continue;
					}
					$label_val = sanitize_text_field((string) $value);
					if ($label_val === '') {
						continue;
					}
					$wpdb->insert($t['amenity_meta'], [
						'amenity_id' => $amenity_id,
						'meta_key' => 'label',
						'meta_value' => $label_val,
						'locale' => $locale,
					]);
				}
				foreach ($posted_descs as $locale => $value) {
					if (is_array($value)) {
						continue;
					}
					$locale = sanitize_text_field((string) $locale);
					if (!in_array($locale, $locales_allowed, true)) {
						continue;
					}
					$desc_val = sanitize_textarea_field((string) $value);
					if ($desc_val === '') {
						continue;
					}
					$wpdb->insert($t['amenity_meta'], [
						'amenity_id' => $amenity_id,
						'meta_key' => 'description',
						'meta_value' => $desc_val,
						'locale' => $locale,
					]);
				}
				self::bump_amenities_cache_version();
			}
		}

		self::add_inline_admin_script("
			window.fillAmenity = function(name, code, category) {
				var nameInput = document.getElementById('amenity_name');
				var codeInput = document.getElementById('amenity_code');
				var categoryInput = document.getElementById('amenity_category');
				if (nameInput) nameInput.value = name;
				if (codeInput) codeInput.value = code;
				if (categoryInput) categoryInput.value = category;
			};
		");
		?>
		<div style="margin-bottom:20px;background:#fff;padding:10px;border:1px solid #ccd0d4">
			<strong><?php esc_html_e('Quick Add:', 'vachr-short-term-rental-reservations'); ?></strong>
			<?php
			$quick = [
				['Netflix', 'NETFLIX', 'entertainment'], ['Smart TV', 'SMART_TV', 'entertainment'], ['Streaming services', 'STREAMING_SERVICES', 'entertainment'], ['Wi-Fi', 'WIFI', 'connectivity'], ['High-speed internet', 'HIGH_SPEED_INTERNET', 'connectivity'],
				['Air conditioning', 'AIR_CONDITIONING', 'comfort'], ['Heating', 'HEATING', 'comfort'], ['Blackout curtains', 'BLACKOUT_CURTAINS', 'comfort'], ['Kitchen', 'KITCHEN', 'kitchen'], ['Coffee maker', 'COFFEE_MAKER', 'kitchen'],
				['Dishwasher', 'DISHWASHER', 'kitchen'], ['Washing machine', 'WASHING_MACHINE', 'laundry'], ['Balcony', 'BALCONY', 'outdoor'], ['Terrace', 'TERRACE', 'outdoor'], ['Pool', 'POOL', 'wellness'], ['Sauna', 'SAUNA', 'wellness'],
				['Parking', 'PARKING', 'parking'], ['EV charger', 'EV_CHARGER', 'parking'], ['Self check-in', 'SELF_CHECKIN', 'access'], ['Pets allowed', 'PETS_ALLOWED', 'rules'],
			];
			foreach ($quick as $item) {
				printf(
					'<button type="button" class="button" onclick="fillAmenity(%s,%s,%s)">%s</button> ',
					wp_json_encode($item[0]),
					wp_json_encode($item[1]),
					wp_json_encode($item[2]),
					esc_html($item[0])
				);
			}
			?>
		</div>
		<?php

		$amenity_label_i18n = [];
		$amenity_desc_i18n = [];
		if ($edit_id) {
			$meta_rows = $wpdb->get_results($wpdb->prepare("SELECT meta_key, meta_value, locale FROM {$t['amenity_meta']} WHERE amenity_id=%d", $edit_id), ARRAY_A);
			foreach ($meta_rows as $mr) {
				$meta_key = strtolower(trim((string) ($mr['meta_key'] ?? '')));
				$locale = sanitize_text_field((string) ($mr['locale'] ?? ''));
				if ($locale === '') {
					continue;
				}
				if ($meta_key === 'label') {
					$amenity_label_i18n[$locale] = sanitize_text_field((string) ($mr['meta_value'] ?? ''));
				} elseif ($meta_key === 'description') {
					$amenity_desc_i18n[$locale] = sanitize_textarea_field((string) ($mr['meta_value'] ?? ''));
				}
			}
		}

		$lbl = $edit_id ? esc_html__('Edit Amenity', 'vachr-short-term-rental-reservations') : esc_html__('Add Amenity', 'vachr-short-term-rental-reservations');
		$btn = $edit_id ? esc_html__('Update', 'vachr-short-term-rental-reservations') : esc_html__('Add', 'vachr-short-term-rental-reservations');
		$form_action = $base_url;
		echo '<form method="post" action="' . esc_url($form_action) . '">';
		wp_nonce_field('vstrb_amenities', 'vstrb_amenities_nonce');
		echo '<input type="hidden" name="edit_id" value="' . (int) $edit_id . '">';
		echo '<h2>' . esc_html($lbl) . ' ' . ($edit_id ? '<a href="' . esc_url($base_url) . '" class="page-title-action">' . esc_html__('Cancel', 'vachr-short-term-rental-reservations') . '</a>' : '') . '</h2>';
		echo '<div style="display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;margin-bottom:10px">';
		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:220px">' . esc_html__('Name', 'vachr-short-term-rental-reservations') . '<input id="amenity_name" name="name" required value="' . esc_attr($edit_data->name ?? '') . '"></label>';
		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:220px">' . esc_html__('Code', 'vachr-short-term-rental-reservations') . '<input id="amenity_code" name="code" required value="' . esc_attr($edit_data->code ?? '') . '" placeholder="CODE"></label>';
		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:180px">' . esc_html__('Category', 'vachr-short-term-rental-reservations') . '<select id="amenity_category" name="category">';
		$val_category = $edit_data->category ?? 'general';
		foreach ($categories as $key => $label) {
			echo '<option value="' . esc_attr($key) . '" ' . selected($val_category, $key, false) . '>' . esc_html($label) . '</option>';
		}
		echo '</select></label>';
		echo '<label style="display:flex;flex-direction:column;gap:4px;min-width:120px">' . esc_html__('Order', 'vachr-short-term-rental-reservations') . '<input type="number" name="sort_order" value="' . esc_attr($edit_data->sort_order ?? '0') . '" style="width:100%"></label>';
		echo '</div>';
		echo '<label style="display:flex;flex-direction:column;gap:4px;max-width:520px;margin-top:12px">' . esc_html__('Description', 'vachr-short-term-rental-reservations') . '<textarea name="description" rows="3" style="width:100%">' . esc_textarea($edit_data->description ?? '') . '</textarea></label>';

		$locales = self::get_language_options(false);
		?>
		<div style="width:100%;border-top:1px solid #ccc;margin-top:10px;padding-top:10px">
			<strong><?php esc_html_e('Translations (Standard Fields)', 'vachr-short-term-rental-reservations'); ?></strong>
			<p style="margin:8px 0 10px 0;color:#666"><?php esc_html_e('Global Name and Description are above. Fill locale-specific translations below if needed.', 'vachr-short-term-rental-reservations'); ?></p>
			<table class="widefat striped" style="max-width:980px">
				<thead><tr><th style="width:80px"><?php esc_html_e('Locale', 'vachr-short-term-rental-reservations'); ?></th><th><?php esc_html_e('Name Translation', 'vachr-short-term-rental-reservations'); ?></th><th><?php esc_html_e('Description Translation', 'vachr-short-term-rental-reservations'); ?></th></tr></thead>
				<tbody>
					<?php foreach ($locales as $loc_key => $loc_label): ?>
						<tr>
							<td><strong><?php echo esc_html($loc_label); ?></strong></td>
							<td><input type="text" name="label_i18n[<?php echo esc_attr($loc_key); ?>]" value="<?php echo esc_attr($amenity_label_i18n[$loc_key] ?? ''); ?>" style="width:100%" placeholder="<?php esc_attr_e('optional', 'vachr-short-term-rental-reservations'); ?>"></td>
							<td><textarea name="desc_i18n[<?php echo esc_attr($loc_key); ?>]" rows="2" style="width:100%" placeholder="<?php esc_attr_e('optional', 'vachr-short-term-rental-reservations'); ?>"><?php echo esc_textarea($amenity_desc_i18n[$loc_key] ?? ''); ?></textarea></td>
						</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
		</div>
		<?php
		echo '<div style="margin-top:14px;display:flex;gap:14px;align-items:center;flex-wrap:wrap">';
		echo '<label style="display:flex;align-items:center;gap:6px"><input type="checkbox" name="is_active" ' . checked((int) ($edit_data->is_active ?? 1), 1, false) . '> ' . esc_html__('active', 'vachr-short-term-rental-reservations') . '</label>';
		echo '<button class="button button-primary" name="vstrb_save_amenity" value="1">' . esc_html($btn) . '</button>';
		echo '</div></form>';

		$amenities = $wpdb->get_results(sprintf("SELECT * FROM %s ORDER BY sort_order ASC, category ASC, name ASC", $t['amenities']), ARRAY_A);
		echo '<h2>' . esc_html__('List', 'vachr-short-term-rental-reservations') . '</h2>';
		echo '<table class="widefat striped"><thead><tr><th>ID</th><th>' . esc_html__('Name', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Code', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Category', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Order', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Active', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Actions', 'vachr-short-term-rental-reservations') . '</th></tr></thead><tbody>';
		foreach ($amenities as $a) {
			$del_url = wp_nonce_url(add_query_arg(['action' => 'delete', 'id' => (int) $a['id']], $base_url), 'vstrb_delete_amenity');
			$edit_url = add_query_arg('edit_id', (int) $a['id'], $base_url);
			echo '<tr><td>' . esc_html($a['id']) . '</td><td>' . esc_html($a['name']) . '</td><td><code>' . esc_html($a['code']) . '</code></td><td>' . esc_html($categories[$a['category']] ?? $a['category']) . '</td><td>' . esc_html($a['sort_order']) . '</td><td>' . ($a['is_active'] ? esc_html__('yes', 'vachr-short-term-rental-reservations') : esc_html__('no', 'vachr-short-term-rental-reservations')) . '</td><td><a href="' . esc_url($edit_url) . '" class="button button-small">' . esc_html__('Edit', 'vachr-short-term-rental-reservations') . '</a> <a href="' . esc_url($del_url) . '" class="button button-small button-link-delete" onclick="return confirm(\'' . esc_js(__('Are you sure?', 'vachr-short-term-rental-reservations')) . '\')">' . esc_html__('Delete', 'vachr-short-term-rental-reservations') . '</a></td></tr>';
		}
		echo '</tbody></table>';
		if (!$embedded) echo '</div>';
		self::render_donation_box();
	}

	public static function page_rates() {
		global $wpdb;
		$t = VSTRB_DB::tables();

		// Self-healing: Check tables & columns
		if ($wpdb->get_var("SHOW TABLES LIKE '{$t['rates']}'") != $t['rates']) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table direct check.
			VSTRB_DB::migrate();
		} else {
			$required_rate_columns = ['operation', 'min_nights', 'highlight_in_calendar', 'is_active', 'apply_mode', 'months_mask', 'day_of_year_from', 'day_of_year_to', 'show_name_in_breakdown', 'breakdown_label_i18n'];
			foreach ($required_rate_columns as $required_col) {
				$col = $wpdb->get_results($wpdb->prepare("SHOW COLUMNS FROM {$t['rates']} LIKE %s", $required_col)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table direct check.
				if (empty($col)) {
					VSTRB_DB::migrate();
					break;
				}
			}
			$wpdb->query("ALTER TABLE {$t['rates']} MODIFY apply_mode ENUM('always','date_range','months_of_year','weekdays','annual_dates','days_of_year') NOT NULL DEFAULT 'date_range'"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Keep existing installs compatible with recurring rate scopes.
			$wpdb->query("ALTER TABLE {$t['rates']} MODIFY months_mask VARCHAR(255) NOT NULL DEFAULT ''"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Annual date scopes can store multiple date/range tokens.
		}

		echo '<div class="wrap"><h1>' . esc_html__('Rates & Discounts (Seasonal)', 'vachr-short-term-rental-reservations') . '</h1>';

		// Delete Action
		if (self::get_param_text('action', '') === 'delete' && self::get_param_int('id', 0) > 0) {
			check_admin_referer('vstrb_delete_rate');
			$id = self::get_param_int('id', 0);
			
			// Get the rate to identify its group
			$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['rates']} WHERE id=%d", $id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
			if ($row) {
				$row_min_nights = isset($row->min_nights) ? (int)$row->min_nights : 0;
				// Delete the rate and its "siblings" (same definition, different currency).
				$row_hl = isset($row->highlight_in_calendar) ? (int) $row->highlight_in_calendar : 0;
				$row_apply_mode = isset($row->apply_mode) ? self::sanitize_rate_apply_mode($row->apply_mode) : 'date_range';
				$row_months_mask = isset($row->months_mask) ? self::sanitize_rate_months_mask($row->months_mask) : '';
				$row_day_from = isset($row->day_of_year_from) ? (int) $row->day_of_year_from : 0;
				$row_day_to = isset($row->day_of_year_to) ? (int) $row->day_of_year_to : 0;
				$wpdb->query($wpdb->prepare(
					"DELETE FROM {$t['rates']} WHERE unit_id=%d AND name=%s AND date_from=%s AND date_to=%s AND rule=%s AND operation=%s AND priority=%d AND COALESCE(min_nights,0)=%d AND COALESCE(highlight_in_calendar,0)=%d AND COALESCE(apply_mode,'date_range')=%s AND COALESCE(months_mask,'')=%s AND COALESCE(day_of_year_from,0)=%d AND COALESCE(day_of_year_to,0)=%d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					$row->unit_id, $row->name, $row->date_from, $row->date_to, $row->rule, $row->operation, (int)$row->priority, $row_min_nights, $row_hl, $row_apply_mode, $row_months_mask, $row_day_from, $row_day_to
				)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table query.
				self::bump_rates_cache_version();
				echo '<div class="notice notice-success"><p>' . esc_html__('Rate group deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
			}
		}

		if (in_array(self::get_param_text('action', ''), ['toggle_active', 'clone'], true) && self::get_param_int('id', 0) > 0) {
			$action = self::get_param_text('action', '');
			check_admin_referer('vstrb_' . $action . '_rate');
			$id = self::get_param_int('id', 0);
			$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['rates']} WHERE id=%d", $id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
			if ($row) {
				$row_min_nights = isset($row->min_nights) ? (int)$row->min_nights : 0;
				$row_hl = isset($row->highlight_in_calendar) ? (int) $row->highlight_in_calendar : 0;
				$row_active = isset($row->is_active) ? (int) $row->is_active : 1;
				$row_apply_mode = isset($row->apply_mode) ? self::sanitize_rate_apply_mode($row->apply_mode) : 'date_range';
				$row_months_mask = isset($row->months_mask) ? self::sanitize_rate_months_mask($row->months_mask) : '';
				$row_day_from = isset($row->day_of_year_from) ? (int) $row->day_of_year_from : 0;
				$row_day_to = isset($row->day_of_year_to) ? (int) $row->day_of_year_to : 0;

				if ($action === 'toggle_active') {
					$new_active = self::get_param_int('enabled', 1) ? 1 : 0;
					$wpdb->query($wpdb->prepare(
						"UPDATE {$t['rates']} SET is_active=%d WHERE unit_id=%d AND name=%s AND date_from=%s AND date_to=%s AND rule=%s AND operation=%s AND priority=%d AND COALESCE(min_nights,0)=%d AND COALESCE(highlight_in_calendar,0)=%d AND COALESCE(is_active,1)=%d AND COALESCE(apply_mode,'date_range')=%s AND COALESCE(months_mask,'')=%s AND COALESCE(day_of_year_from,0)=%d AND COALESCE(day_of_year_to,0)=%d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
						$new_active, $row->unit_id, $row->name, $row->date_from, $row->date_to, $row->rule, $row->operation, (int)$row->priority, $row_min_nights, $row_hl, $row_active, $row_apply_mode, $row_months_mask, $row_day_from, $row_day_to
					)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table query.
					self::bump_rates_cache_version();
					echo '<div class="notice notice-success"><p>' . esc_html($new_active ? __('Rate group enabled.', 'vachr-short-term-rental-reservations') : __('Rate group disabled.', 'vachr-short-term-rental-reservations')) . '</p></div>';
				} elseif ($action === 'clone') {
					$siblings = $wpdb->get_results($wpdb->prepare(
						"SELECT * FROM {$t['rates']} WHERE unit_id=%d AND name=%s AND date_from=%s AND date_to=%s AND rule=%s AND operation=%s AND priority=%d AND COALESCE(min_nights,0)=%d AND COALESCE(highlight_in_calendar,0)=%d AND COALESCE(is_active,1)=%d AND COALESCE(apply_mode,'date_range')=%s AND COALESCE(months_mask,'')=%s AND COALESCE(day_of_year_from,0)=%d AND COALESCE(day_of_year_to,0)=%d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
						$row->unit_id, $row->name, $row->date_from, $row->date_to, $row->rule, $row->operation, (int)$row->priority, $row_min_nights, $row_hl, $row_active, $row_apply_mode, $row_months_mask, $row_day_from, $row_day_to
					), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table query.
					$copy_suffix = ' (' . __('copy', 'vachr-short-term-rental-reservations') . ' ' . gmdate('Ymd His') . ')';
					if ($siblings && is_array($siblings)) {
						foreach ($siblings as $sibling) {
							unset($sibling['id'], $sibling['created_at'], $sibling['updated_at']);
							$copy_name = (string) $sibling['name'] . $copy_suffix;
							$sibling['name'] = function_exists('mb_substr') ? mb_substr($copy_name, 0, 190) : substr($copy_name, 0, 190);
							$sibling['is_active'] = 0;
							$wpdb->insert($t['rates'], $sibling); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
						}
					}
					self::bump_rates_cache_version();
					echo '<div class="notice notice-success"><p>' . esc_html__('Rate group cloned as disabled. Edit it and enable it when ready.', 'vachr-short-term-rental-reservations') . '</p></div>';
				}
			}
		}

		$removed_duplicate_rates = $wpdb->query(
			"DELETE r2 FROM {$t['rates']} r1
			 INNER JOIN {$t['rates']} r2
				ON r1.id < r2.id
				AND r1.unit_id = r2.unit_id
				AND r1.name = r2.name
				AND r1.date_from = r2.date_from
				AND r1.date_to = r2.date_to
				AND COALESCE(r1.apply_mode,'date_range') = COALESCE(r2.apply_mode,'date_range')
				AND COALESCE(r1.months_mask,'') = COALESCE(r2.months_mask,'')
				AND COALESCE(r1.day_of_year_from,0) = COALESCE(r2.day_of_year_from,0)
				AND COALESCE(r1.day_of_year_to,0) = COALESCE(r2.day_of_year_to,0)
				AND r1.rule = r2.rule
				AND r1.operation = r2.operation
				AND r1.currency = r2.currency
				AND r1.priority = r2.priority
				AND COALESCE(r1.min_nights,0) = COALESCE(r2.min_nights,0)
				AND COALESCE(r1.is_active,1) = COALESCE(r2.is_active,1)
				AND COALESCE(r1.highlight_in_calendar,0) = COALESCE(r2.highlight_in_calendar,0)"
		); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Self-healing duplicate cleanup for custom rates table.
		if ((int) $removed_duplicate_rates > 0) {
			self::bump_rates_cache_version();
		}

		$avail_currencies = array_filter(array_map(static function ($currency) {
			$currency = strtoupper(trim((string) $currency));
			return preg_match('/^[A-Z]{3}$/', $currency) ? $currency : '';
		}, explode(',', get_option('vstrb_available_currencies', 'EUR'))));
		$avail_currencies = array_values(array_unique($avail_currencies));
		if (empty($avail_currencies)) {
			$avail_currencies = ['EUR'];
		}

		// Edit Data
		$edit_id = self::request_param_int('edit_id', 0);
		$edit_data = null;
		$sibling_map = []; // currency => id

		if ($edit_id) {
			$edit_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['rates']} WHERE id=%d", $edit_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
			if ($edit_data) {
				$edit_min_nights = isset($edit_data->min_nights) ? (int)$edit_data->min_nights : 0;
				// Find siblings (same unit, name, dates, rule, operation, priority, min_nights) to populate other currencies.
				$edit_hl = isset($edit_data->highlight_in_calendar) ? (int) $edit_data->highlight_in_calendar : 0;
				$edit_active = isset($edit_data->is_active) ? (int) $edit_data->is_active : 1;
				$edit_apply_mode = isset($edit_data->apply_mode) ? self::sanitize_rate_apply_mode($edit_data->apply_mode) : 'date_range';
				$edit_months_mask = isset($edit_data->months_mask) ? self::sanitize_rate_months_mask($edit_data->months_mask) : '';
				$edit_day_from = isset($edit_data->day_of_year_from) ? (int) $edit_data->day_of_year_from : 0;
				$edit_day_to = isset($edit_data->day_of_year_to) ? (int) $edit_data->day_of_year_to : 0;
				$siblings = $wpdb->get_results($wpdb->prepare(
					"SELECT id, currency, price FROM {$t['rates']} 
					 WHERE unit_id=%d AND name=%s AND date_from=%s AND date_to=%s AND rule=%s AND operation=%s AND priority=%d AND COALESCE(min_nights,0)=%d AND COALESCE(highlight_in_calendar,0)=%d AND COALESCE(is_active,1)=%d AND COALESCE(apply_mode,'date_range')=%s AND COALESCE(months_mask,'')=%s AND COALESCE(day_of_year_from,0)=%d AND COALESCE(day_of_year_to,0)=%d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					$edit_data->unit_id, $edit_data->name, $edit_data->date_from, $edit_data->date_to, $edit_data->rule, $edit_data->operation, (int)$edit_data->priority, $edit_min_nights, $edit_hl, $edit_active, $edit_apply_mode, $edit_months_mask, $edit_day_from, $edit_day_to
				)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
				if ($siblings && is_array($siblings)) {
					foreach ($siblings as $sib) {
						if (!isset($sibling_map[$sib->currency])) {
							$sibling_map[$sib->currency] = $sib;
						}
					}
				}
			}
		}

		// Save Action
		if (isset($_POST['vstrb_save_rate'])) {
			check_admin_referer('vstrb_rates', 'vstrb_rates_nonce');
			$min_nights_input = isset($_POST['min_nights']) ? (int) sanitize_text_field(wp_unslash($_POST['min_nights'])) : 0;
			$apply_mode_raw = isset($_POST['apply_mode']) ? sanitize_text_field(wp_unslash($_POST['apply_mode'])) : 'date_range';
			$months_of_year_raw = isset($_POST['months_of_year'])
				? array_map('sanitize_text_field', (array) wp_unslash($_POST['months_of_year']))
				: [];
			$weekdays_raw = isset($_POST['weekdays'])
				? array_map('sanitize_text_field', (array) wp_unslash($_POST['weekdays']))
				: [];
			$annual_dates_raw = isset($_POST['annual_dates']) ? sanitize_textarea_field(wp_unslash($_POST['annual_dates'])) : '';
			$day_of_year_from_raw = isset($_POST['day_of_year_from']) ? sanitize_text_field(wp_unslash($_POST['day_of_year_from'])) : '';
			$day_of_year_to_raw = isset($_POST['day_of_year_to']) ? sanitize_text_field(wp_unslash($_POST['day_of_year_to'])) : '';
			$apply_mode = self::sanitize_rate_apply_mode($apply_mode_raw);
			$months_mask = self::sanitize_rate_months_mask($months_of_year_raw);
			$weekdays_mask = self::sanitize_rate_weekdays_mask($weekdays_raw);
			$annual_dates_mask = self::sanitize_rate_annual_dates_mask($annual_dates_raw);
			$day_of_year_from = self::sanitize_day_of_year($day_of_year_from_raw);
			$day_of_year_to = self::sanitize_day_of_year($day_of_year_to_raw);
			$no_end_date = self::post_checkbox_enabled('no_end_date') === 1;
			$date_from_input = sanitize_text_field(wp_unslash($_POST['date_from'] ?? ''));
			$date_to_input = sanitize_text_field(wp_unslash($_POST['date_to'] ?? ''));
			$save_error = '';

			$date_from = '1970-01-01';
			$date_to = '9999-12-31';
			if ($apply_mode === 'always') {
				$date_from = '1970-01-01';
				$date_to = '9999-12-31';
			} elseif ($apply_mode === 'date_range') {
				$date_from = $date_from_input;
				$date_to = $no_end_date ? '9999-12-31' : $date_to_input;
				if (!self::is_valid_ymd($date_from)) {
					$save_error = esc_html__("Please fill a valid 'From' date.", 'vachr-short-term-rental-reservations');
				} elseif ($date_to !== '9999-12-31' && (!self::is_valid_ymd($date_to) || strtotime($date_to) <= strtotime($date_from))) {
					$save_error = esc_html__("Please fill a valid 'To' date later than 'From'.", 'vachr-short-term-rental-reservations');
				}
			} elseif ($apply_mode === 'months_of_year') {
				if ($months_mask === '') {
					$save_error = esc_html__('Select at least one month.', 'vachr-short-term-rental-reservations');
				}
			} elseif ($apply_mode === 'weekdays') {
				if ($weekdays_mask === '') {
					$save_error = esc_html__('Select at least one weekday.', 'vachr-short-term-rental-reservations');
				}
			} elseif ($apply_mode === 'annual_dates') {
				if ($annual_dates_mask === '') {
					$save_error = esc_html__('Fill at least one valid annual date or date range.', 'vachr-short-term-rental-reservations');
				}
			} elseif ($apply_mode === 'days_of_year') {
				if ($day_of_year_from === null || $day_of_year_to === null) {
					$save_error = esc_html__('Fill valid day-of-year values (1-366).', 'vachr-short-term-rental-reservations');
				}
			}

			$common_data = [
				'unit_id' => isset($_POST['unit_id']) ? (int) sanitize_text_field(wp_unslash($_POST['unit_id'])) : 0,
				'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')),
				'date_from' => $date_from,
				'date_to' => $date_to,
				'apply_mode' => $apply_mode,
				'months_mask' => $apply_mode === 'months_of_year' ? $months_mask : ($apply_mode === 'weekdays' ? $weekdays_mask : ($apply_mode === 'annual_dates' ? $annual_dates_mask : '')),
				'day_of_year_from' => $apply_mode === 'days_of_year' ? $day_of_year_from : null,
				'day_of_year_to' => $apply_mode === 'days_of_year' ? $day_of_year_to : null,
				'rule' => sanitize_text_field(wp_unslash($_POST['rule'] ?? 'per_unit_night')),
				'operation' => sanitize_text_field(wp_unslash($_POST['operation'] ?? 'replace')),
				'min_nights' => $min_nights_input > 0 ? $min_nights_input : null,
				'priority' => isset($_POST['priority']) ? (int) sanitize_text_field(wp_unslash($_POST['priority'])) : 100,
				'highlight_in_calendar' => self::post_checkbox_enabled('highlight_in_calendar'),
				'is_active' => self::post_checkbox_enabled('is_active'),
				'show_name_in_breakdown' => self::post_checkbox_enabled('show_name_in_breakdown'),
				'breakdown_label_i18n' => '',
			];

			$allowed_rate_locales = array_keys(self::get_language_options(false));
			$posted_rate_labels = isset($_POST['rate_label_i18n']) ? (array) map_deep(wp_unslash($_POST['rate_label_i18n']), 'sanitize_text_field') : [];
			$rate_labels_clean = [];
			foreach ($posted_rate_labels as $loc_code => $loc_val) {
				$loc_code = sanitize_text_field((string) $loc_code);
				if (!in_array($loc_code, $allowed_rate_locales, true)) {
					continue;
				}
				$label_val = sanitize_text_field((string) $loc_val);
				if ($label_val === '') {
					continue;
				}
				$rate_labels_clean[$loc_code] = $label_val;
			}
			$common_data['breakdown_label_i18n'] = !empty($rate_labels_clean) ? wp_json_encode($rate_labels_clean) : '';

			if ($save_error !== '') {
				echo '<div class="notice notice-error"><p>' . esc_html($save_error) . '</p></div>';
			} else {
				// 1. If edit_id is set, determine target group via original data.
				$targets = [];
				$duplicate_target_ids = [];
				 if ($edit_id) {
					$orig = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['rates']} WHERE id=%d", $edit_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
					if ($orig) {
						$orig_min_nights = isset($orig->min_nights) ? (int)$orig->min_nights : 0;
						$orig_hl = isset($orig->highlight_in_calendar) ? (int) $orig->highlight_in_calendar : 0;
						$orig_active = isset($orig->is_active) ? (int) $orig->is_active : 1;
						$orig_apply_mode = isset($orig->apply_mode) ? self::sanitize_rate_apply_mode($orig->apply_mode) : 'date_range';
						$orig_months_mask = isset($orig->months_mask) ? self::sanitize_rate_months_mask($orig->months_mask) : '';
						$orig_day_from = isset($orig->day_of_year_from) ? (int) $orig->day_of_year_from : 0;
						$orig_day_to = isset($orig->day_of_year_to) ? (int) $orig->day_of_year_to : 0;
						 $sibs = $wpdb->get_results($wpdb->prepare(
							"SELECT id, currency FROM {$t['rates']} 
							 WHERE unit_id=%d AND name=%s AND date_from=%s AND date_to=%s AND rule=%s AND operation=%s AND priority=%d AND COALESCE(min_nights,0)=%d AND COALESCE(highlight_in_calendar,0)=%d AND COALESCE(is_active,1)=%d AND COALESCE(apply_mode,'date_range')=%s AND COALESCE(months_mask,'')=%s AND COALESCE(day_of_year_from,0)=%d AND COALESCE(day_of_year_to,0)=%d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
							$orig->unit_id, $orig->name, $orig->date_from, $orig->date_to, $orig->rule, $orig->operation, (int)$orig->priority, $orig_min_nights, $orig_hl, $orig_active, $orig_apply_mode, $orig_months_mask, $orig_day_from, $orig_day_to
						)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table query.
						if ($sibs && is_array($sibs)) {
							foreach ($sibs as $s) {
								$currency_key = strtoupper(trim((string) $s->currency));
								if (!isset($targets[$currency_key])) {
									$targets[$currency_key] = (int) $s->id;
								} else {
									$duplicate_target_ids[] = (int) $s->id;
								}
							}
						}
					}
				}
				foreach ($duplicate_target_ids as $duplicate_id) {
					if ($duplicate_id > 0) {
						$wpdb->delete($t['rates'], ['id' => $duplicate_id], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
					}
				}

				foreach ($avail_currencies as $curr) {
					$price_val = isset($_POST['price_' . $curr]) ? self::parse_decimal_input(sanitize_text_field(wp_unslash($_POST['price_' . $curr])), 0) : 0;
					
					$row_data = array_merge($common_data, [
						'price' => $price_val,
						'currency' => $curr
					]);

					if (isset($targets[$curr])) {
						$wpdb->update($t['rates'], $row_data, ['id' => $targets[$curr]]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
						unset($targets[$curr]);
					} else {
						$existing_ids = $wpdb->get_col($wpdb->prepare(
							"SELECT id FROM {$t['rates']}
							 WHERE unit_id=%d AND name=%s AND date_from=%s AND date_to=%s AND rule=%s AND operation=%s AND priority=%d AND COALESCE(min_nights,0)=%d AND COALESCE(highlight_in_calendar,0)=%d AND COALESCE(is_active,1)=%d AND COALESCE(apply_mode,'date_range')=%s AND COALESCE(months_mask,'')=%s AND COALESCE(day_of_year_from,0)=%d AND COALESCE(day_of_year_to,0)=%d AND currency=%s
							 ORDER BY id ASC", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
							$common_data['unit_id'],
							$common_data['name'],
							$common_data['date_from'],
							$common_data['date_to'],
							$common_data['rule'],
							$common_data['operation'],
							(int) $common_data['priority'],
							!empty($common_data['min_nights']) ? (int) $common_data['min_nights'] : 0,
							(int) $common_data['highlight_in_calendar'],
							(int) $common_data['is_active'],
							$common_data['apply_mode'],
							$common_data['months_mask'],
							!empty($common_data['day_of_year_from']) ? (int) $common_data['day_of_year_from'] : 0,
							!empty($common_data['day_of_year_to']) ? (int) $common_data['day_of_year_to'] : 0,
							$curr
						)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table query.
						if (!empty($existing_ids)) {
							$keep_id = (int) array_shift($existing_ids);
							$wpdb->update($t['rates'], $row_data, ['id' => $keep_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
							foreach ($existing_ids as $duplicate_id) {
								$wpdb->delete($t['rates'], ['id' => (int) $duplicate_id], ['%d']); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
							}
						} else {
							$wpdb->insert($t['rates'], $row_data); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
						}
					}
				}
				self::bump_rates_cache_version();
				
				echo '<div class="notice notice-success"><p>' . esc_html__('Saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
				
				// Clear edit mode
				$edit_id = 0; 
				$edit_data = null;
				$sibling_map = [];
			}
		}

		// Form Defaults
		$val_unit = $edit_data
			? (int) $edit_data->unit_id
			: ((isset($_POST['vstrb_save_rate']) || self::get_param_int('unit_id', 0) > 0) ? self::request_param_int('unit_id', 0) : self::resolve_unit_selection(0));
		$val_name = $edit_data ? $edit_data->name : '';
		$val_from = $edit_data ? $edit_data->date_from : '';
		$val_to   = $edit_data ? $edit_data->date_to : '';
		$val_apply_mode = $edit_data ? self::sanitize_rate_apply_mode($edit_data->apply_mode ?? 'date_range') : 'date_range';
		$val_months_mask = $edit_data ? self::sanitize_rate_months_mask($edit_data->months_mask ?? '') : '';
		$val_months = self::parse_rate_months_mask($val_months_mask);
		$val_weekdays_mask = $edit_data ? self::sanitize_rate_weekdays_mask($edit_data->months_mask ?? '') : '';
		$val_weekdays = self::parse_rate_weekdays_mask($val_weekdays_mask);
		$val_annual_dates = $edit_data ? self::format_rate_annual_dates_mask($edit_data->months_mask ?? '') : '';
		$val_day_of_year_from = ($edit_data && !empty($edit_data->day_of_year_from)) ? (int)$edit_data->day_of_year_from : '';
		$val_day_of_year_to = ($edit_data && !empty($edit_data->day_of_year_to)) ? (int)$edit_data->day_of_year_to : '';
		$val_no_end_date = ($val_apply_mode === 'date_range' && $val_to === '9999-12-31');
		if ($val_no_end_date) $val_to = '';
		if ($val_apply_mode !== 'date_range') {
			$val_from = '';
			$val_to = '';
		}
		$val_rule = $edit_data ? $edit_data->rule : 'per_unit_night';
		$val_op   = $edit_data ? ($edit_data->operation ?? 'replace') : 'replace';
		$val_min_nights = ($edit_data && !empty($edit_data->min_nights)) ? (int)$edit_data->min_nights : '';
		$val_prio = $edit_data ? $edit_data->priority : 100;
		$val_active = $edit_data ? !empty($edit_data->is_active) : true;
		$val_show_name_in_breakdown = $edit_data ? !empty($edit_data->show_name_in_breakdown) : false;
		$val_rate_label_i18n = [];
		if ($edit_data && !empty($edit_data->breakdown_label_i18n)) {
			$decoded_rate_labels = json_decode((string) $edit_data->breakdown_label_i18n, true);
			if (is_array($decoded_rate_labels)) {
				foreach ($decoded_rate_labels as $loc_code => $loc_label) {
					$loc_key = sanitize_text_field((string) $loc_code);
					if ($loc_key === '') continue;
					$val_rate_label_i18n[$loc_key] = sanitize_text_field((string) $loc_label);
				}
			}
		}
		$rate_label_locales = self::get_language_options(false);
		$val_hl = $edit_data
			? !empty($edit_data->highlight_in_calendar)
			: in_array($val_op, ['discount_percent', 'discount_amount', 'discount_amount_stay', 'discount_percent_stay', 'surcharge_percent', 'surcharge_amount', 'surcharge_amount_stay', 'surcharge_percent_stay'], true);

		$lbl = $edit_id ? esc_html__('Edit Rate', 'vachr-short-term-rental-reservations') : esc_html__('Add Rate/Discount', 'vachr-short-term-rental-reservations');
		$btn = $edit_id ? esc_html__('Update All Currencies', 'vachr-short-term-rental-reservations') : esc_html__('Add', 'vachr-short-term-rental-reservations');

		$form_action = admin_url('admin.php?page=vstrb-rates');
		echo '<form method="post" action="' . esc_url($form_action) . '">';
		wp_nonce_field('vstrb_rates', 'vstrb_rates_nonce');
		if ($edit_id) {
			echo '<input type="hidden" name="edit_id" value="' . esc_attr((int) $edit_id) . '">';
		}
		echo '<h2>' . esc_html($lbl) . ($edit_id ? ' <a href="'.esc_url(admin_url('admin.php?page=vstrb-rates')).'" class="page-title-action">'.esc_html__('Cancel', 'vachr-short-term-rental-reservations').'</a>' : '') . '</h2>';
		
		echo '<div style="background:#fff;padding:15px;border:1px solid #ccd0d4;max-width:800px">';
		
		echo '<p><label><strong>' . esc_html__('Unit', 'vachr-short-term-rental-reservations') . '</strong><br>';
		self::unit_select($val_unit, true);
		echo '<br><span class="description">' . esc_html__('Choose All apartments to create a global rate. Apartment-specific matching rates override global rates.', 'vachr-short-term-rental-reservations') . '</span>';
		echo '</label></p>';
        echo '<p><label><input type="checkbox" name="is_active" value="1" ' . checked($val_active, true, false) . '> ' . esc_html__('Enabled', 'vachr-short-term-rental-reservations') . '</label> <span class="description">' . esc_html__('Disable to pause this rate without deleting it.', 'vachr-short-term-rental-reservations') . '</span></p>';

		echo '<p><label><strong>' . esc_html__('Name', 'vachr-short-term-rental-reservations') . '</strong> ' . esc_html__('(e.g. Summer Season, Long Stay Discount)', 'vachr-short-term-rental-reservations') . '<br><input name="name" required value="'.esc_attr($val_name).'" style="width:100%"></label></p>';
		echo '<p><label><input type="checkbox" name="show_name_in_breakdown" value="1" ' . checked($val_show_name_in_breakdown, true, false) . '> ' . esc_html__('Show this rate name in calculator breakdown', 'vachr-short-term-rental-reservations') . '</label></p>';
		echo '<details style="margin:8px 0 15px 0;border:1px solid #e5e5e5;background:#fafafa;padding:8px 10px">';
		echo '<summary style="cursor:pointer;font-weight:600">' . esc_html__('Rate name translations for calculator', 'vachr-short-term-rental-reservations') . '</summary>';
		echo '<p class="description" style="margin:10px 0">' . esc_html__('Used only when \'Show this rate name in calculator breakdown\' is enabled. Empty value = fallback to Name.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<table class="widefat striped" style="max-width:560px"><thead><tr><th style="width:90px">' . esc_html__('Language', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Translated Name', 'vachr-short-term-rental-reservations') . '</th></tr></thead><tbody>';
		foreach ($rate_label_locales as $loc_key => $loc_label) {
			echo '<tr><td><strong>' . esc_html($loc_label) . '</strong></td><td><input type="text" name="rate_label_i18n[' . esc_attr($loc_key) . ']" value="' . esc_attr($val_rate_label_i18n[$loc_key] ?? '') . '" style="width:100%" placeholder="' . esc_attr__('optional', 'vachr-short-term-rental-reservations') . '"></td></tr>';
		}
		echo '</tbody></table>';
		echo '</details>';

		echo '<p><label><strong>' . esc_html__('When should this apply?', 'vachr-short-term-rental-reservations') . '</strong><br>';
		echo '<select id="vstrb-rate-apply-mode" name="apply_mode">';
		echo '<option value="always" '.selected($val_apply_mode,'always',false).'>'.esc_html__('Always', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="date_range" '.selected($val_apply_mode,'date_range',false).'>'.esc_html__('Specific dates', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="months_of_year" '.selected($val_apply_mode,'months_of_year',false).'>'.esc_html__('Selected months every year', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="weekdays" '.selected($val_apply_mode,'weekdays',false).'>'.esc_html__('Selected weekdays', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="annual_dates" '.selected($val_apply_mode,'annual_dates',false).'>'.esc_html__('Annual dates or ranges', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="days_of_year" '.selected($val_apply_mode,'days_of_year',false).'>'.esc_html__('Day-of-year range every year', 'vachr-short-term-rental-reservations').'</option>';
		echo '</select></label></p>';

		echo '<div id="vstrb-rate-date-mode-wrap">';
		echo '<div style="display:flex;gap:10px;flex-wrap:wrap">';
		echo '<div><label><strong>' . esc_html__('From', 'vachr-short-term-rental-reservations') . '</strong><br><input type="date" id="vstrb-rate-date-from" name="date_from" value="'.esc_attr($val_from).'"></label></div>';
		echo '<div id="vstrb-rate-date-to-wrap"><label><strong>' . esc_html__('To', 'vachr-short-term-rental-reservations') . '</strong><br><input type="date" id="vstrb-rate-date-to" name="date_to" value="'.esc_attr($val_to).'"></label></div>';
		echo '</div>';
		echo '<p><label><input type="checkbox" id="vstrb-rate-no-end" name="no_end_date" value="1" '.checked($val_no_end_date, true, false).'> ' . esc_html__('No end date (valid indefinitely)', 'vachr-short-term-rental-reservations') . '</label></p>';
		echo '</div>';

		echo '<div id="vstrb-rate-months-mode-wrap" style="display:none">';
		echo '<p><strong>' . esc_html__('Months in year', 'vachr-short-term-rental-reservations') . '</strong></p>';
		$month_labels = [
			1 => esc_html__('Jan', 'vachr-short-term-rental-reservations'),
			2 => esc_html__('Feb', 'vachr-short-term-rental-reservations'),
			3 => esc_html__('Mar', 'vachr-short-term-rental-reservations'),
			4 => esc_html__('Apr', 'vachr-short-term-rental-reservations'),
			5 => esc_html__('May', 'vachr-short-term-rental-reservations'),
			6 => esc_html__('Jun', 'vachr-short-term-rental-reservations'),
			7 => esc_html__('Jul', 'vachr-short-term-rental-reservations'),
			8 => esc_html__('Aug', 'vachr-short-term-rental-reservations'),
			9 => esc_html__('Sep', 'vachr-short-term-rental-reservations'),
			10 => esc_html__('Oct', 'vachr-short-term-rental-reservations'),
			11 => esc_html__('Nov', 'vachr-short-term-rental-reservations'),
			12 => esc_html__('Dec', 'vachr-short-term-rental-reservations'),
		];
		echo '<div style="display:flex;gap:10px;flex-wrap:wrap;max-width:520px">';
		foreach ($month_labels as $m_num => $m_label) {
			echo '<label style="min-width:72px"><input type="checkbox" class="vstrb-rate-month" name="months_of_year[]" value="' . esc_attr($m_num) . '" ' . checked(in_array($m_num, $val_months, true), true, false) . '> ' . esc_html($m_label) . '</label>';
		}
		echo '</div>';
		echo '</div>';

		echo '<div id="vstrb-rate-weekdays-mode-wrap" style="display:none">';
		echo '<p><strong>' . esc_html__('Weekdays', 'vachr-short-term-rental-reservations') . '</strong></p>';
		$weekday_labels = [
			1 => esc_html__('Mon', 'vachr-short-term-rental-reservations'),
			2 => esc_html__('Tue', 'vachr-short-term-rental-reservations'),
			3 => esc_html__('Wed', 'vachr-short-term-rental-reservations'),
			4 => esc_html__('Thu', 'vachr-short-term-rental-reservations'),
			5 => esc_html__('Fri', 'vachr-short-term-rental-reservations'),
			6 => esc_html__('Sat', 'vachr-short-term-rental-reservations'),
			7 => esc_html__('Sun', 'vachr-short-term-rental-reservations'),
		];
		echo '<div style="display:flex;gap:10px;flex-wrap:wrap;max-width:520px">';
		foreach ($weekday_labels as $d_num => $d_label) {
			echo '<label style="min-width:72px"><input type="checkbox" class="vstrb-rate-weekday" name="weekdays[]" value="' . esc_attr($d_num) . '" ' . checked(in_array($d_num, $val_weekdays, true), true, false) . '> ' . esc_html($d_label) . '</label>';
		}
		echo '</div>';
		echo '<p class="description">' . esc_html__('Applies by the start date of each billed night.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '</div>';

		echo '<div id="vstrb-rate-annual-dates-mode-wrap" style="display:none">';
		echo '<p><label><strong>' . esc_html__('Annual dates or ranges', 'vachr-short-term-rental-reservations') . '</strong><br>';
		echo '<textarea id="vstrb-rate-annual-dates" name="annual_dates" rows="3" style="width:100%;max-width:560px" placeholder="' . esc_attr__('1.5., 8.5., 24.12.-26.12.', 'vachr-short-term-rental-reservations') . '">' . esc_textarea($val_annual_dates) . '</textarea></label></p>';
		echo '<p class="description">' . esc_html__('Use day.month dates without a year. Separate multiple dates or ranges with commas or new lines.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '</div>';

		echo '<div id="vstrb-rate-days-mode-wrap" style="display:none">';
		echo '<div style="display:flex;gap:10px;flex-wrap:wrap">';
		echo '<div><label><strong>' . esc_html__('Day from (1-366)', 'vachr-short-term-rental-reservations') . '</strong><br><input type="number" min="1" max="366" id="vstrb-rate-day-from" name="day_of_year_from" value="'.esc_attr($val_day_of_year_from).'" style="width:120px"></label></div>';
		echo '<div><label><strong>' . esc_html__('Day to (1-366)', 'vachr-short-term-rental-reservations') . '</strong><br><input type="number" min="1" max="366" id="vstrb-rate-day-to" name="day_of_year_to" value="'.esc_attr($val_day_of_year_to).'" style="width:120px"></label></div>';
		echo '</div>';
		echo '<p class="description">' . esc_html__('Supports wrap-around ranges, e.g. 350 to 20.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '</div>';

		echo '<p><strong>' . esc_html__('Type & Value', 'vachr-short-term-rental-reservations') . '</strong></p>';
		echo '<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;margin-bottom:10px">';
		
		echo '<div><label>' . esc_html__('Operation', 'vachr-short-term-rental-reservations') . '<br>';
		echo '<select id="vstrb-rate-operation" name="operation">';
		echo '<option value="replace" '.selected($val_op,'replace',false).'>'.esc_html__('Price Override (Standard)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="discount_percent" '.selected($val_op,'discount_percent',false).'>'.esc_html__('Discount (%)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="discount_amount" '.selected($val_op,'discount_amount',false).'>'.esc_html__('Discount (Fixed Amount)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="discount_amount_stay" '.selected($val_op,'discount_amount_stay',false).'>'.esc_html__('Discount (Fixed Amount per Stay)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="discount_percent_stay" '.selected($val_op,'discount_percent_stay',false).'>'.esc_html__('Discount (% per Stay)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="surcharge_percent" '.selected($val_op,'surcharge_percent',false).'>'.esc_html__('Surcharge (%)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="surcharge_amount" '.selected($val_op,'surcharge_amount',false).'>'.esc_html__('Surcharge (Fixed Amount)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="surcharge_amount_stay" '.selected($val_op,'surcharge_amount_stay',false).'>'.esc_html__('Surcharge (Fixed Amount per Stay)', 'vachr-short-term-rental-reservations').'</option>';
		echo '<option value="surcharge_percent_stay" '.selected($val_op,'surcharge_percent_stay',false).'>'.esc_html__('Surcharge (% per Stay)', 'vachr-short-term-rental-reservations').'</option>';
		echo '</select></label></div>';
		echo '<div><label><input type="checkbox" id="vstrb-rate-highlight" name="highlight_in_calendar" value="1" '.checked($val_hl, true, false).'> '.esc_html__('Highlight in calendar', 'vachr-short-term-rental-reservations').'</label></div>';
		echo '</div>'; // end op row

		echo '<div style="background:#f9f9f9;padding:10px;border:1px solid #eee;margin-bottom:15px">';
		echo '<strong>' . esc_html__('Values per Currency:', 'vachr-short-term-rental-reservations') . '</strong><br>';
		echo '<div style="display:flex;gap:15px;flex-wrap:wrap;margin-top:5px">';
		foreach ($avail_currencies as $curr) {
			$val = '';
			if (isset($sibling_map[$curr])) {
				$val = $sibling_map[$curr]->price;
			}
			echo '<div><label>' . esc_html($curr) . '<br><input type="number" step="0.01" name="price_'.esc_attr($curr).'" value="'.esc_attr($val).'" placeholder="0.00" style="width:100px"></label></div>';
		}
		echo '</div></div>';

		echo '<p><strong>' . esc_html__('Application Rule', 'vachr-short-term-rental-reservations') . '</strong> <span class="description">(' . sprintf(
			/* translators: 1: per unit/night rule, 2: weekend rule */
			esc_html__('Most discounts should use %1$s or %2$s', 'vachr-short-term-rental-reservations'), 
			esc_html__('per unit/night', 'vachr-short-term-rental-reservations'), 
			esc_html__('weekend', 'vachr-short-term-rental-reservations')
		) . ')</span></p>';
		echo '<div style="display:flex;gap:10px;align-items:center">';
		echo '<select name="rule">';
		$rules = [
			'per_unit_night' => esc_html__('Every Night (per unit)', 'vachr-short-term-rental-reservations'),
			'per_person_night' => esc_html__('Every Night (per person)', 'vachr-short-term-rental-reservations'),
			'weekend' => esc_html__('Weekend nights only (Fri-Sat, Sat-Sun)', 'vachr-short-term-rental-reservations'),
			'workday' => esc_html__('Workdays Only (Mon-Thu)', 'vachr-short-term-rental-reservations'),
			'christmas' => esc_html__('Christmas (Dec 24-26)', 'vachr-short-term-rental-reservations'),
			'new_year' => esc_html__('New Year (Dec 31)', 'vachr-short-term-rental-reservations'),
			'winter' => esc_html__('Winter Season (Dec-Mar)', 'vachr-short-term-rental-reservations'),
			'summer' => esc_html__('Summer Season (Jul-Aug)', 'vachr-short-term-rental-reservations'),
			'off_season' => esc_html__('Off-season (Apr-Jun, Sep-Nov)', 'vachr-short-term-rental-reservations'),
			'fixed' => esc_html__('One-time Fixed (legacy)', 'vachr-short-term-rental-reservations'),
			'single_night' => esc_html__('Single Night Only', 'vachr-short-term-rental-reservations'),
		];
		foreach ($rules as $k=>$v) echo '<option value="'.esc_attr($k).'" '.selected($val_rule, $k, false).'>'.esc_html($v).'</option>';
		echo '</select>';

		echo '<label>' . esc_html__('Minimum Stay (nights)', 'vachr-short-term-rental-reservations') . ' <input type="number" min="1" name="min_nights" value="'.esc_attr($val_min_nights).'" style="width:70px"> <span class="description">'.esc_html__('(Leave empty = applies to any stay length)', 'vachr-short-term-rental-reservations').'</span></label>';
		echo '<label>' . esc_html__('Priority', 'vachr-short-term-rental-reservations') . ' <input type="number" name="priority" value="'.esc_attr($val_prio).'" style="width:60px"> <span class="description">'.esc_html__('(Lower = applied first when Min Nights is the same)', 'vachr-short-term-rental-reservations').'</span></label>';
		echo '</div>';

		echo '<p><button class="button button-primary" name="vstrb_save_rate" value="1">' . esc_html($btn) . '</button>';
		if ($edit_id) echo ' <a href="'.esc_url(admin_url('admin.php?page=vstrb-rates')).'" class="button">'.esc_html__('Cancel', 'vachr-short-term-rental-reservations').'</a>';
		echo '</p>';
		
		echo '</div>'; // end container
		echo '</form>';

		// List
		$sql_rates = "SELECT r.*, p.post_title FROM {$t['rates']} r LEFT JOIN {$wpdb->posts} p ON p.ID=r.unit_id ORDER BY r.unit_id ASC, r.date_from DESC, r.name ASC";
		$rates = $wpdb->get_results($sql_rates, ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
		
		// Group rates
		$groups = [];
		if ($rates && is_array($rates)) {
			foreach ($rates as $r) {
				$key = implode('|', [$r['unit_id'], $r['name'], $r['date_from'], $r['date_to'], ($r['apply_mode'] ?? 'date_range'), ($r['months_mask'] ?? ''), ($r['day_of_year_from'] ?? 0), ($r['day_of_year_to'] ?? 0), $r['rule'], ($r['operation'] ?? 'replace'), $r['priority'], ($r['min_nights'] ?? 0), (int)($r['highlight_in_calendar'] ?? 0), (int)($r['is_active'] ?? 1)]);
				if (!isset($groups[$key])) {
					$groups[$key] = $r; // keep main info
					$groups[$key]['price_html'] = [];
				}
				
				$val_display = esc_html($r['price']);
				if (in_array(($r['operation'] ?? ''), ['discount_percent', 'surcharge_percent', 'discount_percent_stay', 'surcharge_percent_stay'], true)) $val_display .= '% ' . esc_html($r['currency']);
				else $val_display .= ' ' . esc_html($r['currency']);
				
				$groups[$key]['price_html'][] = $val_display;
				$groups[$key]['id'] = $r['id']; // Use representative ID
			}
		}

		echo '<h2>' . esc_html__('List', 'vachr-short-term-rental-reservations') . '</h2>';
		echo '<table class="widefat striped"><thead><tr>
				<th>'.esc_html__('Unit', 'vachr-short-term-rental-reservations').'</th>
				<th>' . esc_html__('Name', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Status', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Dates', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Type', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Highlight', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Values', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Rule', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Min Nights', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Prio', 'vachr-short-term-rental-reservations') . '</th>
				<th>' . esc_html__('Actions', 'vachr-short-term-rental-reservations') . '</th>
			  </tr></thead><tbody>';
		
		foreach ($groups as $g) {
			$del_url = wp_nonce_url(admin_url("admin.php?page=vstrb-rates&action=delete&id=".(int)$g['id']), 'vstrb_delete_rate');
			$edit_url = admin_url("admin.php?page=vstrb-rates&edit_id=".(int)$g['id']);
			$clone_url = wp_nonce_url(admin_url("admin.php?page=vstrb-rates&action=clone&id=".(int)$g['id']), 'vstrb_clone_rate');
			$is_active = !isset($g['is_active']) || (int) $g['is_active'] === 1;
			$toggle_url = wp_nonce_url(admin_url("admin.php?page=vstrb-rates&action=toggle_active&id=".(int)$g['id']."&enabled=".($is_active ? '0' : '1')), 'vstrb_toggle_active_rate');

			$op_label = $g['operation'] ?? 'replace';
			if ($op_label === 'replace') $op_label = esc_html__('Override', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'discount_percent') $op_label = esc_html__('Discount %', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'discount_amount') $op_label = esc_html__('Discount $', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'discount_amount_stay') $op_label = esc_html__('Discount $ (per stay)', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'discount_percent_stay') $op_label = esc_html__('Discount % (per stay)', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'surcharge_percent') $op_label = esc_html__('Surcharge %', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'surcharge_amount') $op_label = esc_html__('Surcharge $', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'surcharge_amount_stay') $op_label = esc_html__('Surcharge $ (per stay)', 'vachr-short-term-rental-reservations');
			elseif ($op_label === 'surcharge_percent_stay') $op_label = esc_html__('Surcharge % (per stay)', 'vachr-short-term-rental-reservations');
			$apply_mode = self::sanitize_rate_apply_mode($g['apply_mode'] ?? 'date_range');
			$to_display = ($g['date_to'] === '9999-12-31') ? 'unlimited' : $g['date_to'];
			$date_scope = $g['date_from'] . ' -> ' . $to_display;
			if ($apply_mode === 'always') {
				$date_scope = esc_html__('Always', 'vachr-short-term-rental-reservations');
			} elseif ($apply_mode === 'months_of_year') {
				$months = self::parse_rate_months_mask($g['months_mask'] ?? '');
				$date_scope = esc_html__('Months:', 'vachr-short-term-rental-reservations') . ' ' . esc_html(implode(', ', $months));
			} elseif ($apply_mode === 'weekdays') {
				$weekdays = self::parse_rate_weekdays_mask($g['months_mask'] ?? '');
				$weekday_names = [
					1 => esc_html__('Mon', 'vachr-short-term-rental-reservations'),
					2 => esc_html__('Tue', 'vachr-short-term-rental-reservations'),
					3 => esc_html__('Wed', 'vachr-short-term-rental-reservations'),
					4 => esc_html__('Thu', 'vachr-short-term-rental-reservations'),
					5 => esc_html__('Fri', 'vachr-short-term-rental-reservations'),
					6 => esc_html__('Sat', 'vachr-short-term-rental-reservations'),
					7 => esc_html__('Sun', 'vachr-short-term-rental-reservations'),
				];
				$weekday_labels = [];
				foreach ($weekdays as $weekday) {
					if (isset($weekday_names[$weekday])) {
						$weekday_labels[] = $weekday_names[$weekday];
					}
				}
				$date_scope = esc_html__('Weekdays:', 'vachr-short-term-rental-reservations') . ' ' . esc_html(implode(', ', $weekday_labels));
			} elseif ($apply_mode === 'annual_dates') {
				$date_scope = esc_html__('Annual dates:', 'vachr-short-term-rental-reservations') . ' ' . esc_html(self::format_rate_annual_dates_mask($g['months_mask'] ?? ''));
			} elseif ($apply_mode === 'days_of_year') {
				$from_day = !empty($g['day_of_year_from']) ? (int) $g['day_of_year_from'] : 1;
				$to_day = !empty($g['day_of_year_to']) ? (int) $g['day_of_year_to'] : 366;
				$date_scope = esc_html__('Days in year:', 'vachr-short-term-rental-reservations') . ' ' . esc_html($from_day . ' - ' . $to_day);
			}

			echo '<tr>
				<td>'.esc_html(((int) $g['unit_id'] === 0) ? __('All apartments', 'vachr-short-term-rental-reservations') : ($g['post_title'] ?: $g['unit_id'])).'</td>
				<td><strong>'.esc_html($g['name']).'</strong></td>
				<td>'.($is_active ? esc_html__('Enabled', 'vachr-short-term-rental-reservations') : esc_html__('Disabled', 'vachr-short-term-rental-reservations')).'</td>
				<td>'.esc_html($date_scope).'</td>
				<td>'.esc_html($op_label).'</td>
				<td>'.(!empty($g['highlight_in_calendar']) ? esc_html__('yes', 'vachr-short-term-rental-reservations') : esc_html__('no', 'vachr-short-term-rental-reservations')).'</td>
				<td>'.implode('<br>', array_map('esc_html', $g['price_html'])).'</td>
				<td>'.esc_html($g['rule']).'</td>
				<td>'.((!empty($g['min_nights']) && (int)$g['min_nights'] > 0) ? esc_html((int)$g['min_nights']) : '&mdash;').'</td>
				<td>'.esc_html($g['priority']).'</td>
				<td>
					<a href="'.esc_url($edit_url).'" class="button button-small">'.esc_html__('Edit', 'vachr-short-term-rental-reservations').'</a>
					<a href="'.esc_url($clone_url).'" class="button button-small">'.esc_html__('Clone', 'vachr-short-term-rental-reservations').'</a>
					<a href="'.esc_url($toggle_url).'" class="button button-small">'.($is_active ? esc_html__('Disable', 'vachr-short-term-rental-reservations') : esc_html__('Enable', 'vachr-short-term-rental-reservations')).'</a>
					<a href="'.esc_url($del_url).'" class="button button-small button-link-delete" onclick="return confirm(\''.esc_js(__('Delete group?', 'vachr-short-term-rental-reservations')).'\')">'.esc_html__('Delete', 'vachr-short-term-rental-reservations').'</a>
				</td>
			</tr>';
		}
		echo '</tbody></table>';
		self::add_inline_admin_script("
			(function () {
				var applyModeEl = document.getElementById('vstrb-rate-apply-mode');
				var dateModeWrapEl = document.getElementById('vstrb-rate-date-mode-wrap');
				var monthsModeWrapEl = document.getElementById('vstrb-rate-months-mode-wrap');
				var weekdaysModeWrapEl = document.getElementById('vstrb-rate-weekdays-mode-wrap');
				var annualDatesModeWrapEl = document.getElementById('vstrb-rate-annual-dates-mode-wrap');
				var daysModeWrapEl = document.getElementById('vstrb-rate-days-mode-wrap');
				var dateFromEl = document.getElementById('vstrb-rate-date-from');
				var noEndEl = document.getElementById('vstrb-rate-no-end');
				var dateToEl = document.getElementById('vstrb-rate-date-to');
				var dateToWrapEl = document.getElementById('vstrb-rate-date-to-wrap');
				var dayFromEl = document.getElementById('vstrb-rate-day-from');
				var dayToEl = document.getElementById('vstrb-rate-day-to');
				var operationEl = document.getElementById('vstrb-rate-operation');
				var highlightEl = document.getElementById('vstrb-rate-highlight');
				var lastDateToValue = dateToEl ? (dateToEl.value || '') : '';

				function syncNoEndDate() {
					if (!noEndEl || !dateToEl || !dateToWrapEl) return;
					if (noEndEl.checked) {
						lastDateToValue = dateToEl.value || lastDateToValue;
						dateToEl.value = '';
						dateToEl.removeAttribute('required');
						dateToWrapEl.style.display = 'none';
						return;
					}
					dateToWrapEl.style.display = '';
					dateToEl.setAttribute('required', 'required');
					if (!dateToEl.value && lastDateToValue) dateToEl.value = lastDateToValue;
				}

				function syncApplyMode() {
					if (!applyModeEl) return;
					var mode = applyModeEl.value || 'date_range';
					var isDateMode = mode === 'date_range';
					var isMonthsMode = mode === 'months_of_year';
					var isWeekdaysMode = mode === 'weekdays';
					var isAnnualDatesMode = mode === 'annual_dates';
					var isDaysMode = mode === 'days_of_year';

					if (dateModeWrapEl) dateModeWrapEl.style.display = isDateMode ? '' : 'none';
					if (monthsModeWrapEl) monthsModeWrapEl.style.display = isMonthsMode ? '' : 'none';
					if (weekdaysModeWrapEl) weekdaysModeWrapEl.style.display = isWeekdaysMode ? '' : 'none';
					if (annualDatesModeWrapEl) annualDatesModeWrapEl.style.display = isAnnualDatesMode ? '' : 'none';
					if (daysModeWrapEl) daysModeWrapEl.style.display = isDaysMode ? '' : 'none';

					if (dateFromEl) {
						if (isDateMode) dateFromEl.setAttribute('required', 'required');
						else dateFromEl.removeAttribute('required');
					}
					if (dateToEl) {
						if (!isDateMode) dateToEl.removeAttribute('required');
					}
					if (dayFromEl) {
						if (isDaysMode) dayFromEl.setAttribute('required', 'required');
						else dayFromEl.removeAttribute('required');
					}
					if (dayToEl) {
						if (isDaysMode) dayToEl.setAttribute('required', 'required');
						else dayToEl.removeAttribute('required');
					}

					if (isDateMode) syncNoEndDate();
				}

				if (noEndEl) noEndEl.addEventListener('change', syncNoEndDate);
				if (applyModeEl) applyModeEl.addEventListener('change', syncApplyMode);
				syncApplyMode();

				if (operationEl && highlightEl) {
					var highlightDirty = false;
					var discountOps = ['discount_percent', 'discount_amount', 'discount_amount_stay', 'discount_percent_stay', 'surcharge_percent', 'surcharge_amount', 'surcharge_amount_stay', 'surcharge_percent_stay'];
					highlightEl.addEventListener('change', function () { highlightDirty = true; });
					operationEl.addEventListener('change', function () {
						if (highlightDirty) return;
						highlightEl.checked = discountOps.indexOf(operationEl.value) !== -1;
					});
				}
			})();
		");
		echo '</div>';
		self::render_donation_box();
	}

	public static function page_styling($embedded = false) {
		if (!$embedded) echo '<div class="wrap">';
		echo '<h2>' . esc_html__('Short Form Styling', 'vachr-short-term-rental-reservations') . '</h2>';

		// Save
		if (isset($_POST['vstrb_save_styling'])) {
			check_admin_referer('vstrb_styling', 'vstrb_styling_nonce');
			
			$mode = sanitize_text_field(wp_unslash($_POST['vstrb_styling_mode'] ?? 'off'));
			$css_url = esc_url_raw(wp_unslash($_POST['vstrb_custom_css_url'] ?? ''));

			$fields = [
				'font_family', 'cal_bg', 'cal_text', 'cal_active', 'cal_occupied', 'form_bg', 'max_width', 'font_size', 'svc_label', 'meta_style', 'meta_sep',
				'cal_h_text', 'cal_h_size', 'cal_h_color', 'cal_h_weight', 'cal_h_align',
				'frm_h_text', 'frm_h_size', 'frm_h_color', 'frm_h_weight', 'frm_h_align',
				'cal_occ_text', 'cal_sel_text', 'cal_outline', 'cal_outline_size',
				'bs_color', 'bs_blur', 'bs_spread', 'bs_x', 'bs_y',
				'cal_date_size', 'cal_date_color', 'cal_date_weight',
				'cal_lbl_size', 'cal_lbl_color', 'cal_lbl_weight',
				'btn_text', 'btn_bg', 'btn_color', 'btn_size', 'btn_radius', 'btn_font', 'btn_weight',
				'cal_price_bg', 'cal_price_color', 'cal_price_size', 'cal_disc_color', 'cal_surch_color', 'cal_price_lbl_color', 'cal_price_lbl_size', 'cal_price_lbl_weight', 'cal_price_lbl_italic'
			];
			$visual = [];
			foreach ($fields as $f) {
				$val = sanitize_text_field(wp_unslash($_POST['vis_' . $f] ?? ''));
				if (in_array($f, ['cal_bg', 'cal_text', 'cal_active', 'cal_occupied', 'form_bg', 'cal_h_color', 'frm_h_color', 'cal_occ_text', 'cal_sel_text', 'cal_outline', 'bs_color', 'cal_date_color', 'cal_lbl_color', 'btn_bg', 'btn_color', 'cal_price_bg', 'cal_price_color', 'cal_disc_color', 'cal_surch_color', 'cal_price_lbl_color'], true)) {
					$visual[$f] = sanitize_hex_color($val);
				} elseif (in_array($f, ['max_width', 'font_size', 'cal_h_size', 'frm_h_size', 'cal_outline_size', 'bs_blur', 'bs_spread', 'bs_x', 'bs_y', 'cal_date_size', 'cal_lbl_size', 'btn_size', 'btn_radius', 'cal_price_size', 'cal_price_lbl_size'], true)) {
					$visual[$f] = (int)$val;
				} else {
					$visual[$f] = sanitize_text_field($val);
				}
				$visual[$f . '_en'] = isset($_POST['vis_' . $f . '_en']) ? 1 : 0;
			}
			$visual['max_width_unit'] = sanitize_text_field(wp_unslash($_POST['vis_max_width_unit'] ?? 'px'));
			update_option('vstrb_styling_mode', $mode);
			update_option('vstrb_custom_css_url', $css_url);
			update_option('vstrb_visual_settings', $visual);

			echo '<div class="notice notice-success"><p>' . esc_html__('Settings saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}

		// Retrieve
		$mode = get_option('vstrb_styling_mode', 'off');
		$css_url = get_option('vstrb_custom_css_url', '');
		$visual = get_option('vstrb_visual_settings', []);
		
		$v_font = $visual['font_family'] ?? '';
		$v_bg = $visual['cal_bg'] ?? '#f0f0f0';
		$v_text = $visual['cal_text'] ?? '#333333';
		$v_active = $visual['cal_active'] ?? '#007cba';
		$v_occupied = $visual['cal_occupied'] ?? '#ffdddd';
		$v_form_bg = $visual['form_bg'] ?? '#ffffff';
		$v_width = $visual['max_width'] ?? 400;
		$v_width_unit = $visual['max_width_unit'] ?? 'px';
		$v_size = $visual['font_size'] ?? 14;
		$v_svc_label = $visual['svc_label'] ?? '';
		$v_meta_style = $visual['meta_style'] ?? 'list';
		$v_meta_sep = $visual['meta_sep'] ?? '';

		$en = function($key, $default = false) use ($visual) {
			if (!isset($visual[$key . '_en'])) return $default ? 'checked' : '';
			return (!empty($visual[$key . '_en'])) ? 'checked' : '';
		};

		?>
		<form method="post" action="<?php echo esc_url($embedded ? add_query_arg(['page' => 'vstrb-general', 'tab' => 'appearance'], admin_url('admin.php')) : admin_url('admin.php?page=vstrb-styling')); ?>">
			<?php wp_nonce_field('vstrb_styling', 'vstrb_styling_nonce'); ?>
			
			<table class="form-table">
				<tr>
					<th scope="row"><?php esc_html_e('Styling Mode', 'vachr-short-term-rental-reservations'); ?></th>
					<td>
						<fieldset>
							<label><input type="radio" name="vstrb_styling_mode" value="off" <?php checked($mode, 'off'); ?>> <?php esc_html_e('Default (Theme/Plugin CSS)', 'vachr-short-term-rental-reservations'); ?></label><br>
							<label><input type="radio" name="vstrb_styling_mode" value="css" <?php checked($mode, 'css'); ?>> <?php esc_html_e('Custom CSS URL', 'vachr-short-term-rental-reservations'); ?></label>
						</fieldset>
					</td>
				</tr>
			</table>

			<hr>

			<h3><?php esc_html_e('Custom CSS', 'vachr-short-term-rental-reservations'); ?></h3>
			<table class="form-table">
				<tr>
					<th scope="row"><?php esc_html_e('CSS File URL', 'vachr-short-term-rental-reservations'); ?></th>
					<td><input type="url" name="vstrb_custom_css_url" value="<?php echo esc_attr($css_url); ?>" class="regular-text"></td>
				</tr>
			</table>

			<hr>

			<div style="display:flex; justify-content: space-between; align-items: center;">
				<h3><?php esc_html_e('Visual Settings', 'vachr-short-term-rental-reservations'); ?></h3>
				<label><input type="checkbox" id="vstrb_toggle_all_styles"> <strong><?php esc_html_e('Select All / Deselect All', 'vachr-short-term-rental-reservations'); ?></strong></label>
			</div>

			<div class="vstrb-style-preview">
				<div class="vstrb-style-preview__header">
					<div>
						<h3><?php esc_html_e('Live Preview', 'vachr-short-term-rental-reservations'); ?></h3>
						<p class="description"><?php esc_html_e('Changes are shown immediately and are not published until you save them.', 'vachr-short-term-rental-reservations'); ?></p>
					</div>
					<div class="vstrb-style-preview__sizes" role="group" aria-label="<?php esc_attr_e('Preview width', 'vachr-short-term-rental-reservations'); ?>">
						<button type="button" class="button is-active" data-vstrb-preview-width="desktop"><?php esc_html_e('Desktop', 'vachr-short-term-rental-reservations'); ?></button>
						<button type="button" class="button" data-vstrb-preview-width="mobile"><?php esc_html_e('Mobile', 'vachr-short-term-rental-reservations'); ?></button>
					</div>
				</div>
				<div class="vstrb-style-preview__viewport">
					<iframe
						id="vstrb-style-preview-frame"
						title="<?php esc_attr_e('Live styling preview', 'vachr-short-term-rental-reservations'); ?>"
						data-theme-css="<?php echo esc_url(VSTRB_PLUGIN_URL . 'assets/theme-integration.css'); ?>"
						data-frontend-css="<?php echo esc_url(VSTRB_PLUGIN_URL . 'assets/frontend.css'); ?>"
						data-label-calendar="<?php esc_attr_e('Availability Calendar', 'vachr-short-term-rental-reservations'); ?>"
						data-label-available="<?php esc_attr_e('Available', 'vachr-short-term-rental-reservations'); ?>"
						data-label-occupied="<?php esc_attr_e('Occupied', 'vachr-short-term-rental-reservations'); ?>"
						data-label-selected="<?php esc_attr_e('Selected', 'vachr-short-term-rental-reservations'); ?>"
						data-label-price="<?php esc_attr_e('Price per night', 'vachr-short-term-rental-reservations'); ?>"
						data-label-button="<?php esc_attr_e('Send Booking', 'vachr-short-term-rental-reservations'); ?>"
					></iframe>
				</div>
			</div>

			<table class="form-table vstrb-styling-table">
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_font_family_en" class="vstrb-style-en" <?php echo esc_attr($en('font_family')); ?>> <?php esc_html_e('Font Family', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<input type="text" name="vis_font_family" value="<?php echo esc_attr($v_font); ?>" placeholder="e.g. Arial, sans-serif" class="regular-text">
						<p class="description"><?php esc_html_e("Enter font stack, e.g. 'Open Sans', sans-serif", 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_form_bg_en" class="vstrb-style-en" <?php echo esc_attr($en('form_bg')); ?>> <?php esc_html_e('Form Background', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="color" name="vis_form_bg" value="<?php echo esc_attr($v_form_bg); ?>"> <span class="description"><?php esc_html_e('Background of the booking dialog/form', 'vachr-short-term-rental-reservations'); ?></span></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_bg_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_bg')); ?>> <?php esc_html_e('Calendar Background', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="color" name="vis_cal_bg" value="<?php echo esc_attr($v_bg); ?>"></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_text_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_text')); ?>> <?php esc_html_e('Calendar Text Color', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="color" name="vis_cal_text" value="<?php echo esc_attr($v_text); ?>"></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_active_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_active')); ?>> <?php esc_html_e('Calendar Active/Selection', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="color" name="vis_cal_active" value="<?php echo esc_attr($v_active); ?>"></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_occupied_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_occupied')); ?>> <?php esc_html_e('Calendar Occupied Color', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="color" name="vis_cal_occupied" value="<?php echo esc_attr($v_occupied); ?>"></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_max_width_en" class="vstrb-style-en" <?php echo esc_attr($en('max_width')); ?>> <?php esc_html_e('Max Width', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<input type="number" name="vis_max_width" value="<?php echo esc_attr($v_width); ?>" class="small-text"> 
						<select name="vis_max_width_unit">
							<option value="px" <?php selected($v_width_unit, 'px'); ?>>px</option>
							<option value="%" <?php selected($v_width_unit, '%'); ?>>%</option>
						</select>
					</td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_font_size_en" class="vstrb-style-en" <?php echo esc_attr($en('font_size')); ?>> <?php esc_html_e('Base Font Size (px)', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="number" name="vis_font_size" value="<?php echo esc_attr($v_size); ?>" class="small-text"> px</td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_occ_text_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_occ_text')); ?>> <?php esc_html_e('Occupied Text Color', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="color" name="vis_cal_occ_text" value="<?php echo esc_attr($visual['cal_occ_text'] ?? '#ffffff'); ?>"></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_sel_text_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_sel_text')); ?>> <?php esc_html_e('Selected Text Color', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="color" name="vis_cal_sel_text" value="<?php echo esc_attr($visual['cal_sel_text'] ?? '#ffffff'); ?>"></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_outline_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_outline')); ?>> <?php esc_html_e('Calendar Text Outline', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<input type="color" name="vis_cal_outline" value="<?php echo esc_attr($visual['cal_outline'] ?? '#000000'); ?>">
						<input type="number" name="vis_cal_outline_size" value="<?php echo (int)($visual['cal_outline_size'] ?? 1); ?>" class="small-text"> px
						<span class="description"><?php esc_html_e('Creates a slight shadow/outline for readability', 'vachr-short-term-rental-reservations'); ?></span>
					</td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_bs_color_en" class="vstrb-style-en" <?php echo esc_attr($en('bs_color')); ?>> <?php esc_html_e('Calendar Text Shadow', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
							<label title="<?php esc_attr_e('Color', 'vachr-short-term-rental-reservations'); ?>"><input type="color" name="vis_bs_color" value="<?php echo esc_attr($visual['bs_color'] ?? '#00000033'); ?>"></label>
							<label title="<?php esc_attr_e('X Offset', 'vachr-short-term-rental-reservations'); ?>">X: <input type="number" name="vis_bs_x" value="<?php echo (int)($visual['bs_x'] ?? 0); ?>" class="small-text"> px</label>
							<label title="<?php esc_attr_e('Y Offset', 'vachr-short-term-rental-reservations'); ?>">Y: <input type="number" name="vis_bs_y" value="<?php echo (int)($visual['bs_y'] ?? 4); ?>" class="small-text"> px</label>
							<label title="<?php esc_attr_e('Blur', 'vachr-short-term-rental-reservations'); ?>">Blur: <input type="number" name="vis_bs_blur" value="<?php echo (int)($visual['bs_blur'] ?? 10); ?>" class="small-text"> px</label>
							<label title="<?php esc_attr_e('Spread', 'vachr-short-term-rental-reservations'); ?>"><?php esc_html_e('Spread (Note: Text shadow might not support spread in all browsers)', 'vachr-short-term-rental-reservations'); ?>: <input type="number" name="vis_bs_spread" value="<?php echo (int)($visual['bs_spread'] ?? 0); ?>" class="small-text"> px</label>
						</div>
						<p class="description"><?php esc_html_e('Applies a shadow to the numbers (dates) inside the calendar.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row">
						<?php esc_html_e('Calendar Dates Font', 'vachr-short-term-rental-reservations'); ?>
					</th>
					<td>
						<div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
							<label title="<?php esc_attr_e('Font Size', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_date_size_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_date_size')); ?>> <input type="number" name="vis_cal_date_size" value="<?php echo (int)($visual['cal_date_size'] ?? 14); ?>" class="small-text"> px</label>
							<label title="<?php esc_attr_e('Color', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_date_color_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_date_color')); ?>> <input type="color" name="vis_cal_date_color" value="<?php echo esc_attr($visual['cal_date_color'] ?? '#333333'); ?>"></label>
							<label title="<?php esc_attr_e('Bold', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_date_weight_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_date_weight')); ?>> <?php esc_html_e('Bold', 'vachr-short-term-rental-reservations'); ?> <input type="hidden" name="vis_cal_date_weight" value="bold"></label>
						</div>
						<p class="description"><?php esc_html_e('Styling for the day numbers in the calendar grid.', 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row">
						<?php esc_html_e('Calendar Labels Font', 'vachr-short-term-rental-reservations'); ?>
					</th>
					<td>
						<div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
							<label title="<?php esc_attr_e('Font Size', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_lbl_size_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_lbl_size')); ?>> <input type="number" name="vis_cal_lbl_size" value="<?php echo (int)($visual['cal_lbl_size'] ?? 12); ?>" class="small-text"> px</label>
							<label title="<?php esc_attr_e('Color', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_lbl_color_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_lbl_color')); ?>> <input type="color" name="vis_cal_lbl_color" value="<?php echo esc_attr($visual['cal_lbl_color'] ?? '#666666'); ?>"></label>
							<label title="<?php esc_attr_e('Bold', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_lbl_weight_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_lbl_weight')); ?>> <?php esc_html_e('Bold', 'vachr-short-term-rental-reservations'); ?> <input type="hidden" name="vis_cal_lbl_weight" value="bold"></label>
						</div>
						<p class="description"><?php esc_html_e("Styling for the 'Available', 'Occupied', etc. labels below the calendar.", 'vachr-short-term-rental-reservations'); ?></p>
					</td>
				</tr>
			</table>

			<h3><?php esc_html_e('Services & Metadata', 'vachr-short-term-rental-reservations'); ?></h3>
			<table class="form-table vstrb-styling-table">
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_svc_label_en" class="vstrb-style-en" <?php echo esc_attr($en('svc_label')); ?>> <?php esc_html_e('Services Header Label', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td><input type="text" name="vis_svc_label" value="<?php echo esc_attr($v_svc_label); ?>" class="regular-text" placeholder="<?php esc_attr_e('Optional Services', 'vachr-short-term-rental-reservations'); ?>"></td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_meta_style_en" class="vstrb-style-en" <?php echo esc_attr($en('meta_style')); ?>> <?php esc_html_e('Metadata Style', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<select name="vis_meta_style">
							<option value="list" <?php selected($v_meta_style, 'list'); ?>><?php esc_html_e('List (Default)', 'vachr-short-term-rental-reservations'); ?></option>
							<option value="inline_bold" <?php selected($v_meta_style, 'inline_bold'); ?>><?php esc_html_e('Inline (Key Bold)', 'vachr-short-term-rental-reservations'); ?></option>
							<option value="inline_normal" <?php selected($v_meta_style, 'inline_normal'); ?>><?php esc_html_e('Inline (Normal)', 'vachr-short-term-rental-reservations'); ?></option>
						</select>
					</td>
				</tr>
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_meta_sep_en" class="vstrb-style-en" <?php echo esc_attr($en('meta_sep')); ?>> <?php esc_html_e('Metadata Separator', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<input type="text" name="vis_meta_sep" value="<?php echo esc_attr($v_meta_sep); ?>" class="small-text" placeholder="|">
						<span class="description"><?php esc_html_e('Symbol to separate inline metadata items', 'vachr-short-term-rental-reservations'); ?></span>
					</td>
				</tr>
			</table>

			<h3><?php esc_html_e('Section Headings', 'vachr-short-term-rental-reservations'); ?></h3>
			<table class="form-table vstrb-styling-table">
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_cal_h_text_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_h_text')); ?>> <?php esc_html_e('Calendar Heading', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<input type="text" name="vis_cal_h_text" value="<?php echo esc_attr($visual['cal_h_text'] ?? esc_html__('Availability Calendar', 'vachr-short-term-rental-reservations')); ?>" class="regular-text">
						<div class="vstrb-h-styling" style="margin-top: 10px; display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
							<label title="<?php esc_attr_e('Font Size', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_h_size_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_h_size')); ?>> <input type="number" name="vis_cal_h_size" value="<?php echo (int)($visual['cal_h_size'] ?? 24); ?>" class="small-text"> px</label>
							<label title="<?php esc_attr_e('Color', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_h_color_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_h_color')); ?>> <input type="color" name="vis_cal_h_color" value="<?php echo esc_attr($visual['cal_h_color'] ?? '#333333'); ?>"></label>
							<label title="<?php esc_attr_e('Bold', 'vachr-short-term-rental-reservations'); ?>"><input type="checkbox" name="vis_cal_h_weight_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_h_weight')); ?>> <?php esc_html_e('Bold', 'vachr-short-term-rental-reservations'); ?> <input type="hidden" name="vis_cal_h_weight" value="bold"></label>
							<label title="<?php esc_attr_e('Alignment', 'vachr-short-term-rental-reservations'); ?>">
								<input type="checkbox" name="vis_cal_h_align_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_h_align')); ?>> 
								<select name="vis_cal_h_align">
									<option value="left" <?php selected($visual['cal_h_align'] ?? 'left', 'left'); ?>><?php esc_html_e('Left', 'vachr-short-term-rental-reservations'); ?></option>
									<option value="center" <?php selected($visual['cal_h_align'] ?? 'left', 'center'); ?>><?php esc_html_e('Center', 'vachr-short-term-rental-reservations'); ?></option>
									<option value="right" <?php selected($visual['cal_h_align'] ?? 'left', 'right'); ?>><?php esc_html_e('Right', 'vachr-short-term-rental-reservations'); ?></option>
								</select>
							</label>
						</div>
					</td>
				</tr>
			</table>

			<h3><?php esc_html_e('Booking Button', 'vachr-short-term-rental-reservations'); ?></h3>
			<table class="form-table vstrb-styling-table">
				<tr>
					<th scope="row">
						<label><input type="checkbox" name="vis_btn_text_en" class="vstrb-style-en" <?php echo esc_attr($en('btn_text')); ?>> <?php esc_html_e('Button Label', 'vachr-short-term-rental-reservations'); ?></label>
					</th>
					<td>
						<input type="text" name="vis_btn_text" value="<?php echo esc_attr($visual['btn_text'] ?? esc_html__('Send Booking', 'vachr-short-term-rental-reservations')); ?>" class="regular-text" placeholder="<?php esc_attr_e('Send Booking', 'vachr-short-term-rental-reservations'); ?>">
					</td>
				</tr>
				<tr>
					<th scope="row">
						<?php esc_html_e('Typography', 'vachr-short-term-rental-reservations'); ?>
					</th>
					<td>
						<div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
							<label title="<?php esc_attr_e('Font Family', 'vachr-short-term-rental-reservations'); ?>">
								<input type="checkbox" name="vis_btn_font_en" class="vstrb-style-en" <?php echo esc_attr($en('btn_font')); ?>> 
								<input type="text" name="vis_btn_font" value="<?php echo esc_attr($visual['btn_font'] ?? ''); ?>" placeholder="e.g. Arial, sans-serif" style="width: 150px;">
							</label>
							<label title="<?php esc_attr_e('Font Size', 'vachr-short-term-rental-reservations'); ?>">
								<input type="checkbox" name="vis_btn_size_en" class="vstrb-style-en" <?php echo esc_attr($en('btn_size')); ?>> 
								<input type="number" name="vis_btn_size" value="<?php echo (int)($visual['btn_size'] ?? 16); ?>" class="small-text"> px
							</label>
							<label title="<?php esc_attr_e('Bold', 'vachr-short-term-rental-reservations'); ?>">
								<input type="checkbox" name="vis_btn_weight_en" class="vstrb-style-en" <?php echo esc_attr($en('btn_weight')); ?>> <?php esc_html_e('Bold', 'vachr-short-term-rental-reservations'); ?> 
								<input type="hidden" name="vis_btn_weight" value="bold">
							</label>
						</div>
					</td>
				</tr>
				<tr>
					<th scope="row">
						<?php esc_html_e('Visuals', 'vachr-short-term-rental-reservations'); ?>
					</th>
					<td>
						<div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
							<label title="<?php esc_attr_e('Background Color', 'vachr-short-term-rental-reservations'); ?>">
								<input type="checkbox" name="vis_btn_bg_en" class="vstrb-style-en" <?php echo esc_attr($en('btn_bg')); ?>> 
								<input type="color" name="vis_btn_bg" value="<?php echo esc_attr($visual['btn_bg'] ?? '#007cba'); ?>">
							</label>
							<label title="<?php esc_attr_e('Text Color', 'vachr-short-term-rental-reservations'); ?>">
								<input type="checkbox" name="vis_btn_color_en" class="vstrb-style-en" <?php echo esc_attr($en('btn_color')); ?>> 
								<input type="color" name="vis_btn_color" value="<?php echo esc_attr($visual['btn_color'] ?? '#ffffff'); ?>">
							</label>
							<label title="<?php esc_attr_e('Border Radius', 'vachr-short-term-rental-reservations'); ?>">
								<input type="checkbox" name="vis_btn_radius_en" class="vstrb-style-en" <?php echo esc_attr($en('btn_radius')); ?>> 
								<input type="number" name="vis_btn_radius" value="<?php echo (int)($visual['btn_radius'] ?? 4); ?>" class="small-text"> px
							</label>
						</div>
					</td>
				</tr>
			</table>

		<h3><?php esc_html_e('Calendar Price Badge', 'vachr-short-term-rental-reservations'); ?></h3>
		<table class="form-table vstrb-styling-table">
			<tr>
				<th scope="row">
					<label><input type="checkbox" name="vis_cal_price_bg_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_price_bg')); ?>> <?php esc_html_e('Badge Background Color', 'vachr-short-term-rental-reservations'); ?></label>
				</th>
				<td><input type="color" name="vis_cal_price_bg" value="<?php echo esc_attr($visual['cal_price_bg'] ?? '#111111'); ?>"></td>
			</tr>
			<tr>
				<th scope="row">
					<label><input type="checkbox" name="vis_cal_price_color_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_price_color')); ?>> <?php esc_html_e('Badge Text Color', 'vachr-short-term-rental-reservations'); ?></label>
				</th>
				<td><input type="color" name="vis_cal_price_color" value="<?php echo esc_attr($visual['cal_price_color'] ?? '#ffffff'); ?>"></td>
			</tr>
			<tr>
				<th scope="row">
					<label><input type="checkbox" name="vis_cal_price_size_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_price_size')); ?>> <?php esc_html_e('Badge Font Size (px)', 'vachr-short-term-rental-reservations'); ?></label>
				</th>
				<td><input type="number" name="vis_cal_price_size" value="<?php echo (int)($visual['cal_price_size'] ?? 8); ?>" class="small-text" min="6" max="24"> px</td>
			</tr>
			<tr>
				<th scope="row">
					<label><input type="checkbox" name="vis_cal_disc_color_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_disc_color')); ?>> <?php esc_html_e('Discount Strip Color', 'vachr-short-term-rental-reservations'); ?></label>
				</th>
				<td><input type="color" name="vis_cal_disc_color" value="<?php echo esc_attr($visual['cal_disc_color'] ?? '#16a34a'); ?>"></td>
			</tr>
			<tr>
				<th scope="row">
					<label><input type="checkbox" name="vis_cal_surch_color_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_surch_color')); ?>> <?php esc_html_e('Surcharge Strip Color', 'vachr-short-term-rental-reservations'); ?></label>
				</th>
				<td><input type="color" name="vis_cal_surch_color" value="<?php echo esc_attr($visual['cal_surch_color'] ?? '#dc2626'); ?>"></td>
			</tr>
			<tr>
				<th scope="row">
					<label><input type="checkbox" name="vis_cal_price_lbl_color_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_price_lbl_color')); ?>> <?php esc_html_e('Price Mode Label Color', 'vachr-short-term-rental-reservations'); ?></label>
				</th>
				<td><input type="color" name="vis_cal_price_lbl_color" value="<?php echo esc_attr($visual['cal_price_lbl_color'] ?? '#555555'); ?>"></td>
			</tr>
			<tr>
				<th scope="row">
					<label><input type="checkbox" name="vis_cal_price_lbl_size_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_price_lbl_size')); ?>> <?php esc_html_e('Price Mode Label Size (px)', 'vachr-short-term-rental-reservations'); ?></label>
				</th>
				<td><input type="number" name="vis_cal_price_lbl_size" value="<?php echo (int)($visual['cal_price_lbl_size'] ?? 11); ?>" class="small-text" min="6" max="24"> px</td>
			</tr>
			<tr>
				<th scope="row">
					<?php esc_html_e('Price Mode Label Style', 'vachr-short-term-rental-reservations'); ?>
				</th>
				<td>
					<label><input type="checkbox" name="vis_cal_price_lbl_weight_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_price_lbl_weight')); ?>> <?php esc_html_e('Bold', 'vachr-short-term-rental-reservations'); ?></label>
					<input type="hidden" name="vis_cal_price_lbl_weight" value="bold">
					&nbsp;&nbsp;
					<label><input type="checkbox" name="vis_cal_price_lbl_italic_en" class="vstrb-style-en" <?php echo esc_attr($en('cal_price_lbl_italic')); ?>> <?php esc_html_e('Italic', 'vachr-short-term-rental-reservations'); ?></label>
					<input type="hidden" name="vis_cal_price_lbl_italic" value="italic">
				</td>
			</tr>
		</table>

			<p class="submit">
				<button type="submit" name="vstrb_save_styling" class="button button-primary"><?php esc_html_e('Save Changes', 'vachr-short-term-rental-reservations'); ?></button>
			</p>
		</form>
		<?php if (!$embedded) : ?></div><?php endif; ?>
		<?php
		self::add_inline_admin_script("
			(function() {
				var toggleAll = document.getElementById('vstrb_toggle_all_styles');
				if (!toggleAll) return;
				toggleAll.addEventListener('change', function() {
					var checkboxes = document.querySelectorAll('.vstrb-style-en');
					checkboxes.forEach(function(cb) { cb.checked = toggleAll.checked; });
				});
			})();
		");
		self::render_donation_box();
	}

	public static function page_ical($embedded = false) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$requested_unit_id = self::get_param_int('unit_id', 0);
		$unit_id = self::resolve_unit_selection($requested_unit_id);
		$page_slug = $embedded ? 'vstrb-general' : 'vstrb-ical';

		if (!$embedded) echo '<div class="wrap"><h1>' . esc_html__('iCal Imports', 'vachr-short-term-rental-reservations') . '</h1>';
		else echo '<h2>' . esc_html__('iCal Imports', 'vachr-short-term-rental-reservations') . '</h2>';
		echo '<form method="get" style="margin:10px 0">';
		echo '<input type="hidden" name="page" value="' . esc_attr($page_slug) . '" />';
		if ($embedded) echo '<input type="hidden" name="tab" value="ical" />';
		self::unit_select($unit_id);
		submit_button(esc_html__('Show', 'vachr-short-term-rental-reservations'), 'secondary', '', false);
		echo '</form>';

		if (!$unit_id) { if (!$embedded) echo '</div>'; return; }

		// Export link
		$token = get_post_meta($unit_id, '_vstrb_ical_token', true);
		if (!$token) {
			$token = wp_generate_password(20, false, false);
			update_post_meta($unit_id, '_vstrb_ical_token', $token);
		}
		$export_url = rest_url("vstrb/v1/ical/export/{$unit_id}/{$token}.ics");
		$direct_export_url = rest_url("vstrb/v1/ical/export/direct/{$unit_id}/{$token}.ics");

		echo '<div style="background:#fff;padding:15px;border:1px solid #ccd0d4;margin:20px 0;max-width:800px">';
		echo '<h2>' . esc_html__('iCal Export Link', 'vachr-short-term-rental-reservations') . '</h2>';
		echo '<p>' . esc_html__('Use this link to sync THIS apartment to other channels (Airbnb, Booking.com, etc):', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<p><strong>' . esc_html__('All reservations, including imported iCal blocks', 'vachr-short-term-rental-reservations') . '</strong></p>';
		echo '<p><input style="width:100%;font-family:monospace;background:#f9f9f9" readonly value="'.esc_attr($export_url).'" onclick="this.select()" /></p>';
		echo '<p><strong>' . esc_html__('Direct booking-system reservations only', 'vachr-short-term-rental-reservations') . '</strong></p>';
		echo '<p><input style="width:100%;font-family:monospace;background:#f9f9f9" readonly value="'.esc_attr($direct_export_url).'" onclick="this.select()" /></p>';
		echo '</div>';

		// Delete action
		if (self::get_param_text('action', '') === 'delete' && self::get_param_int('id', 0) > 0) {
			check_admin_referer('vstrb_delete_ical');
			$id = self::get_param_int('id', 0);
			$feed = $wpdb->get_row($wpdb->prepare(
				"SELECT id, provider FROM {$t['ical_feeds']} WHERE id=%d AND unit_id=%d",
				$id,
				$unit_id
			), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
			$deleted_feed = $wpdb->delete($t['ical_feeds'], ['id' => $id, 'unit_id' => $unit_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
			if ($deleted_feed) {
				$changed_future_blocks = 0;
				$provider = isset($feed['provider']) ? sanitize_text_field((string) $feed['provider']) : '';
				if ($provider !== '') {
					$remaining_provider_feeds = (int) $wpdb->get_var($wpdb->prepare(
						"SELECT COUNT(*) FROM {$t['ical_feeds']} WHERE unit_id=%d AND provider=%s",
						$unit_id,
						$provider
					)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
					if ($remaining_provider_feeds === 0) {
						$changed_future_blocks = VSTRB_iCal::cleanup_future_blocks_for_provider($unit_id, $provider);
					}
				}

				if ($changed_future_blocks > 0) {
					echo '<div class="notice notice-success"><p>' . esc_html(sprintf(
						/* translators: %d: number of cancelled or removed future iCal blocks */
						__('iCal feed deleted. Cancelled or removed future imported blocks: %d', 'vachr-short-term-rental-reservations'),
						(int) $changed_future_blocks
					)) . '</p></div>';
				} else {
					echo '<div class="notice notice-success"><p>' . esc_html__('iCal feed deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
				}
			} else {
				echo '<div class="notice notice-error"><p>' . esc_html__('iCal feed not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
			}
		}

		if (isset($_POST['vstrb_add_feed'])) {
			check_admin_referer('vstrb_ical', 'vstrb_ical_nonce');
			$url = esc_url_raw(wp_unslash($_POST['url'] ?? ''));
			$provider = sanitize_text_field(wp_unslash($_POST['provider'] ?? ''));
			$provider = VSTRB_iCal::normalize_provider($provider, $url);
			$wpdb->insert($t['ical_feeds'], [
				'unit_id' => $unit_id,
				'provider' => $provider,
				'url' => $url,
				'is_active' => self::post_checkbox_enabled('is_active'),
			]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
			echo '<div class="notice notice-success"><p>' . esc_html__('Feed saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}

		$form_action = add_query_arg(['page' => $page_slug, 'tab' => $embedded ? 'ical' : null, 'unit_id' => (int) $unit_id], admin_url('admin.php'));
		echo '<form method="post" action="' . esc_url($form_action) . '">';
		wp_nonce_field('vstrb_ical', 'vstrb_ical_nonce');
		echo '<h2>' . esc_html__('Add Import URL', 'vachr-short-term-rental-reservations') . '</h2>';
		echo '<div style="display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap">';
		echo '<div>' . esc_html__('Provider / Name', 'vachr-short-term-rental-reservations') . '<br><input id="ical_provider" name="provider" placeholder="' . esc_attr__('e.g. Airbnb, My Channel...', 'vachr-short-term-rental-reservations') . '"></div>';
		echo '<div>' . esc_html__('URL', 'vachr-short-term-rental-reservations') . '<br><input id="ical_url" name="url" style="width:450px" required placeholder="https://..."></div>';
		echo '<div><label><input type="checkbox" name="is_active" checked> ' . esc_html__('active', 'vachr-short-term-rental-reservations') . '</label></div>';
		echo '<div><button class="button button-primary" name="vstrb_add_feed" value="1">' . esc_html__('Add', 'vachr-short-term-rental-reservations') . '</button></div>';
		echo '</div>';
		self::add_inline_admin_script("
			(function() {
				var urlInput = document.getElementById('ical_url');
				if (!urlInput) return;
				urlInput.addEventListener('input', function() {
					var url = this.value.toLowerCase();
					var prov = document.getElementById('ical_provider');
					if (!prov || prov.value) return;

					if (url.indexOf('airbnb.') !== -1) prov.value = 'Airbnb';
					else if (url.indexOf('booking.') !== -1) prov.value = 'Booking.com';
					else if (url.indexOf('e-chalupy.cz') !== -1) prov.value = 'e-chalupy.cz';
					else if (url.indexOf('megaubytovanie.sk') !== -1) prov.value = 'Megaubytovanie';
					else if (url.indexOf('hauzi.sk') !== -1 || url.indexOf('hauzi.cz') !== -1) prov.value = 'Hauzi';
					else if (url.indexOf('bezrealitky.cz') !== -1 || url.indexOf('bezrealitky.sk') !== -1) prov.value = 'BezRealitky';
				});
			})();
		");
		echo '</form>';

		if (self::get_param_int('do_import', 0) === 1) {
			if (!wp_verify_nonce(self::get_param_text('_wpnonce', ''), 'vstrb_ical_import_' . $unit_id)) {
				echo '<div class="notice notice-error"><p>' . esc_html__('Security check failed.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$res = VSTRB_iCal::import_unit_feeds($unit_id);
				echo '<div class="notice notice-info"><p>' . esc_html__('Import:', 'vachr-short-term-rental-reservations') . ' ' . esc_html($res['message']) . '</p></div>';
			}
		}

		$feeds = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$t['ical_feeds']} WHERE unit_id=%d ORDER BY id DESC", $unit_id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		echo '<h2>' . esc_html__('Imports', 'vachr-short-term-rental-reservations') . '</h2><table class="widefat"><thead><tr><th>ID</th><th>' . esc_html__('Provider / Name', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('URL', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Active', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Last Import', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Error', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Actions', 'vachr-short-term-rental-reservations') . '</th></tr></thead><tbody>';
		
		$provider_labels = [
			'airbnb' => 'Airbnb',
			'booking' => 'Booking.com',
			'e_chalupy' => 'e-chalupy.cz',
			'megaubytovanie' => 'Megaubytovanie',
			'hauzi' => 'Hauzi',
			'bezrealitky' => 'BezRealitky',
			'other' => esc_html__('Other', 'vachr-short-term-rental-reservations'),
		];

		foreach ($feeds as $f) {
			$del_url = wp_nonce_url(add_query_arg(['page' => $page_slug, 'tab' => $embedded ? 'ical' : null, 'unit_id' => (int) $unit_id, 'action' => 'delete', 'id' => (int) $f['id']], admin_url('admin.php')), 'vstrb_delete_ical');
			// Format provider label nicely
			if (!isset($provider_labels[$f['provider']])) {
				// If it looks like a domain (contains a dot), display as-is
				// Otherwise capitalize it
				$prov_label = strpos($f['provider'], '.') !== false ? $f['provider'] : ucfirst($f['provider']);
			} else {
				$prov_label = $provider_labels[$f['provider']];
			}
			echo '<tr><td>'.esc_html($f['id']).'</td><td style="font-weight:bold">'.esc_html($prov_label).'</td><td><code>'.esc_url($f['url']).'</code></td><td>'.($f['is_active']?esc_html__('yes', 'vachr-short-term-rental-reservations'):esc_html__('no', 'vachr-short-term-rental-reservations')).'</td><td>'.esc_html($f['last_import_at']).'</td><td>'.esc_html(wp_trim_words($f['last_error'], 12)).'</td>';
			echo '<td><a href="'.esc_url($del_url).'" class="button button-small button-link-delete" onclick="return confirm(\''.esc_js(__('Delete this iCal feed?', 'vachr-short-term-rental-reservations')).'\')" style="color:#a00">' . esc_html__('Delete', 'vachr-short-term-rental-reservations') . '</a></td></tr>';
		}
		echo '</tbody></table>';

		$import_url = wp_nonce_url(
			add_query_arg(['page' => $page_slug, 'tab' => $embedded ? 'ical' : null, 'unit_id' => $unit_id, 'do_import' => 1], admin_url('admin.php')),
			'vstrb_ical_import_' . $unit_id
		);
		echo '<p><a class="button" href="' . esc_url($import_url) . '">' . esc_html__('Run Import Now', 'vachr-short-term-rental-reservations') . '</a></p>';

		if (!$embedded) echo '</div>';
		self::render_donation_box();
	}

	public static function page_contact_fields($embedded = false) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$base_url = $embedded ? add_query_arg(['page' => 'vstrb-general', 'tab' => 'contact-fields'], admin_url('admin.php')) : admin_url('admin.php?page=vstrb-contacts');

		if (!$embedded) echo '<div class="wrap">';
		echo ($embedded ? '<h2>' : '<h1>') . esc_html__('Contact Fields', 'vachr-short-term-rental-reservations') . ($embedded ? '</h2>' : '</h1>');

		// Delete action
		if (self::get_param_text('action', '') === 'delete' && self::get_param_int('id', 0) > 0) {
			check_admin_referer('vstrb_delete_field');
			$id = self::get_param_int('id', 0);
			$wpdb->delete($t['contact_fields'], ['id' => $id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
			$wpdb->delete($t['contact_field_meta'], ['field_id' => $id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
			echo '<div class="notice notice-success"><p>' . esc_html__('Field deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
		}

		// Edit/Add logic
		$edit_id = self::request_param_int('edit_id', 0);
		$edit_data = null;
		if ($edit_id) {
			$edit_data = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t['contact_fields']} WHERE id=%d", $edit_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
		}

		// Save field
		if (isset($_POST['vstrb_save_field'])) {
			check_admin_referer('vstrb_contact_fields', 'vstrb_contact_fields_nonce');
			$data = [
				'field_key' => strtolower(preg_replace('/[^a-z0-9_]/', '', sanitize_text_field(wp_unslash($_POST['field_key'] ?? '')))),
				'is_active' => self::post_checkbox_enabled('is_active'),
				'sort_order' => isset($_POST['sort_order']) ? (int) sanitize_text_field(wp_unslash($_POST['sort_order'])) : 0,
			];

			$field_id = 0;
			if ($edit_id && $edit_data) {
				$wpdb->update($t['contact_fields'], $data, ['id'=>$edit_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				echo '<div class="notice notice-success"><p>' . esc_html__('Updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
				$field_id = $edit_id;
			} else {
				$wpdb->insert($t['contact_fields'], $data); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				$field_id = $wpdb->insert_id;
				echo '<div class="notice notice-success"><p>' . esc_html__('Saved.', 'vachr-short-term-rental-reservations') . '</p></div>';
			}

			// Save Meta (Labels)
			if ($field_id) {
				$posted_meta = self::post_array('meta');
				$wpdb->delete($t['contact_field_meta'], ['field_id' => $field_id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				foreach ($posted_meta as $pair) {
					if (empty($pair['key'])) continue;
					$wpdb->insert($t['contact_field_meta'], [
						'field_id' => $field_id,
						'meta_key' => sanitize_text_field($pair['key']),
						'meta_value' => sanitize_textarea_field($pair['value']),
						'locale' => !empty($pair['locale']) ? sanitize_text_field($pair['locale']) : null,
					]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				}
			}
			$edit_id = 0; $edit_data = null; // reset
		}

		// Form
		$lbl = $edit_id ? esc_html__('Edit Field', 'vachr-short-term-rental-reservations') : esc_html__('Add Field', 'vachr-short-term-rental-reservations');
		$btn = $edit_id ? esc_html__('Update', 'vachr-short-term-rental-reservations') : esc_html__('Add', 'vachr-short-term-rental-reservations');
		$val_key = $edit_data ? $edit_data->field_key : '';
		$val_order = $edit_data ? $edit_data->sort_order : '0';
		$val_active = (!$edit_data || $edit_data->is_active) ? 'checked' : '';

		$form_action = $base_url;
		echo '<form method="post" action="' . esc_url($form_action) . '" style="background:#fff;padding:15px;border:1px solid #ccd0d4;margin-bottom:20px">';
		wp_nonce_field('vstrb_contact_fields', 'vstrb_contact_fields_nonce');
		echo '<input type="hidden" name="edit_id" value="'.(int)$edit_id.'">';
		echo '<h3>' . esc_html($lbl) . ' ' . ($edit_id ? '<a href="' . esc_url($base_url) . '" class="page-title-action">'.esc_html__('Cancel', 'vachr-short-term-rental-reservations').'</a>' : '') . '</h3>';
		echo '<div style="display:flex;gap:15px;align-items:flex-end;flex-wrap:wrap">';
		echo '<div><label>' . esc_html__('Unique Key', 'vachr-short-term-rental-reservations') . ' (e.g. flight_no)<br><input name="field_key" required value="'.esc_attr($val_key).'"></label></div>';
		echo '<div><label>' . esc_html__('Sort Order', 'vachr-short-term-rental-reservations') . '<br><input type="number" name="sort_order" value="'.esc_attr($val_order).'" style="width:70px"></label></div>';
		echo '<div><label><input type="checkbox" name="is_active" '.esc_attr($val_active).'> ' . esc_html__('active', 'vachr-short-term-rental-reservations') . '</label></div>';
		echo '<div><button class="button button-primary" name="vstrb_save_field" value="1">' . esc_html($btn) . '</button></div>';
		echo '</div>';

		// Labels (Metadata)
		$meta = [];
		if ($edit_id) {
			$meta = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$t['contact_field_meta']} WHERE field_id=%d", $edit_id), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
		} else {
			// Default "label" keys for common locales if adding new
			$meta = [['meta_key'=>'label', 'meta_value'=>'', 'locale'=>'']];
		}

		$locales = self::get_language_options(true);
		$locales_json = wp_json_encode($locales);
		?>
		<div style="margin-top:15px;border-top:1px solid #eee;padding-top:15px">
			<strong><?php esc_html_e('Labels (per Language)', 'vachr-short-term-rental-reservations'); ?></strong>
			<div id="vstrb_labels_wrapper" style="margin-top:10px">
				<?php foreach ($meta as $idx => $m): ?>
				<div class="vstrb-meta-row" style="margin-bottom:5px;display:flex;gap:5px">
					<select name="meta[<?php echo (int)$idx; ?>][locale]" style="width:80px">
						<?php foreach ($locales as $k=>$v) echo '<option value="'.esc_attr($k).'" '.selected($m['locale'], $k, false).'>'.esc_html($v).'</option>'; ?>
					</select>
					<input type="hidden" name="meta[<?php echo (int)$idx; ?>][key]" value="label">
					<input type="text" name="meta[<?php echo (int)$idx; ?>][value]" placeholder="<?php esc_attr_e('Label text...', 'vachr-short-term-rental-reservations'); ?>" value="<?php echo esc_attr($m['meta_value']); ?>" size="40">
					<button type="button" class="button button-small" onclick="this.parentNode.remove()">x</button>
				</div>
				<?php endforeach; ?>
			</div>
			<button type="button" class="button button-small" onclick="addLabelRow()"><?php esc_html_e('+ Add Label', 'vachr-short-term-rental-reservations'); ?></button>
		</div>
		</form>
		<?php
		$label_placeholder = wp_json_encode(__('Label text...', 'vachr-short-term-rental-reservations'));
		self::add_inline_admin_script("
			window.addLabelRow = function() {
				const w = document.getElementById('vstrb_labels_wrapper');
				if (!w) return;
				const idx = w.children.length + new Date().getTime();
				const localeOptions = {$locales_json} || {};
				const optionsHtml = Object.entries(localeOptions).map(([k, v]) => `<option value=\"\${k}\">\${v}</option>`).join('');
				const div = document.createElement('div');
				div.className = 'vstrb-meta-row';
				div.style = 'margin-bottom:5px;display:flex;gap:5px';
				div.innerHTML = `<select name=\"meta[\${idx}][locale]\" style=\"width:80px\">
					\${optionsHtml}
				</select> <input type=\"hidden\" name=\"meta[\${idx}][key]\" value=\"label\"> <input type=\"text\" name=\"meta[\${idx}][value]\" placeholder={$label_placeholder} size=\"40\"> <button type=\"button\" class=\"button button-small\" onclick=\"this.parentNode.remove()\">x</button>`;
				w.appendChild(div);
			};
		");

		// List
		$sql_fields = "SELECT * FROM {$t['contact_fields']} ORDER BY sort_order ASC, id DESC";
		$fields = $wpdb->get_results($sql_fields, ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Custom table query.
		$field_labels_map = [];
		if (!empty($fields)) {
			$field_ids = array_values(array_unique(array_map('intval', array_column($fields, 'id'))));
			if (!empty($field_ids)) {
				$field_id_placeholders = implode(', ', array_fill(0, count($field_ids), '%d'));
				$all_labels = $wpdb->get_results(
					$wpdb->prepare(
						"SELECT field_id, locale, meta_value
						 FROM {$t['contact_field_meta']}
						 WHERE meta_key='label' AND field_id IN ({$field_id_placeholders})
						 ORDER BY field_id ASC, locale DESC", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
						...$field_ids
					),
					ARRAY_A
				);
				foreach ($all_labels as $label_row) {
					$fid = (int) $label_row['field_id'];
					if (!isset($field_labels_map[$fid])) {
						$field_labels_map[$fid] = [];
					}
					$field_labels_map[$fid][] = [
						'locale' => $label_row['locale'],
						'meta_value' => $label_row['meta_value'],
					];
				}
			}
		}
		echo '<h2>' . esc_html__('Available Contact Fields', 'vachr-short-term-rental-reservations') . '</h2>';
		echo '<table class="widefat striped"><thead><tr><th>ID</th><th>' . esc_html__('Key', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Labels', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Order', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Active', 'vachr-short-term-rental-reservations') . '</th><th>' . esc_html__('Actions', 'vachr-short-term-rental-reservations') . '</th></tr></thead><tbody>';
		foreach ($fields as $f) {
			$labels = $field_labels_map[(int) $f['id']] ?? [];
			$label_str = [];
			foreach ($labels as $l) {
				$loc = $l['locale'] ?: 'global';
				$label_str[] = "<strong>" . esc_html($loc) . ":</strong> " . esc_html($l['meta_value']);
			}

			$del_url = wp_nonce_url(add_query_arg(['action' => 'delete', 'id' => (int) $f['id']], $base_url), 'vstrb_delete_field');
			$edit_url = add_query_arg('edit_id', (int) $f['id'], $base_url);
			echo '<tr>
				<td>'.esc_html($f['id']).'</td>
				<td><code>'.esc_html($f['field_key']).'</code></td>
				<td>' . wp_kses_post(implode(' | ', $label_str)) . '</td>
				<td>'.esc_html($f['sort_order']).'</td>
				<td>'.($f['is_active'] ? esc_html__('yes', 'vachr-short-term-rental-reservations') : esc_html__('no', 'vachr-short-term-rental-reservations')).'</td>
				<td>
					<a href="'.esc_url($edit_url).'" class="button button-small">'.esc_html__('Edit', 'vachr-short-term-rental-reservations').'</a>
					<a href="'.esc_url($del_url).'" class="button button-small button-link-delete" onclick="return confirm(\''.esc_js(__('Are you sure?', 'vachr-short-term-rental-reservations')).'\')">'.esc_html__('Delete', 'vachr-short-term-rental-reservations').'</a>
				</td>
			</tr>';
		}
		if (!$fields) echo '<tr><td colspan="6">'.esc_html__('No custom fields defined.', 'vachr-short-term-rental-reservations').'</td></tr>';
		echo '</tbody></table>';
		if (!$embedded) echo '</div>';
		self::render_donation_box();
	}

	private static function guest_registration_group_conditions($rep) {
		$unit_id = (int) ($rep->unit_id ?? 0);
		$booking_reference = (string) ($rep->booking_reference ?? '');
		$date_from = (string) ($rep->date_from ?? '');
		$date_to = (string) ($rep->date_to ?? '');
		$email = (string) ($rep->email ?? '');

		if ($booking_reference !== '') {
			return [
				'sql' => 'unit_id = %d AND booking_reference = %s',
				'params' => [$unit_id, $booking_reference],
			];
		}

		return [
			'sql' => "unit_id = %d AND date_from = %s AND date_to = %s AND email = %s AND (booking_reference = '' OR booking_reference IS NULL)",
			'params' => [$unit_id, $date_from, $date_to, $email],
		];
	}

	private static function guest_source_to_booking_source($source) {
		$src = strtolower(trim((string) $source));
		$map = [
			'booking_com' => 'ical_booking',
			'airbnb' => 'ical_airbnb',
			'vrbo' => 'ical_vrbo',
			'expedia' => 'ical_expedia',
			'direct' => 'direct',
		];
		return $map[$src] ?? '';
	}

	private static function booking_candidates_for_registration_group($rep, $limit = 30) {
		global $wpdb;
		$t = VSTRB_DB::tables();

		$date_from = (string) ($rep->date_from ?? '');
		$date_to = (string) ($rep->date_to ?? '');
		$limit = max(1, min(100, (int) $limit));

		if ($date_from === '' || $date_to === '') {
			return [];
		}

		$sql = "SELECT id, unit_id, source, status, date_from, date_to, external_booking_id, customer_name, customer_lastname, customer_email
				FROM {$t['bookings']}
				WHERE NOT (date_to <= %s OR date_from >= %s)";
		$params = [$date_from, $date_to];
		$sql .= " ORDER BY
					(date_from = %s AND date_to = %s) DESC,
					(unit_id = %d) DESC,
					ABS(DATEDIFF(date_from, %s)) ASC,
					ABS(DATEDIFF(date_to, %s)) ASC,
					id DESC
				  LIMIT %d";
		$params[] = $date_from;
		$params[] = $date_to;
		$params[] = (int) ($rep->unit_id ?? 0);
		$params[] = $date_from;
		$params[] = $date_to;
		$params[] = $limit;

		return $wpdb->get_results($wpdb->prepare($sql, ...$params)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
	}

	private static function group_rows_for_guest_registration($rep) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$gr_table = $t['guest_registrations'];

		$group_where = self::guest_registration_group_conditions($rep);
		$sql = "SELECT * FROM {$gr_table} WHERE " . $group_where['sql'] . " ORDER BY id ASC";
		return $wpdb->get_results($wpdb->prepare($sql, ...$group_where['params'])); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
	}

	private static function render_guest_invoice_page($rep, $group_rows, $booking = null) {
		self::switch_to_invoice_locale();
		$unit_title = get_the_title((int) ($rep->unit_id ?? 0));
		$invoice_no = self::build_guest_invoice_number($rep);
		$issue_date = date_i18n('Y-m-d');
		$currency = strtoupper(trim((string) ($booking->currency ?? '')));
		$total_price = isset($booking->total_price) ? (float) $booking->total_price : null;
		$show_price = ($total_price !== null && $total_price > 0 && $currency !== '');
		$customer_name = trim((string) ($rep->billing_name ?? ''));
		if ($customer_name === '') {
			$customer_name = trim((string) ($rep->first_name ?? '') . ' ' . (string) ($rep->last_name ?? ''));
		}
		$supplier = self::get_invoice_supplier_details();
		$payment_amount_due = ($booking && !empty($booking->id))
			? (float) self::booking_meta_value((int) $booking->id, 'payment_amount_due', (string) $total_price)
			: (float) $total_price;
		if ($payment_amount_due <= 0) {
			$payment_amount_due = (float) $total_price;
		}
		$payment_qr_payload = $show_price
			? VSTRB_Payment_QR::payload(
				$payment_amount_due,
				$currency,
				$invoice_no
			)
			: '';

		nocache_headers();
		header('Content-Type: text/html; charset=' . get_option('blog_charset'));
		?>
		<!doctype html>
		<html>
		<head>
			<meta charset="<?php echo esc_attr(get_option('blog_charset')); ?>">
			<title><?php echo esc_html(self::invoice_text('Invoice')); ?> - <?php echo esc_html($invoice_no); ?></title>
			<style>
				body{font-family:Arial,sans-serif;color:#1d2327;margin:20px}
				.vstrb-invoice-wrap{max-width:900px;margin:0 auto}
				.vstrb-invoice-actions{display:flex;gap:8px;justify-content:flex-end;margin-bottom:16px}
				.vstrb-invoice-card{border:1px solid #dcdcde;padding:20px}
				.vstrb-invoice-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:16px}
				.vstrb-invoice-table{width:100%;border-collapse:collapse;margin-top:20px}
				.vstrb-invoice-table th,.vstrb-invoice-table td{border:1px solid #dcdcde;padding:8px;text-align:left}
				@media print {.vstrb-invoice-actions{display:none} body{margin:0}}
			</style>
		</head>
		<body>
			<div class="vstrb-invoice-wrap">
				<div class="vstrb-invoice-actions">
					<button type="button" onclick="window.print()"><?php esc_html_e('Print Invoice', 'vachr-short-term-rental-reservations'); ?></button>
					<button type="button" onclick="window.print()"><?php esc_html_e('Save as PDF', 'vachr-short-term-rental-reservations'); ?></button>
					<button type="button" onclick="window.close()"><?php esc_html_e('Close', 'vachr-short-term-rental-reservations'); ?></button>
				</div>
				<div class="vstrb-invoice-card">
					<h1 style="margin:0"><?php echo esc_html(self::invoice_text('Invoice')); ?></h1>
					<p style="margin:6px 0 0 0"><?php echo esc_html($invoice_no); ?></p>
					<div class="vstrb-invoice-grid">
						<div>
							<?php self::render_invoice_party_block(self::invoice_text('Supplier'), $supplier); ?>
						</div>
						<div>
							<strong><?php echo esc_html(self::invoice_text('Customer')); ?></strong><br>
							<?php echo esc_html($customer_name); ?><br>
							<?php if (!empty($rep->billing_company_id)): ?>
								<?php echo esc_html(self::invoice_text('Company ID')); ?>: <?php echo esc_html($rep->billing_company_id); ?><br>
							<?php endif; ?>
							<?php if (!empty($rep->billing_vat_id)): ?>
								<?php echo esc_html(self::invoice_text('VAT ID')); ?>: <?php echo esc_html($rep->billing_vat_id); ?><br>
							<?php endif; ?>
							<?php echo esc_html(trim((string) ($rep->billing_street ?? ''))); ?><br>
							<?php echo esc_html(trim((string) ($rep->billing_zip ?? '') . ' ' . (string) ($rep->billing_city ?? ''))); ?><br>
							<?php echo esc_html(trim((string) ($rep->billing_country ?? ''))); ?>
						</div>
					</div>
					<div class="vstrb-invoice-grid">
						<div><strong><?php echo esc_html(self::invoice_text('Issue Date')); ?>:</strong> <?php echo esc_html($issue_date); ?></div>
						<div><strong><?php echo esc_html(self::invoice_text('Stay')); ?>:</strong> <?php echo esc_html((string) ($rep->date_from ?? '') . ' - ' . (string) ($rep->date_to ?? '')); ?></div>
					</div>
					<table class="vstrb-invoice-table">
						<thead>
							<tr>
								<th><?php echo esc_html(self::invoice_text('Description')); ?></th>
								<th><?php echo esc_html(self::invoice_text('Quantity')); ?></th>
								<th><?php echo esc_html(self::invoice_text('Unit Price')); ?></th>
								<th><?php echo esc_html(self::invoice_text('Total')); ?></th>
							</tr>
						</thead>
						<tbody>
							<tr>
								<td><?php echo esc_html($unit_title . ' (' . (string) count($group_rows) . ' ' . self::invoice_text('guests') . ')'); ?></td>
								<td>1</td>
								<td><?php echo esc_html($show_price ? number_format_i18n($total_price, 2) . ' ' . $currency : '-'); ?></td>
								<td><?php echo esc_html($show_price ? number_format_i18n($total_price, 2) . ' ' . $currency : '-'); ?></td>
							</tr>
						</tbody>
					</table>
					<?php VSTRB_Payment_QR::render($payment_qr_payload); ?>
					<?php if (!$show_price): ?>
						<p style="margin-top:10px;color:#646970"><?php echo esc_html(self::invoice_text('Price not available. Link this registration group to a booking with total price to print a complete invoice.')); ?></p>
					<?php endif; ?>
					<?php if (!empty($supplier['tax_note'])): ?>
						<p style="margin-top:14px;color:#50575e"><?php echo nl2br(esc_html((string) $supplier['tax_note'])); ?></p>
					<?php endif; ?>
				</div>
			</div>
			<?php if ($payment_qr_payload !== '') VSTRB_Payment_QR::render_assets(); ?>
		</body>
		</html>
		<?php
		exit;
	}

	public static function page_guest_registrations() {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$gr_table = $t['guest_registrations'];

		// Delete action
		$action = self::get_param_text('action', '');
		$id     = self::get_param_int('id', 0);
		$nonce  = self::get_param_text('_wpnonce', '');
		if ($action === 'delete' && $id > 0 && $nonce !== '') {
			if (wp_verify_nonce($nonce, 'vstrb_guest_delete_' . $id)) {
				$wpdb->delete($gr_table, ['id' => $id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table query.
				echo '<div class="notice notice-success"><p>' . esc_html__('Registration deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				echo '<div class="notice notice-error"><p>' . esc_html__('Nonce verification failed.', 'vachr-short-term-rental-reservations') . '</p></div>';
			}
		}

		if ($action === 'delete_group' && $id > 0 && $nonce !== '') {
			if (wp_verify_nonce($nonce, 'vstrb_guest_delete_group_' . $id)) {
				$rep = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
					$wpdb->prepare("SELECT * FROM {$gr_table} WHERE id = %d", $id)
				);
				if ($rep) {
					if (!empty($rep->booking_reference)) {
						$wpdb->delete( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
							$gr_table,
							['unit_id' => (int)$rep->unit_id, 'booking_reference' => $rep->booking_reference],
							['%d', '%s']
						);
					} else {
						$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
							$wpdb->prepare(
								"DELETE FROM {$gr_table} WHERE unit_id = %d AND date_from = %s AND date_to = %s AND email = %s AND (booking_reference = '' OR booking_reference IS NULL)",
								(int)$rep->unit_id, $rep->date_from, $rep->date_to, $rep->email
							)
						);
					}
					echo '<div class="notice notice-success"><p>' . esc_html__('Group deleted.', 'vachr-short-term-rental-reservations') . '</p></div>';
				}
			} else {
				echo '<div class="notice notice-error"><p>' . esc_html__('Nonce verification failed.', 'vachr-short-term-rental-reservations') . '</p></div>';
			}
		}

		if (isset($_POST['vstrb_update_contact_group'])) {
			check_admin_referer('vstrb_update_contact_group', 'vstrb_update_contact_group_nonce');
			$rep_id = isset($_POST['rep_id']) ? (int) sanitize_text_field(wp_unslash($_POST['rep_id'])) : 0;
			if ($rep_id <= 0) {
				echo '<div class="notice notice-error"><p>' . esc_html__('Registration group not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$rep = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$gr_table} WHERE id = %d", $rep_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				if (!$rep) {
					echo '<div class="notice notice-error"><p>' . esc_html__('Registration group not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
				} else {
					$group_date_from = isset($_POST['group_date_from']) ? sanitize_text_field(wp_unslash($_POST['group_date_from'])) : (string) $rep->date_from;
					$group_date_to = isset($_POST['group_date_to']) ? sanitize_text_field(wp_unslash($_POST['group_date_to'])) : (string) $rep->date_to;
					$group_unit_id = isset($_POST['group_unit_id']) ? (int) sanitize_text_field(wp_unslash($_POST['group_unit_id'])) : (int) $rep->unit_id;
					$group_email_raw = isset($_POST['group_email']) ? sanitize_email(wp_unslash($_POST['group_email'])) : (string) $rep->email;
					$group_email = is_email($group_email_raw) ? $group_email_raw : '';
					$allowed_sources = ['booking_com', 'airbnb', 'vrbo', 'expedia', 'direct', 'other', ''];
					$group_source = isset($_POST['group_source']) ? sanitize_text_field(wp_unslash($_POST['group_source'])) : '';
					if (!in_array($group_source, $allowed_sources, true)) {
						$group_source = '';
					}
					$group_booking_reference = isset($_POST['group_booking_reference']) ? sanitize_text_field(wp_unslash($_POST['group_booking_reference'])) : '';
					$billing_enabled = isset($_POST['group_billing_enabled']) ? 1 : 0;
					$billing_name = isset($_POST['group_billing_name']) ? sanitize_text_field(wp_unslash($_POST['group_billing_name'])) : '';
					$billing_company_id = isset($_POST['group_billing_company_id']) ? sanitize_text_field(wp_unslash($_POST['group_billing_company_id'])) : '';
					$billing_vat_id = isset($_POST['group_billing_vat_id']) ? sanitize_text_field(wp_unslash($_POST['group_billing_vat_id'])) : '';
					$billing_street = isset($_POST['group_billing_street']) ? sanitize_text_field(wp_unslash($_POST['group_billing_street'])) : '';
					$billing_city = isset($_POST['group_billing_city']) ? sanitize_text_field(wp_unslash($_POST['group_billing_city'])) : '';
					$billing_zip = isset($_POST['group_billing_zip']) ? sanitize_text_field(wp_unslash($_POST['group_billing_zip'])) : '';
					$billing_country = isset($_POST['group_billing_country']) ? sanitize_text_field(wp_unslash($_POST['group_billing_country'])) : '';

					$dates_valid = self::is_valid_ymd($group_date_from) && self::is_valid_ymd($group_date_to) && strtotime($group_date_to) > strtotime($group_date_from);
					if (!$dates_valid || $group_email === '') {
						echo '<div class="notice notice-error"><p>' . esc_html__('Please provide a valid email and stay dates.', 'vachr-short-term-rental-reservations') . '</p></div>';
					} elseif ($group_unit_id <= 0 || !VSTRB_DB::unit_exists($group_unit_id)) {
						echo '<div class="notice notice-error"><p>' . esc_html__('Invalid unit selected.', 'vachr-short-term-rental-reservations') . '</p></div>';
					} else {
						$group_where = self::guest_registration_group_conditions($rep);
						$sql = "UPDATE {$gr_table}
							SET unit_id = %d,
								booking_id = CASE WHEN %d = %d THEN booking_id ELSE NULL END,
								date_from = %s,
								date_to = %s,
								email = %s,
								source = %s,
								booking_reference = %s,
								billing_enabled = %d,
								billing_name = %s,
								billing_company_id = %s,
								billing_vat_id = %s,
								billing_street = %s,
								billing_city = %s,
								billing_zip = %s,
								billing_country = %s
							WHERE " . $group_where['sql'];
						$params = array_merge(
							[
								$group_unit_id,
								$group_unit_id,
								(int) $rep->unit_id,
								$group_date_from,
								$group_date_to,
								$group_email,
								$group_source,
								$group_booking_reference,
								$billing_enabled,
								$billing_enabled ? $billing_name : '',
								$billing_enabled ? $billing_company_id : '',
								$billing_enabled ? $billing_vat_id : '',
								$billing_enabled ? $billing_street : '',
								$billing_enabled ? $billing_city : '',
								$billing_enabled ? $billing_zip : '',
								$billing_enabled ? $billing_country : '',
							],
							$group_where['params']
						);
						$updated = $wpdb->query($wpdb->prepare($sql, ...$params)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
						if ($updated === false) {
							echo '<div class="notice notice-error"><p>' . esc_html__('Unable to update contact details.', 'vachr-short-term-rental-reservations') . '</p></div>';
						} else {
							echo '<div class="notice notice-success"><p>' . esc_html__('Contact details updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
						}
					}
				}
			}
		}

		if (isset($_POST['vstrb_update_guest_registration'])) {
			check_admin_referer('vstrb_update_guest_registration', 'vstrb_update_guest_registration_nonce');
			$guest_id = isset($_POST['guest_id']) ? (int) sanitize_text_field(wp_unslash($_POST['guest_id'])) : 0;
			if ($guest_id <= 0) {
				echo '<div class="notice notice-error"><p>' . esc_html__('Registration not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$guest = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$gr_table} WHERE id = %d", $guest_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				if (!$guest) {
					echo '<div class="notice notice-error"><p>' . esc_html__('Registration not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
				} else {
					$first_name = isset($_POST['guest_first_name']) ? sanitize_text_field(wp_unslash($_POST['guest_first_name'])) : '';
					$last_name = isset($_POST['guest_last_name']) ? sanitize_text_field(wp_unslash($_POST['guest_last_name'])) : '';
					$date_of_birth = isset($_POST['guest_date_of_birth']) ? sanitize_text_field(wp_unslash($_POST['guest_date_of_birth'])) : '';
					$nationality = isset($_POST['guest_nationality']) ? sanitize_text_field(wp_unslash($_POST['guest_nationality'])) : '';
					$document_type = isset($_POST['guest_document_type']) ? sanitize_text_field(wp_unslash($_POST['guest_document_type'])) : '';
					$document_number = isset($_POST['guest_document_number']) ? sanitize_text_field(wp_unslash($_POST['guest_document_number'])) : '';
					$street = isset($_POST['guest_street']) ? sanitize_text_field(wp_unslash($_POST['guest_street'])) : '';
					$city = isset($_POST['guest_city']) ? sanitize_text_field(wp_unslash($_POST['guest_city'])) : '';
					$state = isset($_POST['guest_state']) ? sanitize_text_field(wp_unslash($_POST['guest_state'])) : '';

					if ($first_name === '' || $last_name === '' || $nationality === '' || $document_type === '' || $document_number === '' || $street === '' || $city === '' || $state === '' || !self::is_valid_ymd($date_of_birth)) {
						echo '<div class="notice notice-error"><p>' . esc_html__('Please fill in all required guest fields.', 'vachr-short-term-rental-reservations') . '</p></div>';
					} else {
						$updated = $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
							$gr_table,
							[
								'first_name' => $first_name,
								'last_name' => $last_name,
								'date_of_birth' => $date_of_birth,
								'nationality' => $nationality,
								'document_type' => $document_type,
								'document_number' => $document_number,
								'street' => $street,
								'city' => $city,
								'state' => $state,
							],
							['id' => $guest_id],
							['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'],
							['%d']
						);
						if ($updated === false) {
							echo '<div class="notice notice-error"><p>' . esc_html__('Unable to update guest details.', 'vachr-short-term-rental-reservations') . '</p></div>';
						} else {
							echo '<div class="notice notice-success"><p>' . esc_html__('Guest details updated.', 'vachr-short-term-rental-reservations') . '</p></div>';
						}
					}
				}
			}
		}

		if (isset($_POST['vstrb_pair_booking_group'])) {
			check_admin_referer('vstrb_pair_booking_group', 'vstrb_pair_booking_group_nonce');
			$rep_id = isset($_POST['rep_id']) ? (int) sanitize_text_field(wp_unslash($_POST['rep_id'])) : 0;
			$pair_booking_id = isset($_POST['pair_booking_id']) ? (int) sanitize_text_field(wp_unslash($_POST['pair_booking_id'])) : 0;

			if ($rep_id <= 0) {
				echo '<div class="notice notice-error"><p>' . esc_html__('Registration group not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
			} else {
				$rep = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$gr_table} WHERE id = %d", $rep_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				if (!$rep) {
					echo '<div class="notice notice-error"><p>' . esc_html__('Registration group not found.', 'vachr-short-term-rental-reservations') . '</p></div>';
				} else {
					$new_booking_id = null;
					$new_unit_id = null;
					if ($pair_booking_id > 0) {
						$booking = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
							$wpdb->prepare(
								"SELECT id, unit_id FROM {$t['bookings']}
								 WHERE id = %d
								   AND NOT (date_to <= %s OR date_from >= %s)",
								$pair_booking_id,
								(string) $rep->date_from,
								(string) $rep->date_to
							)
						);
						if (!$booking) {
							echo '<div class="notice notice-error"><p>' . esc_html__('Selected booking is not compatible with this registration group.', 'vachr-short-term-rental-reservations') . '</p></div>';
							$pair_booking_id = 0;
						} else {
							$new_booking_id = (int) $booking->id;
							$new_unit_id = (int) $booking->unit_id;
						}
					}

					if ($pair_booking_id === 0 || $new_booking_id !== null) {
						$group_where = self::guest_registration_group_conditions($rep);
						if ($new_booking_id !== null) {
							$sql = "UPDATE {$gr_table} SET unit_id = %d, booking_id = %d WHERE " . $group_where['sql'];
							$params = array_merge([$new_unit_id, $new_booking_id], $group_where['params']);
						} else {
							$sql = "UPDATE {$gr_table} SET booking_id = NULL WHERE " . $group_where['sql'];
							$params = $group_where['params'];
						}
						$updated = $wpdb->query($wpdb->prepare($sql, ...$params)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
						if ($updated === false) {
							echo '<div class="notice notice-error"><p>' . esc_html__('Unable to update pairing.', 'vachr-short-term-rental-reservations') . '</p></div>';
						} else {
							if ($new_booking_id !== null) {
								echo '<div class="notice notice-success"><p>' . esc_html__('Registration group paired with booking.', 'vachr-short-term-rental-reservations') . '</p></div>';
							} else {
								echo '<div class="notice notice-success"><p>' . esc_html__('Pairing removed.', 'vachr-short-term-rental-reservations') . '</p></div>';
							}
						}
					}
				}
			}
		}

		// Filters
		$filter_unit = self::get_param_int('unit_id', 0);
		$filter_booking_id = self::get_param_int('booking_id', 0);
		$filter_source = self::get_param_text('filter_source', '');
		$filter_date_from = self::get_param_text('filter_date_from', '');
		$filter_date_to = self::get_param_text('filter_date_to', '');
		$filter_guest = self::get_param_text('filter_guest', '');
		$filter_linked = self::get_param_text('filter_linked', '');

		$allowed_sources = ['booking_com', 'airbnb', 'vrbo', 'expedia', 'direct', 'other', ''];
		if (!in_array($filter_source, $allowed_sources, true)) {
			$filter_source = '';
		}
		if ($filter_date_from !== '' && !self::is_valid_ymd($filter_date_from)) {
			$filter_date_from = '';
		}
		if ($filter_date_to !== '' && !self::is_valid_ymd($filter_date_to)) {
			$filter_date_to = '';
		}
		if (!in_array($filter_linked, ['linked', 'unlinked', ''], true)) {
			$filter_linked = '';
		}

		$where_clauses = [];
		if ($filter_unit > 0) {
			$where_clauses[] = $wpdb->prepare('unit_id = %d', $filter_unit);
		}
		if ($filter_booking_id > 0) {
			$where_clauses[] = $wpdb->prepare('booking_id = %d', $filter_booking_id);
		}
		if ($filter_source !== '') {
			$where_clauses[] = $wpdb->prepare('source = %s', $filter_source);
		}
		if ($filter_date_from !== '') {
			$where_clauses[] = $wpdb->prepare('date_to >= %s', $filter_date_from);
		}
		if ($filter_date_to !== '') {
			$where_clauses[] = $wpdb->prepare('date_from <= %s', $filter_date_to);
		}
		if ($filter_linked === 'linked') {
			$where_clauses[] = 'booking_id IS NOT NULL AND booking_id > 0';
		} elseif ($filter_linked === 'unlinked') {
			$where_clauses[] = '(booking_id IS NULL OR booking_id = 0)';
		}
		if ($filter_guest !== '') {
			$like_guest = '%' . $wpdb->esc_like($filter_guest) . '%';
			$where_clauses[] = $wpdb->prepare(
				'(first_name LIKE %s OR last_name LIKE %s OR email LIKE %s OR document_number LIKE %s OR booking_reference LIKE %s)',
				$like_guest,
				$like_guest,
				$like_guest,
				$like_guest,
				$like_guest
			);
		}

		$where = '';
		if (!empty($where_clauses)) {
			$where = 'WHERE ' . implode(' AND ', $where_clauses);
		}

		$rows  = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			"SELECT * FROM {$gr_table} {$where} ORDER BY id DESC"
		);

		// Group rows by booking_reference+unit_id, or by date_from+date_to+unit_id+email
		$groups = [];
		foreach ($rows as $row) {
			if (!empty($row->booking_reference)) {
				$gkey = 'ref:' . $row->unit_id . ':' . $row->booking_reference;
			} else {
				$gkey = 'nref:' . $row->unit_id . ':' . $row->date_from . ':' . $row->date_to . ':' . $row->email;
			}
			$groups[$gkey][] = $row;
		}

		$linked_booking_ids = [];
		foreach ($groups as $group_rows) {
			$booking_ids_in_group = array_values(array_unique(array_filter(array_map(static function($item) {
				return (int) ($item->booking_id ?? 0);
			}, $group_rows))));
			if (count($booking_ids_in_group) === 1) {
				$linked_booking_ids[] = (int) $booking_ids_in_group[0];
			}
		}
		$linked_booking_ids = array_values(array_unique($linked_booking_ids));
		$linked_bookings_map = [];
		if (!empty($linked_booking_ids)) {
			$ids_placeholders = implode(', ', array_fill(0, count($linked_booking_ids), '%d'));
			$linked_rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->prepare(
					"SELECT id, date_from, date_to, source, status, external_booking_id, customer_name, customer_lastname
					 FROM {$t['bookings']}
					 WHERE id IN ({$ids_placeholders})", // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
					...$linked_booking_ids
				)
			);
			foreach ($linked_rows as $lb) {
				$linked_bookings_map[(int) $lb->id] = $lb;
			}
		}

		// Get all units for filter dropdown
		$units = get_posts(['post_type' => 'vstrb_unit', 'posts_per_page' => -1, 'post_status' => 'publish']);
		$sources = [
			'booking_com' => 'Booking.com',
			'airbnb' => 'Airbnb',
			'vrbo' => 'VRBO / HomeAway',
			'expedia' => 'Expedia',
			'direct' => __('Direct booking', 'vachr-short-term-rental-reservations'),
			'other' => __('Other', 'vachr-short-term-rental-reservations'),
		];

		?>
		<div class="wrap">
			<h1><?php esc_html_e('Guest Registrations', 'vachr-short-term-rental-reservations'); ?></h1>

			<form method="get" style="background:#fff;padding:15px;margin:15px 0;border:1px solid #ccd0d4">
				<input type="hidden" name="page" value="vstrb-guests">
				<h3 style="margin-top:0"><?php esc_html_e('Filters', 'vachr-short-term-rental-reservations'); ?></h3>
				<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;align-items:end">
					<div>
						<label><?php esc_html_e('Apartment', 'vachr-short-term-rental-reservations'); ?><br>
							<select name="unit_id" style="width:100%">
								<option value="0"><?php esc_html_e('- All Apartments -', 'vachr-short-term-rental-reservations'); ?></option>
								<?php foreach ($units as $u): ?>
									<option value="<?php echo esc_attr($u->ID); ?>" <?php selected($filter_unit, $u->ID); ?>>
										<?php echo esc_html($u->post_title); ?>
									</option>
								<?php endforeach; ?>
							</select>
						</label>
					</div>
					<div>
						<label><?php esc_html_e('Source', 'vachr-short-term-rental-reservations'); ?><br>
							<select name="filter_source" style="width:100%">
								<option value=""><?php esc_html_e('All sources', 'vachr-short-term-rental-reservations'); ?></option>
								<?php foreach ($sources as $source_key => $source_label): ?>
									<option value="<?php echo esc_attr($source_key); ?>" <?php selected($filter_source, $source_key); ?>>
										<?php echo esc_html($source_label); ?>
									</option>
								<?php endforeach; ?>
							</select>
						</label>
					</div>
					<div><label><?php esc_html_e('Stay From', 'vachr-short-term-rental-reservations'); ?><br><input type="date" name="filter_date_from" value="<?php echo esc_attr($filter_date_from); ?>" style="width:100%"></label></div>
					<div><label><?php esc_html_e('Stay To', 'vachr-short-term-rental-reservations'); ?><br><input type="date" name="filter_date_to" value="<?php echo esc_attr($filter_date_to); ?>" style="width:100%"></label></div>
					<div><label><?php esc_html_e('Guest / Email / Reference', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="filter_guest" value="<?php echo esc_attr($filter_guest); ?>" placeholder="<?php esc_attr_e('Search...', 'vachr-short-term-rental-reservations'); ?>" style="width:100%"></label></div>
					<div><label><?php esc_html_e('Booking ID', 'vachr-short-term-rental-reservations'); ?><br><input type="number" min="0" name="booking_id" value="<?php echo esc_attr((string) $filter_booking_id); ?>" style="width:100%"></label></div>
					<div>
						<label><?php esc_html_e('Pairing', 'vachr-short-term-rental-reservations'); ?><br>
							<select name="filter_linked" style="width:100%">
								<option value=""><?php esc_html_e('All registrations', 'vachr-short-term-rental-reservations'); ?></option>
								<option value="linked" <?php selected($filter_linked, 'linked'); ?>><?php esc_html_e('Linked to booking', 'vachr-short-term-rental-reservations'); ?></option>
								<option value="unlinked" <?php selected($filter_linked, 'unlinked'); ?>><?php esc_html_e('Not linked', 'vachr-short-term-rental-reservations'); ?></option>
							</select>
						</label>
					</div>
				</div>
				<p style="margin-bottom:0">
					<button type="submit" class="button button-primary"><?php esc_html_e('Apply Filters', 'vachr-short-term-rental-reservations'); ?></button>
					<a href="<?php echo esc_url(admin_url('admin.php?page=vstrb-guests')); ?>" class="button"><?php esc_html_e('Clear Filters', 'vachr-short-term-rental-reservations'); ?></a>
					<a href="<?php echo esc_url(add_query_arg(['action' => 'export_csv', 'vstrb_export_nonce' => wp_create_nonce('vstrb_export_csv')], isset($_SERVER['REQUEST_URI']) ? sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])) : '')); ?>" class="button button-secondary">
						<?php esc_html_e('Export to CSV', 'vachr-short-term-rental-reservations'); ?>
					</a>
				</p>
			</form>

			<?php if ($filter_booking_id > 0): ?>
				<div class="notice notice-info is-dismissible">
					<?php
				// translators: %d is the booking ID number.
				$notice_text = sprintf(__('Showing guest registrations for booking #%d', 'vachr-short-term-rental-reservations'), $filter_booking_id);
				?>
				<p><?php echo esc_html($notice_text); ?></p>
				</div>
			<?php endif; ?>

			<?php if (empty($rows)): ?>
				<p><?php esc_html_e('No registrations found.', 'vachr-short-term-rental-reservations'); ?></p>
			<?php else: ?>
			<table class="wp-list-table widefat">
				<thead>
					<tr>
						<th><?php esc_html_e('Apartment', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Stay', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Source', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Booking Reference', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Email', 'vachr-short-term-rental-reservations'); ?></th>
						<th style="width:80px"><?php esc_html_e('Guests', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Submitted', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Linked Booking', 'vachr-short-term-rental-reservations'); ?></th>
						<th><?php esc_html_e('Actions', 'vachr-short-term-rental-reservations'); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php
					$g_index = 0;
					foreach ($groups as $group_key => $group):
						$rep       = $group[0];
						$count     = count($group);
						$rep_id    = (int) $rep->id;
						$toggle_id = 'vstrb-gr-group-' . $rep_id;
						$row_bg    = ($g_index % 2 === 0) ? '' : 'background:#f6f7f7;';
						$del_group_url = wp_nonce_url(
							admin_url('admin.php?page=vstrb-guests&action=delete_group&id=' . $rep_id),
							'vstrb_guest_delete_group_' . $rep_id
						);
						$invoice_group_url = wp_nonce_url(
							admin_url('admin.php?page=vstrb-guests&action=print_invoice_group&id=' . $rep_id),
							'vstrb_guest_print_invoice_' . $rep_id
						);
						$edit_form_id = 'vstrb-gr-edit-' . $rep_id;
						$group_booking_ids = array_values(array_unique(array_filter(array_map(static function($item) {
							return (int) ($item->booking_id ?? 0);
						}, $group))));
						$group_booking_id = (count($group_booking_ids) === 1) ? (int) $group_booking_ids[0] : 0;
						$link_booking = ($group_booking_id > 0) ? ($linked_bookings_map[$group_booking_id] ?? null) : null;
						$candidates = self::booking_candidates_for_registration_group($rep, 30);
						$g_index++;
					?>
					<tr class="vstrb-gr-summary-row" style="<?php echo esc_attr($row_bg); ?>">
						<td><?php echo esc_html(get_the_title($rep->unit_id)); ?></td>
                        <td><?php echo esc_html($rep->date_from . ' - ' . $rep->date_to); ?></td>
						<td><?php echo esc_html($rep->source ?? ''); ?></td>
						<td><?php echo esc_html($rep->booking_reference ?? ''); ?></td>
						<td><?php echo esc_html($rep->email); ?></td>
						<td>
							<?php echo (int) $count; ?><br>
							<a href="#"
							   class="vstrb-gr-toggle"
							   data-group-id="<?php echo esc_attr($toggle_id); ?>"
							   style="font-size:11px"
							><?php esc_html_e('Show guests', 'vachr-short-term-rental-reservations'); ?></a>
						</td>
						<td><?php echo esc_html(substr($rep->created_at, 0, 16)); ?></td>
						<td>
							<?php if ($group_booking_id > 0 && $link_booking): ?>
								<?php
									$booking_admin_url = admin_url('admin.php?page=vstrb&filter_booking_id=' . (int) $group_booking_id);
									$label_parts = ['#' . (int) $group_booking_id];
									if (!empty($link_booking->external_booking_id)) {
										$label_parts[] = (string) $link_booking->external_booking_id;
									}
								?>
								<a href="<?php echo esc_url($booking_admin_url); ?>">
									<?php echo esc_html(implode(' / ', $label_parts)); ?>
								</a>
								<br><small><?php echo esc_html(($link_booking->date_from ?? '') . ' - ' . ($link_booking->date_to ?? '')); ?></small>
							<?php elseif ($group_booking_id > 0): ?>
								<?php echo esc_html('#' . $group_booking_id); ?>
							<?php elseif (count($group_booking_ids) > 1): ?>
								<?php esc_html_e('Mixed group (different links)', 'vachr-short-term-rental-reservations'); ?>
							<?php else: ?>
								<?php esc_html_e('Not linked', 'vachr-short-term-rental-reservations'); ?>
							<?php endif; ?>
						</td>
						<td>
							<form method="post" style="margin:0 0 6px 0">
								<?php wp_nonce_field('vstrb_pair_booking_group', 'vstrb_pair_booking_group_nonce'); ?>
								<input type="hidden" name="rep_id" value="<?php echo (int) $rep_id; ?>">
								<select name="pair_booking_id" style="max-width:280px">
									<option value="0"><?php esc_html_e('- Unpair / no booking -', 'vachr-short-term-rental-reservations'); ?></option>
									<?php foreach ($candidates as $cand): ?>
										<?php
											$cand_id = (int) ($cand->id ?? 0);
											$cand_external = trim((string) ($cand->external_booking_id ?? ''));
											$cand_guest = trim((string) ($cand->customer_name ?? '') . ' ' . (string) ($cand->customer_lastname ?? ''));
											$cand_label = '#' . $cand_id;
											if ($cand_external !== '') {
												$cand_label .= ' / ' . $cand_external;
											}
											$cand_unit = get_the_title((int) ($cand->unit_id ?? 0));
											if ($cand_unit !== '') {
												$cand_label .= ' / ' . $cand_unit;
											}
											$cand_label .= ' / ' . (string) ($cand->date_from ?? '') . ' - ' . (string) ($cand->date_to ?? '');
											if ($cand_guest !== '') {
												$cand_label .= ' / ' . $cand_guest;
											}
										?>
										<option value="<?php echo esc_attr($cand_id); ?>" <?php selected($group_booking_id, $cand_id); ?>>
											<?php echo esc_html($cand_label); ?>
										</option>
									<?php endforeach; ?>
								</select>
								<button type="submit" name="vstrb_pair_booking_group" value="1" class="button button-small"><?php esc_html_e('Pair', 'vachr-short-term-rental-reservations'); ?></button>
							</form>
							<a href="<?php echo esc_url($invoice_group_url); ?>" target="_blank" class="button button-small">
								<?php esc_html_e('Print invoice', 'vachr-short-term-rental-reservations'); ?>
							</a>
							<button type="button" class="button button-small vstrb-gr-edit-toggle" data-edit-id="<?php echo esc_attr($edit_form_id); ?>">
								<?php esc_html_e('Edit contact', 'vachr-short-term-rental-reservations'); ?>
							</button>
							<a href="<?php echo esc_url($del_group_url); ?>"
							   onclick="return confirm('<?php esc_attr_e('Delete entire group?', 'vachr-short-term-rental-reservations'); ?>')"
							   class="button button-small button-link-delete"
							><?php esc_html_e('Delete group', 'vachr-short-term-rental-reservations'); ?></a>
						</td>
					</tr>
					<tr class="vstrb-gr-detail-row" id="<?php echo esc_attr($toggle_id); ?>" style="display:none">
						<td colspan="9" style="background:#f9f9f9;padding:12px 16px">
							<form method="post" id="<?php echo esc_attr($edit_form_id); ?>" class="vstrb-gr-edit-form" style="display:none;background:#fff;border:1px solid #dcdcde;padding:12px;margin-bottom:12px">
								<?php wp_nonce_field('vstrb_update_contact_group', 'vstrb_update_contact_group_nonce'); ?>
								<input type="hidden" name="rep_id" value="<?php echo (int) $rep_id; ?>">
								<div style="display:grid;grid-template-columns:repeat(3,minmax(150px,1fr));gap:10px;margin-bottom:10px">
									<label><?php esc_html_e('Email', 'vachr-short-term-rental-reservations'); ?><br><input type="email" name="group_email" value="<?php echo esc_attr((string) $rep->email); ?>" required style="width:100%"></label>
									<label><?php esc_html_e('Apartment', 'vachr-short-term-rental-reservations'); ?><br>
										<select name="group_unit_id" required style="width:100%">
											<?php foreach ($units as $u): ?>
												<option value="<?php echo esc_attr($u->ID); ?>" <?php selected((int) $rep->unit_id, (int) $u->ID); ?>>
													<?php echo esc_html($u->post_title); ?>
												</option>
											<?php endforeach; ?>
										</select>
									</label>
									<label><?php esc_html_e('Check-in Date', 'vachr-short-term-rental-reservations'); ?><br><input type="date" name="group_date_from" value="<?php echo esc_attr((string) $rep->date_from); ?>" required style="width:100%"></label>
									<label><?php esc_html_e('Check-out Date', 'vachr-short-term-rental-reservations'); ?><br><input type="date" name="group_date_to" value="<?php echo esc_attr((string) $rep->date_to); ?>" required style="width:100%"></label>
									<label><?php esc_html_e('Booking Source', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_source" value="<?php echo esc_attr((string) ($rep->source ?? '')); ?>" style="width:100%"></label>
									<label><?php esc_html_e('Booking Reference', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_booking_reference" value="<?php echo esc_attr((string) ($rep->booking_reference ?? '')); ?>" style="width:100%"></label>
									<label style="display:flex;align-items:end;gap:6px"><input type="checkbox" name="group_billing_enabled" value="1" <?php checked(!empty($rep->billing_enabled)); ?>> <?php esc_html_e('Billing enabled', 'vachr-short-term-rental-reservations'); ?></label>
								</div>
								<div style="display:grid;grid-template-columns:repeat(4,minmax(120px,1fr));gap:10px;margin-bottom:10px">
									<label><?php esc_html_e('Billing Name', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_billing_name" value="<?php echo esc_attr((string) ($rep->billing_name ?? '')); ?>" style="width:100%"></label>
									<label><?php esc_html_e('Company ID', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_billing_company_id" value="<?php echo esc_attr((string) ($rep->billing_company_id ?? '')); ?>" style="width:100%"></label>
									<label><?php esc_html_e('VAT ID', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_billing_vat_id" value="<?php echo esc_attr((string) ($rep->billing_vat_id ?? '')); ?>" style="width:100%"></label>
									<label><?php esc_html_e('ZIP Code', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_billing_zip" value="<?php echo esc_attr((string) ($rep->billing_zip ?? '')); ?>" style="width:100%"></label>
									<label><?php esc_html_e('Billing Street', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_billing_street" value="<?php echo esc_attr((string) ($rep->billing_street ?? '')); ?>" style="width:100%"></label>
									<label><?php esc_html_e('Billing City', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_billing_city" value="<?php echo esc_attr((string) ($rep->billing_city ?? '')); ?>" style="width:100%"></label>
									<label><?php esc_html_e('Billing Country', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="group_billing_country" value="<?php echo esc_attr((string) ($rep->billing_country ?? '')); ?>" style="width:100%"></label>
								</div>
								<?php $save_contact_label = self::i18n_with_locale_fallback('Save Contact', ['cs' => 'Uložit kontakt']); ?>
								<input type="submit" name="vstrb_update_contact_group" value="<?php echo esc_attr($save_contact_label); ?>" class="button button-primary button-small" style="min-width:120px;color:#fff;font-size:13px;" />
							</form>
							<table class="widefat" style="background:#fff">
								<thead>
									<tr>
										<th style="width:35px"><?php esc_html_e('ID', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('Name', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('Date of Birth', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('Nationality', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('Document', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('Street', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('City', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('Country', 'vachr-short-term-rental-reservations'); ?></th>
										<th><?php esc_html_e('Actions', 'vachr-short-term-rental-reservations'); ?></th>
									</tr>
								</thead>
								<tbody>
									<?php foreach ($group as $guest):
										$guest_edit_form_id = 'vstrb-gr-guest-edit-' . (int) $guest->id;
										$del_url = wp_nonce_url(
											admin_url('admin.php?page=vstrb-guests&action=delete&id=' . (int)$guest->id),
											'vstrb_guest_delete_' . (int)$guest->id
										);
									?>
									<tr>
										<td><?php echo (int) $guest->id; ?></td>
										<td><?php echo esc_html($guest->first_name . ' ' . $guest->last_name); ?></td>
										<td><?php echo esc_html($guest->date_of_birth); ?></td>
										<td><?php echo esc_html($guest->nationality); ?></td>
										<td><?php echo esc_html(ucfirst(str_replace('_', ' ', $guest->document_type)) . ': ' . $guest->document_number); ?></td>
										<td><?php echo esc_html($guest->street ?? ''); ?></td>
										<td><?php echo esc_html($guest->city ?? ''); ?></td>
										<td><?php echo esc_html($guest->state ?? ''); ?></td>
										<td>
											<button type="button" class="button button-small vstrb-gr-edit-guest-toggle" data-edit-id="<?php echo esc_attr($guest_edit_form_id); ?>">
												<?php esc_html_e('Edit guest', 'vachr-short-term-rental-reservations'); ?>
											</button>
											<a href="<?php echo esc_url($del_url); ?>"
											   onclick="return confirm('<?php esc_attr_e('Delete this registration?', 'vachr-short-term-rental-reservations'); ?>')"
											   class="button button-small button-link-delete"
											><?php esc_html_e('Delete', 'vachr-short-term-rental-reservations'); ?></a>
											<form method="post" id="<?php echo esc_attr($guest_edit_form_id); ?>" class="vstrb-gr-edit-guest-form" style="display:none;margin-top:8px;padding:8px;background:#f6f7f7;border:1px solid #dcdcde">
												<?php wp_nonce_field('vstrb_update_guest_registration', 'vstrb_update_guest_registration_nonce'); ?>
												<input type="hidden" name="guest_id" value="<?php echo (int) $guest->id; ?>">
												<div style="display:grid;grid-template-columns:repeat(2,minmax(120px,1fr));gap:6px">
													<label><?php esc_html_e('First Name', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_first_name" value="<?php echo esc_attr((string) ($guest->first_name ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('Last Name', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_last_name" value="<?php echo esc_attr((string) ($guest->last_name ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('Date of Birth', 'vachr-short-term-rental-reservations'); ?><br><input type="date" name="guest_date_of_birth" value="<?php echo esc_attr((string) ($guest->date_of_birth ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('Nationality', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_nationality" value="<?php echo esc_attr((string) ($guest->nationality ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('Document Type', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_document_type" value="<?php echo esc_attr((string) ($guest->document_type ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('Document Number', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_document_number" value="<?php echo esc_attr((string) ($guest->document_number ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('Street', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_street" value="<?php echo esc_attr((string) ($guest->street ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('City', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_city" value="<?php echo esc_attr((string) ($guest->city ?? '')); ?>" required style="width:100%"></label>
													<label><?php esc_html_e('Country', 'vachr-short-term-rental-reservations'); ?><br><input type="text" name="guest_state" value="<?php echo esc_attr((string) ($guest->state ?? '')); ?>" required style="width:100%"></label>
												</div>
												<?php $save_guest_label = self::i18n_with_locale_fallback('Save guest', ['cs' => 'Uložit hosta']); ?>
												<input type="submit" name="vstrb_update_guest_registration" value="<?php echo esc_attr($save_guest_label); ?>" class="button button-primary button-small" style="margin-top:8px;min-width:110px;color:#fff;font-size:13px;" />
											</form>
										</td>
									</tr>
									<?php endforeach; ?>
								</tbody>
							</table>
						</td>
					</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
			<?php endif; ?>
		</div>
		<?php
		$show_guests_text = wp_json_encode(__('Show guests', 'vachr-short-term-rental-reservations'));
		$hide_guests_text = wp_json_encode(__('Hide guests', 'vachr-short-term-rental-reservations'));
		self::add_inline_admin_script("
			document.addEventListener('DOMContentLoaded', function() {
				document.querySelectorAll('.vstrb-gr-toggle').forEach(function(link) {
					link.addEventListener('click', function(e) {
						e.preventDefault();
						var groupId = this.getAttribute('data-group-id');
						var detailRow = document.getElementById(groupId);
						if (!detailRow) return;
						var expanded = detailRow.style.display !== 'none';
						detailRow.style.display = expanded ? 'none' : 'table-row';
						this.textContent = expanded ? {$show_guests_text} : {$hide_guests_text};
					});
				});
				document.querySelectorAll('.vstrb-gr-edit-toggle').forEach(function(btn) {
					btn.addEventListener('click', function() {
						var formId = this.getAttribute('data-edit-id');
						var frm = document.getElementById(formId);
						if (!frm) return;
						frm.style.display = (frm.style.display === 'none' || !frm.style.display) ? 'block' : 'none';
					});
				});
				document.querySelectorAll('.vstrb-gr-edit-guest-toggle').forEach(function(btn) {
					btn.addEventListener('click', function() {
						var formId = this.getAttribute('data-edit-id');
						var frm = document.getElementById(formId);
						if (!frm) return;
						frm.style.display = (frm.style.display === 'none' || !frm.style.display) ? 'block' : 'none';
					});
				});
			});
		");
		self::render_donation_box();
	}

	public static function handle_print_invoice() {
		if (!current_user_can('manage_options')) {
			return;
		}
		$page   = isset($_GET['page'])   ? sanitize_key($_GET['page'])   : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$action = isset($_GET['action']) ? sanitize_key($_GET['action']) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$id     = isset($_GET['id'])     ? (int) $_GET['id']             : 0;  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$nonce  = isset($_GET['_wpnonce']) ? sanitize_key($_GET['_wpnonce']) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		if ($page === 'vstrb-transactions' && $action === 'print_invoice' && $id > 0 && $nonce !== '') {
			if (!wp_verify_nonce($nonce, 'vstrb_print_invoice_' . $id)) {
				wp_die(esc_html__('Security check failed.', 'vachr-short-term-rental-reservations'));
			}
			self::render_booking_invoice_page($id);
		}

		if ($page === 'vstrb-guests' && $action === 'print_invoice_group' && $id > 0 && $nonce !== '') {
			if (!wp_verify_nonce($nonce, 'vstrb_guest_print_invoice_' . $id)) {
				wp_die(esc_html__('Security check failed.', 'vachr-short-term-rental-reservations'));
			}
			global $wpdb;
			$t        = VSTRB_DB::tables();
			$gr_table = $t['guest_registrations'] ?? '';
			if ($gr_table === '') {
				wp_die(esc_html__('Registration group not found.', 'vachr-short-term-rental-reservations'));
			}
			$rep = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->prepare("SELECT * FROM {$gr_table} WHERE id = %d", $id)
			);
			if (!$rep) {
				wp_die(esc_html__('Registration group not found.', 'vachr-short-term-rental-reservations'));
			}
			$group_rows = self::group_rows_for_guest_registration($rep);
			$booking_id = (int) ($rep->booking_id ?? 0);
			$booking    = null;
			if ($booking_id > 0) {
				$booking = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
					$wpdb->prepare("SELECT id, total_price, currency FROM {$t['bookings']} WHERE id = %d", $booking_id)
				);
			}
			self::render_guest_invoice_page($rep, $group_rows, $booking);
		}
	}

	public static function handle_export() {
		if (isset($_GET['page']) && $_GET['page'] === 'vstrb-guests' && isset($_GET['action']) && $_GET['action'] === 'export_csv') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if (!isset($_GET['vstrb_export_nonce']) || !wp_verify_nonce(sanitize_key($_GET['vstrb_export_nonce']), 'vstrb_export_csv')) {
				wp_die(esc_html__('Security check failed.', 'vachr-short-term-rental-reservations'));
			}
			self::export_guest_registrations_csv();
		}
	}

	private static function export_guest_registrations_csv() {
		if (!current_user_can('manage_options')) {
			wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'vachr-short-term-rental-reservations'));
		}

		global $wpdb;
		$t = VSTRB_DB::tables();
		$gr_table = $t['guest_registrations'];
		$filter_unit = self::get_param_int('unit_id', 0);
		$where = $filter_unit > 0 ? $wpdb->prepare('WHERE unit_id = %d', $filter_unit) : '';

		$rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			"SELECT * FROM {$gr_table} {$where} ORDER BY id DESC"
		);

		$filename = 'guest-registrations-' . gmdate('Y-m-d') . '.csv';

		header('Content-Type: text/csv; charset=utf-8');
		header('Content-Disposition: attachment; filename=' . $filename);

		// Build CSV in memory then echo — avoids direct filesystem calls on php://output.
		ob_start();
		$csv_handle = fopen('php://temp', 'w'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
		// BOM for Excel
		fwrite($csv_handle, "\xEF\xBB\xBF"); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite

		fputcsv($csv_handle, [ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fputcsv
			__('Apartment', 'vachr-short-term-rental-reservations'),
			__('First Name', 'vachr-short-term-rental-reservations'),
			__('Last Name', 'vachr-short-term-rental-reservations'),
			__('Date of Birth', 'vachr-short-term-rental-reservations'),
			__('Street', 'vachr-short-term-rental-reservations'),
			__('City', 'vachr-short-term-rental-reservations'),
			__('State', 'vachr-short-term-rental-reservations'),
			__('Document Type', 'vachr-short-term-rental-reservations'),
			__('Document Number', 'vachr-short-term-rental-reservations'),
			__('Nationality', 'vachr-short-term-rental-reservations'),
			__('Stay From', 'vachr-short-term-rental-reservations'),
			__('Stay To', 'vachr-short-term-rental-reservations'),
			__('Email', 'vachr-short-term-rental-reservations'),
			__('Source', 'vachr-short-term-rental-reservations'),
			__('Booking Reference', 'vachr-short-term-rental-reservations'),
			__('Billing Enabled', 'vachr-short-term-rental-reservations'),
			__('Billing Name', 'vachr-short-term-rental-reservations'),
			__('Company ID', 'vachr-short-term-rental-reservations'),
			__('VAT ID', 'vachr-short-term-rental-reservations'),
			__('Billing Street', 'vachr-short-term-rental-reservations'),
			__('Billing City', 'vachr-short-term-rental-reservations'),
			__('Billing ZIP', 'vachr-short-term-rental-reservations'),
			__('Billing Country', 'vachr-short-term-rental-reservations'),
			__('Submitted', 'vachr-short-term-rental-reservations'),
		]);

		foreach ($rows as $row) {
			fputcsv($csv_handle, [ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fputcsv
				get_the_title($row->unit_id),
				$row->first_name,
				$row->last_name,
				$row->date_of_birth,
				$row->street,
				$row->city,
				$row->state,
				$row->document_type,
				$row->document_number,
				$row->nationality,
				$row->date_from,
				$row->date_to,
				$row->email,
				$row->source,
				$row->booking_reference,
				!empty($row->billing_enabled) ? '1' : '0',
				$row->billing_name ?? '',
				$row->billing_company_id ?? '',
				$row->billing_vat_id ?? '',
				$row->billing_street ?? '',
				$row->billing_city ?? '',
				$row->billing_zip ?? '',
				$row->billing_country ?? '',
				$row->created_at,
			]);
		}

		rewind($csv_handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rewind
		$csv_content = stream_get_contents($csv_handle);
		fclose($csv_handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
		ob_end_clean();

		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $csv_content;
		exit;
	}

	private static function render_donation_box() {
		?>
		<div style="margin-top: 40px; padding: 20px; background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; max-width: 800px; display: flex; align-items: center; gap: 20px; box-shadow: 0 1px 1px rgba(0,0,0,.04);">
			<div style="font-size: 40px; color: #f0c330;">
				<span class="dashicons dashicons-coffee" style="font-size: inherit; width: auto; height: auto;"></span>
			</div>
			<div style="flex-grow: 1;">
				<h3 style="margin-top: 0; margin-bottom: 5px;"><?php esc_html_e('Support Developer', 'vachr-short-term-rental-reservations'); ?></h3>
				<p style="margin: 0; color: #666;"><?php esc_html_e('If you like this plugin and find it useful, please consider a small donation to support further development and maintenance. Help me keep it free!', 'vachr-short-term-rental-reservations'); ?></p>
				<p style="margin: 8px 0 0; color: #666;"><?php esc_html_e('Need help with setup? I offer one-time WordPress setup for short-term rentals, including connection to Booking.com, Airbnb, and more for 100 EUR, plus optional yearly light support for 20 EUR.', 'vachr-short-term-rental-reservations'); ?></p>
			</div>
			<div>
				<a href="https://buymeacoffee.com/vachr" target="_blank" class="button button-primary button-hero" style="background: #FFDD00; border-color: #FFDD00; color: #000000; display: flex; align-items: center; gap: 8px; font-weight: bold;">
					<span class="dashicons dashicons-coffee" style="margin-top: 4px;"></span>
					<?php esc_html_e('Buy me a coffee', 'vachr-short-term-rental-reservations'); ?>
				</a>
			</div>
		</div>
		<?php
	}
	// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value, PluginCheck.Security.DirectDB.UnescapedDBParameter
}

add_action('admin_menu', ['VSTRB_Admin', 'register']);
