<?php
/**
 * Admin AJAX handlers for module settings save/load.
 *
 * @package WP_PowerSuite\Core\Http\Controllers
 * @since 1.0.0
 */

namespace WP_PowerSuite\Core\Http\Controllers;

use WP_PowerSuite\Core\ApplicationPasswordConflictPolicy;
use WP_PowerSuite\Core\Http\AjaxRouter;
use WP_PowerSuite\Core\Http\Concerns\ParsesModuleAjaxInput;
use WP_PowerSuite\Core\ModuleRegistry;
use WP_PowerSuite\Core\OptionStore;
use WP_PowerSuite\Core\OptionStoreModuleSettingsRepository;
use WP_PowerSuite\Core\TemporarySessionGuard;
use WP_PowerSuite\Core\WpConfigHelper;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class ModuleSettingsController {

	use ParsesModuleAjaxInput;

	private static ?self $instance = null;

	public static function getInstance(): self {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public static function register(): void {
		$controller = self::getInstance();
		AjaxRouter::registerAdminAction( 'wp_powersuite_save_module_settings', [ $controller, 'saveModuleSettings' ] );
		AjaxRouter::registerAdminAction( 'wp_powersuite_load_module_settings', [ $controller, 'loadModuleSettings' ] );
	}

	public function saveModuleSettings(): void {
		$module_id = $this->getPostedModuleId();
		if ( ! $module_id ) {
			wp_send_json_error(
				[
					'message' => __( 'Invalid module ID provided.', 'wp-powersuite' ),
					'code'    => 'invalid_module_id',
				]
			);
			return;
		}

		if ( TemporarySessionGuard::isActive() && TemporarySessionGuard::blocksModule( $module_id ) ) {
			wp_send_json_error( TemporarySessionGuard::deniedPayload() );
			return;
		}

		$module_registry = ModuleRegistry::getInstance();
		$module_registry->init();
		$module_metadata = $module_registry->getModuleMetadata( $module_id );
		if ( ! empty( $module_metadata['is_pro'] ) && function_exists( 'wp_powersuite_is_license_valid' ) && ! wp_powersuite_is_license_valid() ) {
			wp_send_json_error(
				[
					'message'      => __( 'A valid license is required to save settings for premium modules.', 'wp-powersuite' ),
					'code'         => 'license_required',
					'license_page' => admin_url( 'admin.php?page=powersuite-license' ),
				]
			);
			return;
		}

		$settings_repo = OptionStoreModuleSettingsRepository::forModule( $module_id );

		// Header/Footer Code requires unfiltered_html (can inject scripts)
		if ( $module_id === 'header-footer-code' && ! current_user_can( 'unfiltered_html' ) ) {
			wp_send_json_error(
				[
					'message' => __( 'You need the unfiltered_html capability to save header/footer code.', 'wp-powersuite' ),
					'code'    => 'insufficient_permissions',
				]
			);
			return;
		}

		$raw_settings = isset( $_POST['settings'] ) ? wp_unslash( $_POST['settings'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- validated per module below
		
		if ( ! is_array( $raw_settings ) ) {
			$raw_settings = [];
		}

		// Header/Footer Code: reject over-length code instead of silently truncating (avoids corrupting scripts).
		if ( $module_id === 'header-footer-code' ) {
			$this->ensureHeaderFooterCodeModuleLoaded();
			$hfc_max = \HeaderFooterCode\Module::getMaxCodeLength();
			$hfc_labels = [
				'header_code' => __( 'Header Code', 'wp-powersuite' ),
				'body_code'   => __( 'Body Code', 'wp-powersuite' ),
				'footer_code' => __( 'Footer Code', 'wp-powersuite' ),
			];
			foreach ( $hfc_labels as $hfc_key => $hfc_label ) {
				if ( isset( $raw_settings[ $hfc_key ] ) && is_string( $raw_settings[ $hfc_key ] ) && strlen( $raw_settings[ $hfc_key ] ) > $hfc_max ) {
					wp_send_json_error(
						[
							/* translators: 1: field label, 2: maximum character count. */
							'message' => sprintf( __( '%1$s is too long. Please keep it under %2$s characters.', 'wp-powersuite' ), $hfc_label, number_format_i18n( $hfc_max ) ),
							'code'    => 'code_too_long',
						]
					);
					return;
				}
			}
		}

		// Custom Image Sizes fallback: nested FormData can fail on some hosts; use top-level param
		if ( $module_id === 'custom-image-sizes' && isset( $_POST['wpps_cis_custom_sizes'] ) && is_string( $_POST['wpps_cis_custom_sizes'] ) ) {
			$raw_settings['custom_sizes'] = wp_unslash( $_POST['wpps_cis_custom_sizes'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- validated in custom_sizes handler
		}

		$has_nested_roles = isset( $raw_settings['roles'] ) && is_array( $raw_settings['roles'] );
		
		if ( ! $has_nested_roles ) {
			$normalized_settings = [];
			foreach ( $raw_settings as $key => $value ) {
				// Skip null values
				if ( $value === null ) {
					continue;
				}
				
				if ( strpos( $key, 'roles' ) === 0 && strlen( $key ) > 5 && ! isset( $raw_settings['roles'] ) ) {
					$role_key = substr( $key, 5 );
					if ( ! isset( $normalized_settings['roles'] ) ) {
						$normalized_settings['roles'] = [];
					}
					$normalized_settings['roles'][ $role_key ] = $value;
				} else {
					$normalized_settings[ $key ] = $value;
				}
			}
			
			if ( isset( $normalized_settings['roles'] ) && ! empty( $normalized_settings['roles'] ) && is_array( $normalized_settings['roles'] ) ) {
				foreach ( $normalized_settings as $k => $v ) {
					if ( $k !== 'roles' ) {
						$normalized_settings[ $k ] = $v;
					}
				}
				$raw_settings = $normalized_settings;
			}
		}
		
		if ( ! is_array( $raw_settings ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Invalid settings data provided.', 'wp-powersuite' ),
					'code'    => 'invalid_settings',
				]
			);
			return;
		}

		// Sanitize settings array (recursively handle nested arrays)
		$settings = [];
		foreach ( $raw_settings as $key => $value ) {
			$key = sanitize_key( $key );
			
			// Skip null values
			if ( $value === null ) {
				continue;
			}
			
			// Captcha modules (Google reCAPTCHA + Turnstile): skip_rules has a textarea of IPs/CIDR
			// that must be split into an array before the generic nested sanitizer runs — otherwise
			// sanitize_text_field would strip the newlines and collapse the list into one entry.
			if ( $key === 'skip_rules' && in_array( $module_id, [ 'simple-captcha', 'google-recaptcha', 'cloudflare-turnstile' ], true ) ) {
				$rules_value = is_array( $value ) ? $value : [];
				$roles_in    = isset( $rules_value['roles'] ) && is_array( $rules_value['roles'] ) ? $rules_value['roles'] : [];
				$ips_in      = $rules_value['ips'] ?? '';
				if ( is_array( $ips_in ) ) {
					$ips_in = implode( "\n", array_map( 'strval', $ips_in ) );
				}
				$ip_list = preg_split( '/\r\n|\r|\n/', (string) $ips_in );
				$ip_list = is_array( $ip_list ) ? array_values( array_filter( array_map( 'trim', $ip_list ) ) ) : [];
				$user_agents_in = $rules_value['user_agents'] ?? '';
				if ( is_array( $user_agents_in ) ) {
					$user_agents_in = implode( "\n", array_map( 'strval', $user_agents_in ) );
				}
				$ua_list = preg_split( '/\r\n|\r|\n/', (string) $user_agents_in );
				$ua_list = is_array( $ua_list ) ? array_values( array_filter( array_map( 'trim', $ua_list ) ) ) : [];
				$settings[ $key ] = [
					'enabled'       => ! empty( $rules_value['enabled'] ),
					'roles'         => array_values( array_unique( array_map( 'sanitize_key', $roles_in ) ) ),
					'ips'           => array_map( 'sanitize_text_field', $ip_list ),
					'user_agents'   => array_map( 'sanitize_text_field', $ua_list ),
				];
				continue;
			}

			if ( is_array( $value ) ) {
				if ( $this->hasNestedArrays( $value ) ) {
					$settings[ $key ] = $this->sanitizeNestedArray( $value );
					continue;
				}
				
				if ( empty( $value ) ) {
					$settings[ $key ] = [];
					continue;
				}
				
				$array_keys = array_keys( $value );
				if ( $array_keys === null ) {
					continue;
				}
				$is_associative = $array_keys !== range( 0, count( $value ) - 1 );
				
				if ( $is_associative ) {
					// Associative array - preserve keys, sanitize values
					$sanitized = [];
					foreach ( $value as $sub_key => $sub_value ) {
						// Skip null values
						if ( $sub_value === null ) {
							continue;
						}
						
						$sub_key = sanitize_key( $sub_key );

						// Numeric IDs must preserve 0 (otherwise keys disappear and nested settings break).
						if ( is_string( $sub_value ) && str_ends_with( $sub_key, '_id' ) ) {
							$sanitized[ $sub_key ] = max( 0, (int) $sub_value );
							continue;
						}

						// Convert checkbox values '1'/'0' to boolean
						if ( $sub_value === '1' || $sub_value === 'true' || $sub_value === true ) {
							$sanitized[ $sub_key ] = true;
						} elseif ( $sub_value === '0' || $sub_value === 'false' || $sub_value === false ) {
							// For checkboxes, skip false values - only save checked checkboxes
							continue;
						} elseif ( is_array( $sub_value ) ) {
							// Recursively sanitize nested arrays
							$sanitized[ $sub_key ] = $this->sanitizeNestedArray( $sub_value );
						} else {
							$sanitized[ $sub_key ] = sanitize_text_field( $sub_value );
						}
					}
					// Always set the key, even if empty array (allows clearing all selections)
					$settings[ $key ] = $sanitized;
				} else {
					// Indexed array (like disabled_blocks[] or disabled_post_types[])
					// Filter out empty values and sanitize
					$filtered = array_filter( $value, function( $v ) {
						return ! empty( $v ) && $v !== '' && $v !== '[]';
					} );
					// Always set as array, even if empty (re-index array)
					// This ensures empty arrays are saved (allows clearing all selections)
					$settings[ $key ] = array_values( array_map( 'sanitize_text_field', $filtered ) );
				}
			} elseif ( is_string( $value ) ) {
				if ( $module_id === 'custom-admin-css' && $key === 'custom_css' ) {
					$settings[ $key ] = sanitize_textarea_field( $value );
					continue;
				}
				if ( $module_id === 'header-footer-code' && in_array( $key, [ 'header_code', 'body_code', 'footer_code' ], true ) ) {
					$settings[ $key ] = $this->sanitizeHeaderFooterCode( $value );
					continue;
				}
				if ( $module_id === 'custom-admin-footer-text' && ( $key === 'footer_left' || $key === 'footer_right' ) ) {
					$settings[ $key ] = wp_kses_post( $value );
					continue;
				}
				if ( $module_id === 'email-reports' && $key === 'footer_text' ) {
					$settings[ $key ] = wp_kses_post( $value );
					continue;
				}
				if ( $module_id === 'ads-txt-manager' && $key === 'content' ) {
					$content = sanitize_textarea_field( $value );
					$size_error = \WP_PowerSuite\Core\RootTextFileHelper::validateContentSize( $content );
					if ( $size_error !== null ) {
						wp_send_json_error(
							[
								'message' => $size_error,
								'code'    => 'content_too_large',
							]
						);
					}
					$settings[ $key ] = $content;
					continue;
				}
				if ( $module_id === 'advanced-security-headers' && $key === 'content_security_policy_value' ) {
					if ( ! class_exists( 'AdvancedSecurityHeaders\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
						$mod_file = WP_POWERSUITE_MODULES_DIR . 'advanced-security-headers' . DIRECTORY_SEPARATOR . 'module.php';
						if ( is_readable( $mod_file ) ) {
							require_once $mod_file;
						}
					}
					$settings[ $key ] = class_exists( 'AdvancedSecurityHeaders\\Module' )
						? \AdvancedSecurityHeaders\Module::sanitizeCspValue( $value )
						: sanitize_textarea_field( $value );
					continue;
				}
				if ( $module_id === 'link-shortener' && $key === 'analytics_retention_days' ) {
					$settings[ $key ] = max( 0, min( 365, (int) $value ) );
					continue;
				}
				if ( $module_id === 'link-shortener' && $key === 'default_short_code_length' ) {
					$settings[ $key ] = max( 4, min( 20, (int) $value ) );
					continue;
				}
				if ( $module_id === 'advanced-user-activity-log' && $key === 'retention_days' ) {
					$settings[ $key ] = max( 1, min( 365, (int) $value ) );
					continue;
				}
				if ( $module_id === 'advanced-user-activity-log' && $key === 'auto_cleanup' ) {
					$settings[ $key ] = 1;
					continue;
				}
				if ( $module_id === 'alpine-js-enqueue' && $key === 'init_code' ) {
					if ( ! class_exists( \AlpineJsEnqueue\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
						$mod = WP_POWERSUITE_MODULES_DIR . 'alpine-js-enqueue' . DIRECTORY_SEPARATOR . 'module.php';
						if ( is_readable( $mod ) ) {
							require_once $mod;
						}
					}
					$settings[ $key ] = class_exists( \AlpineJsEnqueue\Module::class )
						? \AlpineJsEnqueue\Module::sanitizeInitCode( $value )
						: sanitize_textarea_field( $value );
					continue;
				}
				if ( $module_id === 'limit-login-attempts' ) {
					if ( $key === 'never_block_ips' ) {
						if ( ! class_exists( 'LimitLoginAttempts\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
							$mod_file = WP_POWERSUITE_MODULES_DIR . 'limit-login-attempts' . DIRECTORY_SEPARATOR . 'module.php';
							if ( file_exists( $mod_file ) ) {
								require_once $mod_file;
							}
						}
						$settings[ $key ] = class_exists( 'LimitLoginAttempts\\Module' )
							? \LimitLoginAttempts\Module::normalizeAllowlistIps( $value )
							: [];
						continue;
					}
					if ( $key === 'ip_header' ) {
						if ( ! class_exists( 'LimitLoginAttempts\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
							$mod_file = WP_POWERSUITE_MODULES_DIR . 'limit-login-attempts' . DIRECTORY_SEPARATOR . 'module.php';
							if ( file_exists( $mod_file ) ) {
								require_once $mod_file;
							}
						}
						$settings[ $key ] = class_exists( 'LimitLoginAttempts\\Module' )
							? \LimitLoginAttempts\Module::normalizeIpHeader( $value )
							: '';
						continue;
					}
					if ( in_array( $key, [ 'lockout_minutes', 'lockout_minutes_escalated', 'attempt_window_minutes' ], true ) ) {
						$settings[ $key ] = max( 5, min( 1440, (int) $value ) );
						continue;
					}
					if ( $key === 'log_retention_days' ) {
						$settings[ $key ] = max( 7, min( 365, (int) $value ) );
						continue;
					}
					if ( $key === 'max_attempts' ) {
						$settings[ $key ] = max( 1, min( 20, (int) $value ) );
						continue;
					}
					if ( $key === 'escalation_threshold' ) {
						$settings[ $key ] = max( 1, min( 10, (int) $value ) );
						continue;
					}
					if ( $key === 'lockout_mode' ) {
						$settings[ $key ] = ( is_string( $value ) && $value === 'ip_username' ) ? 'ip_username' : 'ip';
						continue;
					}
					if ( in_array( $key, [ 'geo_lookup_enabled', 'generic_error_messages' ], true ) ) {
						$settings[ $key ] = ! empty( $value ) ? 1 : 0;
						continue;
					}
				}
				// email-smtp: encrypt secrets, preserve if masked.
				if ( $module_id === 'email-smtp' ) {
					if ( ! class_exists( 'EmailSmtp\\Includes\\CredentialManager', false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
						$mod_file = WP_POWERSUITE_MODULES_DIR . 'email-smtp' . DIRECTORY_SEPARATOR . 'module.php';
						if ( file_exists( $mod_file ) ) {
							require_once $mod_file;
						}
					}
					$secret_keys = class_exists( 'EmailSmtp\\Includes\\CredentialManager' )
						? \EmailSmtp\Includes\CredentialManager::secret_keys()
						: [ 'smtp_password' ];
					if ( in_array( $key, $secret_keys, true ) ) {
						$mask = class_exists( 'EmailSmtp\\Module' ) ? \EmailSmtp\Module::getPasswordMask() : '••••••••••••';
						if ( $value === $mask || ( is_string( $value ) && trim( $value ) === '' ) ) {
							$existing         = $settings_repo->all( [] );
							$settings[ $key ] = is_array( $existing ) && isset( $existing[ $key ] ) ? $existing[ $key ] : '';
						} else {
							$settings[ $key ] = class_exists( 'EmailSmtp\\Module' ) ? \EmailSmtp\Module::encryptPassword( (string) $value ) : '';
						}
						continue;
					}
				}
				// google-recaptcha / cloudflare-turnstile: encrypt secret_key, preserve if unchanged.
				// Mask allows the UI to show a placeholder without exposing the stored secret; submitting the mask
				// (or an empty value) keeps the existing encrypted value.
				if ( $key === 'secret_key' && in_array( $module_id, [ 'google-recaptcha', 'cloudflare-turnstile' ], true ) ) {
					$module_class = $module_id === 'google-recaptcha' ? 'GoogleRecaptcha\\Module' : 'CloudflareTurnstile\\Module';
					$module_dir   = $module_id === 'google-recaptcha' ? 'google-recaptcha' : 'cloudflare-turnstile';
					if ( ! class_exists( $module_class ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
						$mod_file = WP_POWERSUITE_MODULES_DIR . $module_dir . DIRECTORY_SEPARATOR . 'module.php';
						if ( file_exists( $mod_file ) ) {
							require_once $mod_file;
						}
					}
					$mask = class_exists( $module_class ) && method_exists( $module_class, 'getSecretMask' )
						? call_user_func( [ $module_class, 'getSecretMask' ] )
						: '••••••••••••';
					if ( $value === $mask || ( is_string( $value ) && trim( $value ) === '' ) ) {
						$existing = $settings_repo->all( [] );
						$settings[ $key ] = is_array( $existing ) && isset( $existing['secret_key'] ) ? $existing['secret_key'] : '';
					} else {
						$plain = sanitize_text_field( (string) $value );
						$settings[ $key ] = class_exists( $module_class ) && method_exists( $module_class, 'encryptSecret' )
							? call_user_func( [ $module_class, 'encryptSecret' ], $plain )
							: $plain;
					}
					continue;
				}
				if ( $module_id === 'cloudflare-turnstile' && $key === 'failsafe_recaptcha_secret_key' ) {
					if ( ! class_exists( 'CloudflareTurnstile\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
						$cf_turnstile_file = WP_POWERSUITE_MODULES_DIR . 'cloudflare-turnstile' . DIRECTORY_SEPARATOR . 'module.php';
						if ( file_exists( $cf_turnstile_file ) ) {
							require_once $cf_turnstile_file;
						}
					}
					$mask = class_exists( 'CloudflareTurnstile\\Module' ) && method_exists( 'CloudflareTurnstile\\Module', 'getSecretMask' )
						? \CloudflareTurnstile\Module::getSecretMask()
						: '••••••••••••';
					if ( $value === $mask || ( is_string( $value ) && trim( $value ) === '' ) ) {
						$existing = $settings_repo->all( [] );
						$settings[ $key ] = is_array( $existing ) && isset( $existing['failsafe_recaptcha_secret_key'] )
							? $existing['failsafe_recaptcha_secret_key']
							: '';
					} else {
						$plain            = sanitize_text_field( (string) $value );
						$settings[ $key ] = class_exists( 'CloudflareTurnstile\\Module' ) && method_exists( 'CloudflareTurnstile\\Module', 'encryptSecret' )
							? \CloudflareTurnstile\Module::encryptSecret( $plain )
							: $plain;
					}
					continue;
				}
				// custom-wp-emails: preserve newlines in message fields, sanitize email fields
				if ( $module_id === 'custom-wp-emails' ) {
					if ( str_ends_with( $key, '_message' ) ) {
						$settings[ $key ] = sanitize_textarea_field( $value );
						continue;
					}
					if ( in_array( $key, [ 'from_email', 'reply_to_email' ], true ) ) {
						$settings[ $key ] = sanitize_email( $value );
						continue;
					}
					if ( in_array( $key, [ 'html_format', 'disable_admin_registration' ], true ) ) {
						$settings[ $key ] = ! empty( $value ) && $value !== '0' ? true : false;
						continue;
					}
				}
				if ( $module_id === 'alphabet-avatars' ) {
					if ( $key === 'letter_source' ) {
						$settings[ $key ] = in_array( $value, [ 'first_last', 'username', 'display_name', 'full_name' ], true ) ? $value : 'first_last';
						continue;
					}
					if ( $key === 'bg_mode' ) {
						$settings[ $key ] = in_array( $value, [ 'random', 'custom' ], true ) ? $value : 'random';
						continue;
					}
					if ( $key === 'custom_color' ) {
						$hex = ltrim( (string) $value, '#' );
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#3498db';
						}
						continue;
					}
					if ( $key === 'shape' ) {
						$settings[ $key ] = in_array( $value, [ 'circle', 'rounded', 'square' ], true ) ? $value : 'circle';
						continue;
					}
					if ( $key === 'size' ) {
						$settings[ $key ] = in_array( $value, [ 'small', 'regular' ], true ) ? $value : 'regular';
						continue;
					}
				}
				if ( $module_id === 'custom-login-logo' && $key === 'logo_source' ) {
					$settings[ $key ] = in_array( $value, [ 'upload', 'site_icon' ], true ) ? $value : 'upload';
					continue;
				}
				if ( $module_id === 'custom-login-logo' && $key === 'logo_url' ) {
					$url = esc_url_raw( $value );
					$allowed = array_filter( [ untrailingslashit( home_url() ), untrailingslashit( site_url() ), untrailingslashit( content_url() ), untrailingslashit( ( wp_upload_dir() )['baseurl'] ?? '' ) ] );
					$valid = false;
					foreach ( $allowed as $base ) {
						if ( str_starts_with( $url, $base . '/' ) || $url === $base ) {
							$valid = true;
							break;
						}
					}
					$settings[ $key ] = $valid ? $url : '';
					continue;
				}
				if ( $module_id === 'custom-login-logo-link' && $key === 'logo_link_url' ) {
					$url = esc_url_raw( trim( (string) $value ) );
					$settings[ $key ] = $url !== '' ? $url : home_url( '/' );
					continue;
				}
				if ( $module_id === 'custom-login-error-messages' && $key === 'generic_error_message' ) {
					$plain = sanitize_text_field( (string) $value );
					$settings[ $key ] = function_exists( 'mb_substr' )
						? mb_substr( $plain, 0, 500 )
						: substr( $plain, 0, 500 );
					continue;
				}
				if ( $module_id === 'login-id-type' && $key === 'login_id_type' ) {
					$allowed = [ 'username_email', 'username_only', 'email_only' ];
					$settings[ $key ] = in_array( $value, $allowed, true ) ? $value : 'username_email';
					continue;
				}
				if ( $module_id === 'custom-login-logo' && $key === 'logo_width' ) {
					$settings[ $key ] = min( 500, max( 50, (int) $value ) );
					continue;
				}
				if ( $module_id === 'custom-login-logo' && $key === 'logo_height' ) {
					$settings[ $key ] = min( 200, max( 30, (int) $value ) );
					continue;
				}
				if ( $module_id === 'custom-login-logo' && $key === 'logo_size_mode' ) {
					$settings[ $key ] = in_array( $value, [ 'fixed', 'contain', 'cover' ], true ) ? $value : 'contain';
					continue;
				}
				if ( $module_id === 'coming-soon-maintenance' ) {
					if ( $key === 'mode' ) {
						$settings[ $key ] = ( is_string( $value ) && $value === 'maintenance' ) ? 'maintenance' : 'coming_soon';
						continue;
					}
					if ( $key === 'bg_video' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) );
						continue;
					}
					if ( in_array( $key, [ 'bypass_token', 'fetch_secret' ], true ) ) {
						continue;
					}
					if ( $key === 'subscribe_consent_text' ) {
						$settings[ $key ] = sanitize_text_field( (string) $value );
						continue;
					}
					if ( $key === 'exclude_woocommerce' ) {
						$settings[ $key ] = ! empty( $value ) ? 1 : 0;
						continue;
					}
					if ( $key === 'page_preset' ) {
						$settings[ $key ] = sanitize_key( (string) $value );
						continue;
					}
					if ( $key === 'bg_image_url' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) );
						continue;
					}
					if ( $key === 'bg_type' ) {
						$allowed = [ 'color', 'gradient', 'image', 'video', 'patterns', 'lines', 'stripes', 'curves' ];
						$type    = is_string( $value ) ? sanitize_key( $value ) : 'color';
						if ( in_array( $type, [ 'lines', 'stripes', 'curves' ], true ) ) {
							$settings['bg_type']    = 'patterns';
							$settings['bg_pattern'] = $type;
						} else {
							$settings[ $key ] = in_array( $type, $allowed, true ) ? $type : 'color';
						}
						continue;
					}
					if ( $key === 'bg_pattern' ) {
						$settings[ $key ] = in_array( $value, [ 'lines', 'stripes', 'curves' ], true ) ? $value : 'lines';
						continue;
					}
					if ( $key === 'use_existing_page' ) {
						$settings[ $key ] = ! empty( $value ) ? 1 : 0;
						continue;
					}
					if ( $key === 'existing_page_url' ) {
						$url = esc_url_raw( trim( (string) $value ) );
						$home = untrailingslashit( home_url() );
						$site = untrailingslashit( site_url() );
						$settings[ $key ] = ( $url && ( str_starts_with( $url, $home . '/' ) || $url === $home || str_starts_with( $url, $site . '/' ) || $url === $site ) ) ? $url : '';
						continue;
					}
					if ( $key === 'social_links' && is_string( $value ) ) {
						$decoded = json_decode( wp_unslash( $value ), true );
						$arr    = is_array( $decoded ) ? $decoded : [];
						$sanitized = [];
						$allowed_icons = [ 'facebook', 'twitter', 'instagram', 'linkedin', 'youtube', 'pinterest', 'tiktok', 'github', 'rss', 'link', 'mastodon', 'threads' ];
						foreach ( $arr as $item ) {
							if ( ! is_array( $item ) ) {
								continue;
							}
							$url = isset( $item['url'] ) ? esc_url_raw( trim( (string) $item['url'] ) ) : '';
							if ( $url === '' ) {
								continue;
							}
							$icon = isset( $item['icon'] ) ? sanitize_key( (string) $item['icon'] ) : 'link';
							if ( ! in_array( $icon, $allowed_icons, true ) ) {
								$icon = 'link';
							}
							$sanitized[] = [ 'icon' => $icon, 'url' => $url ];
						}
						$settings[ $key ] = $sanitized;
						continue;
					}
					if ( in_array( $key, [ 'logo_id', 'bg_image_id' ], true ) ) {
						$settings[ $key ] = max( 0, (int) $value );
						continue;
					}
					if ( $key === 'logo_url' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) );
						continue;
					}
					if ( $key === 'logo_source' ) {
						$settings[ $key ] = in_array( $value, [ 'upload', 'site_icon', 'url', 'theme' ], true ) ? $value : 'upload';
						continue;
					}
					if ( in_array( $key, [ 'content_align', 'logo_align' ], true ) ) {
						$settings[ $key ] = in_array( $value, [ 'left', 'center', 'right' ], true ) ? $value : 'center';
						continue;
					}
					if ( $key === 'container_max_width' ) {
						$settings[ $key ] = max( 320, min( 1400, (int) $value ) );
						continue;
					}
					if ( $key === 'logo_max_width' ) {
						$settings[ $key ] = max( 40, min( 600, (int) $value ) );
						continue;
					}
					if ( in_array( $key, [ 'logo_width_desktop', 'logo_width_tablet', 'logo_width_mobile' ], true ) ) {
						if ( $value === '' || $value === null ) {
							$settings[ $key ] = '';
						} else {
							$settings[ $key ] = max( 40, min( 600, (int) $value ) );
						}
						continue;
					}
					if ( $key === 'logo_bg_enabled' ) {
						$settings[ $key ] = ! empty( $value ) ? 1 : 0;
						continue;
					}
					if ( in_array( $key, [ 'logo_bg_padding', 'logo_border_radius', 'countdown_border_radius' ], true ) ) {
						$settings[ $key ] = max( 0, min( 100, (int) $value ) );
						continue;
					}
					if ( $key === 'bg_image_position' ) {
						$allowed = [ 'center center', 'top center', 'bottom center', 'center left', 'center right', 'top left', 'top right', 'bottom left', 'bottom right' ];
						$settings[ $key ] = in_array( $value, $allowed, true ) ? $value : 'center center';
						continue;
					}
					if ( $key === 'bg_image_size' ) {
						$settings[ $key ] = in_array( $value, [ 'cover', 'contain', 'auto' ], true ) ? $value : 'cover';
						continue;
					}
					if ( $key === 'bg_image_repeat' ) {
						$settings[ $key ] = in_array( $value, [ 'no-repeat', 'repeat', 'repeat-x', 'repeat-y' ], true ) ? $value : 'no-repeat';
						continue;
					}
					if ( in_array(
						$key,
						[
							'text_color',
							'heading_color',
							'description_color',
							'logo_bg_color',
							'countdown_bg_color',
							'countdown_text_color',
							'subscribe_btn_bg',
							'subscribe_btn_text',
							'subscribe_input_bg',
							'subscribe_input_text',
							'subscribe_input_border',
							'social_icon_bg',
							'social_icon_color',
						],
						true
					) ) {
						$raw = trim( (string) $value );
						if ( $raw === '' ) {
							$settings[ $key ] = '';
							continue;
						}
						if ( preg_match( '/^rgba?\([^)]+\)$/i', $raw ) ) {
							$settings[ $key ] = preg_replace( '/[^a-zA-Z0-9(),.%#\s-]/', '', $raw );
							continue;
						}
						$settings[ $key ] = sanitize_hex_color( $raw ) ?: '';
						continue;
					}
					if ( in_array( $key, [ 'heading', 'description' ], true ) ) {
						$value = preg_replace( '/<span[^>]*data-mce-type="bookmark"[^>]*>[\s\S]*?<\/span>/i', '', (string) $value );
						$settings[ $key ] = wp_kses_post( $value );
						continue;
					}
					if ( in_array( $key, [ 'custom_head', 'custom_css', 'exclude_urls' ], true ) ) {
						$settings[ $key ] = sanitize_textarea_field( $value );
						continue;
					}
					if ( $key === 'allowed_user_ids' ) {
						$lines = array_filter( array_map( 'trim', explode( "\n", (string) $value ) ) );
						$ids   = [];
						foreach ( $lines as $line ) {
							$id = (int) $line;
							if ( $id > 0 ) {
								$ids[] = $id;
							}
						}
						$settings[ $key ] = array_unique( $ids );
						continue;
					}
					if ( $key === 'bg_color' ) {
						$settings[ $key ] = sanitize_hex_color( $value ) ?: '#2563eb';
						continue;
					}
					if ( $key === 'launch_date' ) {
						$settings[ $key ] = sanitize_text_field( $value );
						continue;
					}
				}
				if ( $module_id === 'admin-logo' && in_array( $key, [ 'admin_bar_logo_url', 'admin_menu_logo_url' ], true ) ) {
					$url = esc_url_raw( $value );
					$settings[ $key ] = ( $url !== '' && filter_var( $url, FILTER_VALIDATE_URL ) ) ? $url : '';
					continue;
				}
				if ( $module_id === 'admin-logo' && $key === 'logo_location' ) {
					$settings[ $key ] = in_array( $value, [ 'admin_bar', 'admin_menu', 'admin_bar_and_menu' ], true ) ? $value : 'admin_bar_and_menu';
					continue;
				}
				if ( $module_id === 'admin-logo' && $key === 'admin_bar_logo_size' ) {
					$settings[ $key ] = $value === '' || $value === '0' ? '' : (string) min( 30, max( 1, absint( $value ) ) );
					continue;
				}
				if ( $module_id === 'admin-logo' && $key === 'admin_menu_logo_width' ) {
					$settings[ $key ] = $value === '' || $value === '0' ? '' : (string) min( 200, max( 1, absint( $value ) ) );
					continue;
				}
				if ( $module_id === 'admin-logo' && $key === 'admin_menu_link_custom' ) {
					$settings[ $key ] = esc_url_raw( $value );
					continue;
				}
				if ( $module_id === 'admin-logo' && $key === 'admin_menu_link' ) {
					$settings[ $key ] = in_array( $value, [ 'home', 'dashboard', 'custom', 'none' ], true ) ? $value : 'home';
					continue;
				}
				if ( $module_id === 'admin-logo' && $key === 'admin_menu_link_target' ) {
					$settings[ $key ] = in_array( $value, [ 'same', 'blank' ], true ) ? $value : 'blank';
					continue;
				}
				if ( $module_id === 'ai-alt-text-generator' ) {
					continue;
				}
				if ( $module_id === 'basic-forms' ) {
					continue;
				}
				if ( $module_id === 'custom-image-sizes' && $key === 'custom_sizes' ) {
					$decoded = is_string( $value ) ? json_decode( $value, true ) : ( is_array( $value ) ? $value : null );
					$reserved = [ 'thumbnail', 'medium', 'medium_large', 'large' ];
					$max_dim  = 4096;
					$max_sizes = 20;
					if ( is_array( $decoded ) ) {
						$sanitized_sizes = [];
						$seen_names      = [];
						foreach ( array_slice( $decoded, 0, $max_sizes ) as $s ) {
							if ( ! is_array( $s ) || empty( $s['name'] ) ) {
								continue;
							}
							$name = substr( preg_replace( '/[^a-z0-9_]/', '', strtolower( (string) ( $s['name'] ?? '' ) ) ), 0, 50 );
							if ( $name === '' || in_array( $name, $reserved, true ) || isset( $seen_names[ $name ] ) ) {
								continue;
							}
							$seen_names[ $name ] = true;
							$width               = max( 0, min( $max_dim, (int) ( $s['width'] ?? 0 ) ) );
							$height              = max( 0, min( $max_dim, (int) ( $s['height'] ?? 0 ) ) );
							if ( $width <= 0 && $height <= 0 ) {
								continue;
							}
							$sanitized_sizes[] = [
								'name'   => $name,
								'width'  => $width,
								'height' => $height,
								'crop'   => ! empty( $s['crop'] ),
							];
						}
						$settings[ $key ] = $sanitized_sizes;
					} else {
						$settings[ $key ] = [];
					}
					continue;
				}
				if ( $module_id === 'simple-captcha' && $key === 'text_questions' ) {
					$decoded = is_string( $value ) ? json_decode( $value, true ) : ( is_array( $value ) ? $value : null );
					if ( is_array( $decoded ) ) {
						$sanitized = [];
						foreach ( $decoded as $q ) {
							if ( ! is_array( $q ) ) {
								continue;
							}
							$question = trim( (string) ( $q['question'] ?? '' ) );
							$answer   = trim( (string) ( $q['answer'] ?? '' ) );
							if ( $question !== '' && $answer !== '' ) {
								$sanitized[] = [
									'question' => sanitize_text_field( $question ),
									'answer'   => sanitize_text_field( $answer ),
								];
							}
						}
						$settings[ $key ] = $sanitized;
					} else {
						$settings[ $key ] = [];
					}
					continue;
				}
				if ( $module_id === 'login-page-customizor' ) {
					if ( $key === 'logo_source' ) {
						$settings[ $key ] = in_array( $value, [ 'upload', 'site_icon', 'url' ], true ) ? $value : 'upload';
						continue;
					}
					if ( $key === 'logo_external_url' || $key === 'logo_url' || $key === 'logo_link' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) ) ?: '';
						continue;
					}
					if ( in_array( $key, [ 'logo_width_desktop', 'logo_width_tablet', 'logo_width_mobile' ], true ) ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 50 && $num <= 500 ) ? (string) $num : '';
						}
						continue;
					}
					if ( in_array( $key, [ 'hide_remember_me', 'hide_language_switcher', 'hide_back_link', 'hide_lost_password', 'hide_logo', 'form_customization_enabled' ], true ) ) {
						$settings[ $key ] = ( ! empty( $value ) && $value !== '0' ) ? '1' : '0';
						continue;
					}
					if ( $key === 'custom_css' ) {
						$css = is_string( $value ) ? $value : '';
						$css = preg_replace( '/<script\b[^>]*>(.*?)<\/script>/is', '', $css );
						$css = wp_strip_all_tags( $css, true );
						$css = preg_replace( '/javascript\s*:/i', '', $css );
						$css = preg_replace( '/expression\s*\(/i', '', $css );
						$css = preg_replace( '/@import/i', '', $css );
						$css = preg_replace( '/behavior\s*:/i', '', $css );
						$settings[ $key ] = $css;
						continue;
					}
					if ( $key === 'logo_alignment' ) {
						$settings[ $key ] = in_array( $value, [ 'left', 'center', 'right' ], true ) ? $value : 'center';
						continue;
					}
					if ( $key === 'logo_margin_top' || $key === 'logo_margin_bottom' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 100 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'template_type' ) {
						$settings[ $key ] = in_array( $value, [ 'classic-centered', 'split-layout' ], true ) ? $value : 'classic-centered';
						continue;
					}
					if ( $key === 'split_ratio' ) {
						$settings[ $key ] = in_array( (string) $value, [ '25', '30', '35', '40', '45', '50' ], true ) ? (string) $value : '50';
						continue;
					}
					if ( $key === 'split_column_order' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'form-first', 'form-second' ], true ) ? (string) $value : 'form-first';
						continue;
					}
					if ( $key === 'form_alignment' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'left', 'center', 'right' ], true ) ? (string) $value : 'center';
						continue;
					}
					if ( $key === 'classic_bg_type' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'color', 'gradient', 'image', 'video' ], true ) ? (string) $value : '';
						continue;
					}
					if ( $key === 'classic_bg_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#f0f0f1';
						}
						continue;
					}
					if ( $key === 'classic_bg_image' || $key === 'classic_bg_video' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) ) ?: '';
						continue;
					}
					if ( $key === 'classic_bg_video_overlay_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#000000';
						}
						continue;
					}
					if ( $key === 'classic_bg_video_overlay_opacity' ) {
						$v = trim( (string) $value );
						$settings[ $key ] = $v !== '' && is_numeric( $v ) ? (string) max( 0, min( 100, (int) $v ) ) : '0';
						continue;
					}
					if ( $key === 'classic_bg_image_overlay_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#000000';
						}
						continue;
					}
					if ( $key === 'classic_bg_image_overlay_opacity' ) {
						$v = trim( (string) $value );
						$settings[ $key ] = $v !== '' && is_numeric( $v ) ? (string) max( 0, min( 100, (int) $v ) ) : '0';
						continue;
					}
					if ( $key === 'classic_bg_image_position' ) {
						$valid = [ 'left top', 'center top', 'right top', 'left center', 'center center', 'right center', 'left bottom', 'center bottom', 'right bottom' ];
						$settings[ $key ] = in_array( (string) $value, $valid, true ) ? (string) $value : 'center center';
						continue;
					}
					if ( $key === 'classic_bg_image_size' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'auto', 'cover', 'contain' ], true ) ? (string) $value : 'cover';
						continue;
					}
					if ( $key === 'classic_bg_image_repeat' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'repeat', 'no-repeat', 'repeat-x', 'repeat-y' ], true ) ? (string) $value : 'no-repeat';
						continue;
					}
					if ( $key === 'classic_bg_gradient' ) {
						$settings[ $key ] = $this->sanitize_login_page_gradient( $value );
						continue;
					}
					if ( $key === 'classic_bg_gradient_type' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'linear', 'radial' ], true ) ? (string) $value : 'linear';
						continue;
					}
					if ( $key === 'classic_bg_gradient_angle' ) {
						$settings[ $key ] = max( 0, min( 360, (int) $value ) );
						continue;
					}
					if ( $key === 'classic_bg_gradient_color1' || $key === 'classic_bg_gradient_color2' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = ( $key === 'classic_bg_gradient_color1' ) ? '#f0f0f1' : '#e0e0e0';
						}
						continue;
					}
					if ( $key === 'classic_bg_gradient_pos1' || $key === 'classic_bg_gradient_pos2' ) {
						$settings[ $key ] = max( 0, min( 100, (int) $value ) );
						continue;
					}
					// Split layout: Column with Form
					if ( $key === 'split_form_form_alignment' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'left', 'center', 'right' ], true ) ? (string) $value : 'center';
						continue;
					}
					if ( $key === 'split_form_bg_type' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'color', 'gradient', 'image', 'video' ], true ) ? (string) $value : '';
						continue;
					}
					if ( $key === 'split_form_bg_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#f0f0f1';
						}
						continue;
					}
					if ( $key === 'split_form_bg_image' || $key === 'split_form_bg_video' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) ) ?: '';
						continue;
					}
					if ( strpos( $key, 'split_form_bg_' ) === 0 && $key !== 'split_form_bg_type' && $key !== 'split_form_bg_color' && $key !== 'split_form_bg_image' && $key !== 'split_form_bg_video' && $key !== 'split_form_bg_gradient' ) {
						$key_end = substr( $key, -6 );
						$key_end_long = substr( $key, -13 );
						if ( $key_end === '_color' || $key_end_long === 'overlay_color' ) {
							$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
							$settings[ $key ] = ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) ? '#' . $hex : ( strpos( $key, 'overlay' ) !== false ? '#000000' : '#f0f0f1' );
						} elseif ( substr( $key, -8 ) === '_opacity' ) {
							$v = trim( (string) $value );
							$settings[ $key ] = $v !== '' && is_numeric( $v ) ? (string) max( 0, min( 100, (int) $v ) ) : '0';
						} elseif ( $key === 'split_form_bg_image_position' ) {
							$valid = [ 'left top', 'center top', 'right top', 'left center', 'center center', 'right center', 'left bottom', 'center bottom', 'right bottom' ];
							$settings[ $key ] = in_array( (string) $value, $valid, true ) ? (string) $value : 'center center';
						} elseif ( $key === 'split_form_bg_image_size' ) {
							$settings[ $key ] = in_array( (string) $value, [ 'auto', 'cover', 'contain' ], true ) ? (string) $value : 'cover';
						} elseif ( $key === 'split_form_bg_image_repeat' ) {
							$settings[ $key ] = in_array( (string) $value, [ 'repeat', 'no-repeat', 'repeat-x', 'repeat-y' ], true ) ? (string) $value : 'no-repeat';
						} elseif ( strpos( $key, 'gradient' ) !== false ) {
							if ( substr( $key, -4 ) === 'type' ) {
								$settings[ $key ] = in_array( (string) $value, [ 'linear', 'radial' ], true ) ? (string) $value : 'linear';
							} elseif ( substr( $key, -5 ) === 'angle' ) {
								$settings[ $key ] = max( 0, min( 360, (int) $value ) );
							} elseif ( substr( $key, -4 ) === 'pos1' || substr( $key, -4 ) === 'pos2' ) {
								$settings[ $key ] = max( 0, min( 100, (int) $value ) );
							} else {
								$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
								$settings[ $key ] = ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) ? '#' . $hex : ( strpos( $key, 'color1' ) !== false ? '#f0f0f1' : '#e0e0e0' );
							}
						}
						continue;
					}
					if ( $key === 'split_form_bg_gradient' ) {
						$settings[ $key ] = $this->sanitize_login_page_gradient( $value );
						continue;
					}
					// Split layout: Column without Form
					if ( $key === 'split_empty_bg_type' ) {
						$settings[ $key ] = in_array( (string) $value, [ 'color', 'gradient', 'image', 'video' ], true ) ? (string) $value : '';
						continue;
					}
					if ( $key === 'split_empty_bg_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#f0f0f1';
						}
						continue;
					}
					if ( $key === 'split_empty_bg_image' || $key === 'split_empty_bg_video' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) ) ?: '';
						continue;
					}
					if ( strpos( $key, 'split_empty_bg_' ) === 0 && $key !== 'split_empty_bg_type' && $key !== 'split_empty_bg_color' && $key !== 'split_empty_bg_image' && $key !== 'split_empty_bg_video' && $key !== 'split_empty_bg_gradient' ) {
						$key_end = substr( $key, -6 );
						$key_end_long = substr( $key, -13 );
						if ( $key_end === '_color' || $key_end_long === 'overlay_color' ) {
							$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
							$settings[ $key ] = ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) ? '#' . $hex : ( strpos( $key, 'overlay' ) !== false ? '#000000' : '#f0f0f1' );
						} elseif ( substr( $key, -8 ) === '_opacity' ) {
							$v = trim( (string) $value );
							$settings[ $key ] = $v !== '' && is_numeric( $v ) ? (string) max( 0, min( 100, (int) $v ) ) : '0';
						} elseif ( $key === 'split_empty_bg_image_position' ) {
							$valid = [ 'left top', 'center top', 'right top', 'left center', 'center center', 'right center', 'left bottom', 'center bottom', 'right bottom' ];
							$settings[ $key ] = in_array( (string) $value, $valid, true ) ? (string) $value : 'center center';
						} elseif ( $key === 'split_empty_bg_image_size' ) {
							$settings[ $key ] = in_array( (string) $value, [ 'auto', 'cover', 'contain' ], true ) ? (string) $value : 'cover';
						} elseif ( $key === 'split_empty_bg_image_repeat' ) {
							$settings[ $key ] = in_array( (string) $value, [ 'repeat', 'no-repeat', 'repeat-x', 'repeat-y' ], true ) ? (string) $value : 'no-repeat';
						} elseif ( strpos( $key, 'gradient' ) !== false ) {
							if ( substr( $key, -4 ) === 'type' ) {
								$settings[ $key ] = in_array( (string) $value, [ 'linear', 'radial' ], true ) ? (string) $value : 'linear';
							} elseif ( substr( $key, -5 ) === 'angle' ) {
								$settings[ $key ] = max( 0, min( 360, (int) $value ) );
							} elseif ( substr( $key, -4 ) === 'pos1' || substr( $key, -4 ) === 'pos2' ) {
								$settings[ $key ] = max( 0, min( 100, (int) $value ) );
							} else {
								$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
								$settings[ $key ] = ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) ? '#' . $hex : ( strpos( $key, 'color1' ) !== false ? '#f0f0f1' : '#e0e0e0' );
							}
						}
						continue;
					}
					if ( $key === 'split_empty_bg_gradient' ) {
						$settings[ $key ] = $this->sanitize_login_page_gradient( $value );
						continue;
					}
					if ( $key === 'split_empty_add_content' ) {
						$settings[ $key ] = ( ! empty( $value ) && $value !== '0' ) ? '1' : '0';
						continue;
					}
					if ( $key === 'split_empty_content' ) {
						$settings[ $key ] = wp_kses_post( $value );
						continue;
					}
					if ( $key === 'box_form_width' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $v;
							$settings[ $key ] = ( $num > 0 && $num <= 2000 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'box_form_width_unit' ) {
						$settings[ $key ] = in_array( $value, [ 'px', '%' ], true ) ? $value : 'px';
						continue;
					}
					if ( $key === 'form_width' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $v;
							$settings[ $key ] = ( $num > 0 && $num <= 2000 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'form_width_unit' ) {
						$settings[ $key ] = in_array( $value, [ 'px', '%' ], true ) ? $value : 'px';
						continue;
					}
					if ( $key === 'box_bg_type' ) {
						$settings[ $key ] = in_array( $value, [ 'color', 'image', 'gradient' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'box_bg_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#ffffff';
						}
						continue;
					}
					if ( $key === 'box_bg_image' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) ) ?: '';
						continue;
					}
					if ( $key === 'box_bg_gradient' ) {
						$settings[ $key ] = $this->sanitize_login_page_gradient( $value );
						continue;
					}
					if ( $key === 'box_bg_gradient_type' ) {
						$settings[ $key ] = in_array( $value, [ 'linear', 'radial' ], true ) ? $value : 'linear';
						continue;
					}
					if ( $key === 'box_bg_gradient_angle' ) {
						$settings[ $key ] = max( 0, min( 360, (int) $value ) );
						continue;
					}
					if ( $key === 'box_bg_gradient_color1' || $key === 'box_bg_gradient_color2' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = ( $key === 'box_bg_gradient_color1' ) ? '#ffffff' : '#e8e8e8';
						}
						continue;
					}
					if ( $key === 'box_bg_gradient_pos1' || $key === 'box_bg_gradient_pos2' ) {
						$settings[ $key ] = max( 0, min( 100, (int) $value ) );
						continue;
					}
					if ( $key === 'box_padding_desktop' || $key === 'box_padding_tablet' || $key === 'box_padding_mobile'
						|| preg_match( '/^box_padding_(top|right|bottom|left)(_(tablet|mobile))?$/', $key ) ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 200 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'box_border_type' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'solid', 'dashed', 'dotted' ], true ) ? $value : 'none';
						continue;
					}
					if ( $key === 'box_border_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#cccccc';
						}
						continue;
					}
					if ( $key === 'box_border_width' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '1';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 50 ) ? (string) $num : '1';
						}
						continue;
					}
					if ( $key === 'box_border_radius_desktop' || $key === 'box_border_radius_tablet' || $key === 'box_border_radius_mobile' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 200 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'box_shadow_x' || $key === 'box_shadow_y' ) {
						$settings[ $key ] = max( -50, min( 50, (int) $value ) );
						continue;
					}
					if ( $key === 'box_shadow_blur' || $key === 'box_shadow_spread' ) {
						$settings[ $key ] = max( $key === 'box_shadow_spread' ? -20 : 0, min( 50, (int) $value ) );
						continue;
					}
					if ( $key === 'box_shadow_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#000000';
						}
						continue;
					}
					if ( $key === 'box_shadow_inset' ) {
						$settings[ $key ] = ( $value === '1' || $value === 'true' || $value === true ) ? '1' : '0';
						continue;
					}
					if ( $key === 'form_shadow_x' || $key === 'form_shadow_y' ) {
						$settings[ $key ] = max( -50, min( 50, (int) $value ) );
						continue;
					}
					if ( $key === 'form_shadow_blur' ) {
						$settings[ $key ] = max( 0, min( 50, (int) $value ) );
						continue;
					}
					if ( $key === 'form_shadow_spread' ) {
						$settings[ $key ] = max( -20, min( 20, (int) $value ) );
						continue;
					}
					if ( $key === 'form_shadow_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#000000';
						}
						continue;
					}
					if ( $key === 'form_shadow_inset' ) {
						$settings[ $key ] = ( $value === '1' || $value === 'true' || $value === true ) ? '1' : '0';
						continue;
					}
					// Form tab (form section) settings.
					if ( $key === 'form_padding_desktop' || $key === 'form_padding_tablet' || $key === 'form_padding_mobile'
						|| preg_match( '/^form_padding_(top|right|bottom|left)(_(tablet|mobile))?$/', $key ) ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 200 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'form_border_type' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'solid', 'dashed', 'dotted' ], true ) ? $value : 'none';
						continue;
					}
					if ( $key === 'form_border_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#cccccc';
						}
						continue;
					}
					if ( $key === 'form_border_width' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '1';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 50 ) ? (string) $num : '1';
						}
						continue;
					}
					if ( $key === 'form_border_radius_desktop' || $key === 'form_border_radius_tablet' || $key === 'form_border_radius_mobile' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 200 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'form_bg_type' ) {
						$settings[ $key ] = in_array( $value, [ 'color', 'image', 'gradient' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'form_bg_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#ffffff';
						}
						continue;
					}
					if ( $key === 'form_bg_image' ) {
						$settings[ $key ] = esc_url_raw( trim( (string) $value ) ) ?: '';
						continue;
					}
					if ( $key === 'form_bg_gradient' ) {
						$settings[ $key ] = $this->sanitize_login_page_gradient( $value );
						continue;
					}
					if ( $key === 'form_bg_gradient_type' ) {
						$settings[ $key ] = in_array( $value, [ 'linear', 'radial' ], true ) ? $value : 'linear';
						continue;
					}
					if ( $key === 'form_bg_gradient_angle' ) {
						$settings[ $key ] = max( 0, min( 360, (int) $value ) );
						continue;
					}
					if ( $key === 'form_bg_gradient_color1' || $key === 'form_bg_gradient_color2' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = ( $key === 'form_bg_gradient_color1' ) ? '#ffffff' : '#e8e8e8';
						}
						continue;
					}
					if ( $key === 'form_bg_gradient_pos1' || $key === 'form_bg_gradient_pos2' ) {
						$settings[ $key ] = max( 0, min( 100, (int) $value ) );
						continue;
					}
					// Fields tab (input typography)
					if ( $key === 'field_font_family' ) {
						$settings[ $key ] = sanitize_text_field( $value );
						continue;
					}
					if ( $key === 'field_font_size' || preg_match( '/^field_font_size_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 10 && $num <= 72 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'field_font_weight' ) {
						$settings[ $key ] = in_array( $value, [ '300', '400', '500', '600', '700' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'field_line_height' || preg_match( '/^field_line_height_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$f = (float) $value;
							$settings[ $key ] = ( $f >= 0.5 && $f <= 3 ) ? (string) $f : '';
						}
						continue;
					}
					if ( $key === 'field_letter_spacing' || preg_match( '/^field_letter_spacing_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$settings[ $key ] = is_numeric( $value ) ? (string) (float) $value : '';
						}
						continue;
					}
					if ( $key === 'field_text_transform' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'lowercase', 'uppercase', 'capitalize' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'field_font_style' ) {
						$settings[ $key ] = in_array( $value, [ 'normal', 'italic', 'oblique' ], true ) ? $value : '';
						continue;
					}
					// Fields: Normal state
					if ( $key === 'field_normal_text_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '';
						}
						continue;
					}
					if ( $key === 'field_normal_border_type' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'solid', 'dashed', 'dotted' ], true ) ? $value : 'none';
						continue;
					}
					if ( $key === 'field_normal_border_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#cccccc';
						}
						continue;
					}
					if ( $key === 'field_normal_border_width' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '1';
						} else {
							$num = (int) $value;
							$settings[ $key ] = (string) max( 0, min( 50, $num ) );
						}
						continue;
					}
					if ( $key === 'field_normal_border_radius' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 50 ) ? (string) $num : '';
						}
						continue;
					}
					if ( $key === 'field_normal_shadow_x' || $key === 'field_normal_shadow_y' ) {
						$settings[ $key ] = max( -50, min( 50, (int) $value ) );
						continue;
					}
					if ( $key === 'field_normal_shadow_blur' ) {
						$settings[ $key ] = max( 0, min( 50, (int) $value ) );
						continue;
					}
					if ( $key === 'field_normal_shadow_spread' ) {
						$settings[ $key ] = max( -20, min( 20, (int) $value ) );
						continue;
					}
					if ( $key === 'field_normal_shadow_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#000000';
						}
						continue;
					}
					if ( $key === 'field_normal_shadow_inset' ) {
						$settings[ $key ] = ( ! empty( $value ) && $value !== '0' ) ? '1' : '0';
						continue;
					}
					// Fields: Focus state
					if ( $key === 'field_focus_text_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '';
						}
						continue;
					}
					if ( $key === 'field_focus_border_type' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'solid', 'dashed', 'dotted' ], true ) ? $value : 'none';
						continue;
					}
					if ( $key === 'field_focus_border_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#2271b1';
						}
						continue;
					}
					if ( $key === 'field_focus_border_width' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '1';
						} else {
							$num = (int) $value;
							$settings[ $key ] = (string) max( 0, min( 50, $num ) );
						}
						continue;
					}
					if ( $key === 'field_focus_border_radius' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 50 ) ? (string) $num : '';
						}
						continue;
					}
					// Fields & Button: padding (per-side, 0–200 or empty)
					$padding_keys = [
						'field_normal_padding_top', 'field_normal_padding_right', 'field_normal_padding_bottom', 'field_normal_padding_left',
						'field_focus_padding_top', 'field_focus_padding_right', 'field_focus_padding_bottom', 'field_focus_padding_left',
						'button_normal_padding_top', 'button_normal_padding_right', 'button_normal_padding_bottom', 'button_normal_padding_left',
					];
					if ( in_array( $key, $padding_keys, true ) ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 200 ) ? (string) $num : '';
						}
						continue;
					}
					// Button tab: typography
					if ( $key === 'button_font_family' ) {
						$settings[ $key ] = sanitize_text_field( $value );
						continue;
					}
					if ( preg_match( '/^button_font_size_(desktop|tablet|mobile)$/', $key ) || $key === 'button_font_size_desktop' ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && (int) $v >= 10 && (int) $v <= 72 ) ? (string) (int) $v : '';
						continue;
					}
					if ( $key === 'button_font_weight' ) {
						$settings[ $key ] = in_array( $value, [ '300', '400', '500', '600', '700' ], true ) ? $value : '';
						continue;
					}
					if ( preg_match( '/^button_line_height_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && is_numeric( $v ) && (float) $v >= 0.5 && (float) $v <= 3 ) ? (string) (float) $v : '';
						continue;
					}
					if ( preg_match( '/^button_letter_spacing_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && is_numeric( $v ) ) ? (string) (float) $v : '';
						continue;
					}
					if ( $key === 'button_text_transform' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'lowercase', 'uppercase', 'capitalize' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'button_font_style' ) {
						$settings[ $key ] = in_array( $value, [ 'normal', 'italic', 'oblique' ], true ) ? $value : '';
						continue;
					}
					// Button Normal: text color, background, border, shadow
					if ( $key === 'button_normal_text_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '';
						}
						continue;
					}
					if ( $key === 'button_normal_bg_type' ) {
						$settings[ $key ] = in_array( $value, [ 'color', 'gradient' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'button_normal_bg_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#2271b1';
						}
						continue;
					}
					if ( $key === 'button_normal_bg_gradient' ) {
						$settings[ $key ] = $this->sanitize_login_page_gradient( $value );
						continue;
					}
					if ( preg_match( '/^button_normal_bg_gradient_type$/', $key ) ) {
						$settings[ $key ] = in_array( $value, [ 'linear', 'radial' ], true ) ? $value : 'linear';
						continue;
					}
					if ( preg_match( '/^button_normal_bg_gradient_angle$/', $key ) ) {
						$settings[ $key ] = max( 0, min( 360, (int) $value ) );
						continue;
					}
					if ( preg_match( '/^button_normal_bg_gradient_color[12]$/', $key ) ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = strpos( $key, 'color1' ) !== false ? '#2271b1' : '#135e96';
						}
						continue;
					}
					if ( preg_match( '/^button_normal_bg_gradient_pos[12]$/', $key ) ) {
						$settings[ $key ] = max( 0, min( 100, (int) $value ) );
						continue;
					}
					if ( $key === 'button_normal_border_type' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'solid', 'dashed', 'dotted' ], true ) ? $value : 'none';
						continue;
					}
					if ( $key === 'button_normal_border_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#cccccc';
						}
						continue;
					}
					if ( $key === 'button_normal_border_width' ) {
						$num = (int) $value;
						$settings[ $key ] = ( $num >= 0 && $num <= 50 ) ? (string) $num : '1';
						continue;
					}
					if ( $key === 'button_normal_border_radius'
						|| $key === 'button_normal_border_radius_desktop'
						|| $key === 'button_normal_border_radius_tablet'
						|| $key === 'button_normal_border_radius_mobile' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 200 ) ? (string) $num : '';
						}
						continue;
					}
					if ( in_array( $key, [ 'button_normal_shadow_x', 'button_normal_shadow_y', 'button_normal_shadow_blur', 'button_normal_shadow_spread' ], true ) ) {
						$num = (int) $value;
						$max = in_array( $key, [ 'button_normal_shadow_blur', 'button_normal_shadow_spread' ], true ) ? ( $key === 'button_normal_shadow_spread' ? 20 : 50 ) : 50;
						$min = ( $key === 'button_normal_shadow_spread' ) ? -20 : ( in_array( $key, [ 'button_normal_shadow_x', 'button_normal_shadow_y' ], true ) ? -50 : 0 );
						$settings[ $key ] = max( $min, min( $max, $num ) );
						continue;
					}
					if ( $key === 'button_normal_shadow_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#000000';
						}
						continue;
					}
					if ( $key === 'button_normal_shadow_inset' ) {
						$settings[ $key ] = ( ! empty( $value ) && $value !== '0' ) ? '1' : '0';
						continue;
					}
					// Button Hover: text color, background, border, shadow
					if ( $key === 'button_hover_text_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '';
						}
						continue;
					}
					if ( $key === 'button_hover_bg_type' ) {
						$settings[ $key ] = in_array( $value, [ 'color', 'gradient' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'button_hover_bg_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#135e96';
						}
						continue;
					}
					if ( $key === 'button_hover_bg_gradient' ) {
						$settings[ $key ] = $this->sanitize_login_page_gradient( $value );
						continue;
					}
					if ( preg_match( '/^button_hover_bg_gradient_type$/', $key ) ) {
						$settings[ $key ] = in_array( $value, [ 'linear', 'radial' ], true ) ? $value : 'linear';
						continue;
					}
					if ( preg_match( '/^button_hover_bg_gradient_angle$/', $key ) ) {
						$settings[ $key ] = max( 0, min( 360, (int) $value ) );
						continue;
					}
					if ( preg_match( '/^button_hover_bg_gradient_color[12]$/', $key ) ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = strpos( $key, 'color1' ) !== false ? '#135e96' : '#2271b1';
						}
						continue;
					}
					if ( preg_match( '/^button_hover_bg_gradient_pos[12]$/', $key ) ) {
						$settings[ $key ] = max( 0, min( 100, (int) $value ) );
						continue;
					}
					if ( $key === 'button_hover_border_type' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'solid', 'dashed', 'dotted' ], true ) ? $value : 'none';
						continue;
					}
					if ( $key === 'button_hover_border_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#cccccc';
						}
						continue;
					}
					if ( $key === 'button_hover_border_width' ) {
						$num = (int) $value;
						$settings[ $key ] = ( $num >= 0 && $num <= 50 ) ? (string) $num : '1';
						continue;
					}
					if ( $key === 'button_hover_border_radius'
						|| $key === 'button_hover_border_radius_desktop'
						|| $key === 'button_hover_border_radius_tablet'
						|| $key === 'button_hover_border_radius_mobile' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 200 ) ? (string) $num : '';
						}
						continue;
					}
					if ( in_array( $key, [ 'button_hover_shadow_x', 'button_hover_shadow_y', 'button_hover_shadow_blur', 'button_hover_shadow_spread' ], true ) ) {
						$num = (int) $value;
						$max = in_array( $key, [ 'button_hover_shadow_blur', 'button_hover_shadow_spread' ], true ) ? ( $key === 'button_hover_shadow_spread' ? 20 : 50 ) : 50;
						$min = ( $key === 'button_hover_shadow_spread' ) ? -20 : ( in_array( $key, [ 'button_hover_shadow_x', 'button_hover_shadow_y' ], true ) ? -50 : 0 );
						$settings[ $key ] = max( $min, min( $max, $num ) );
						continue;
					}
					if ( $key === 'button_hover_shadow_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '#000000';
						}
						continue;
					}
					if ( $key === 'button_hover_shadow_inset' ) {
						$settings[ $key ] = ( ! empty( $value ) && $value !== '0' ) ? '1' : '0';
						continue;
					}
					// Footer tab: typography
					if ( $key === 'footer_font_family' ) {
						$settings[ $key ] = sanitize_text_field( $value );
						continue;
					}
					if ( preg_match( '/^footer_font_size_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && (int) $v >= 10 && (int) $v <= 72 ) ? (string) (int) $v : '';
						continue;
					}
					if ( $key === 'footer_font_weight' ) {
						$settings[ $key ] = in_array( $value, [ '300', '400', '500', '600', '700' ], true ) ? $value : '';
						continue;
					}
					if ( preg_match( '/^footer_line_height_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && is_numeric( $v ) && (float) $v >= 0.5 && (float) $v <= 3 ) ? (string) (float) $v : '';
						continue;
					}
					if ( preg_match( '/^footer_letter_spacing_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && is_numeric( $v ) ) ? (string) (float) $v : '';
						continue;
					}
					if ( $key === 'footer_text_transform' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'lowercase', 'uppercase', 'capitalize' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'footer_font_style' ) {
						$settings[ $key ] = in_array( $value, [ 'normal', 'italic', 'oblique' ], true ) ? $value : '';
						continue;
					}
					if ( in_array( $key, [ 'footer_normal_text_color', 'footer_hover_text_color' ], true ) ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = ( $key === 'footer_normal_text_color' ) ? '' : '';
						}
						continue;
					}
					// Label tab: typography
					if ( $key === 'label_font_family' ) {
						$settings[ $key ] = sanitize_text_field( $value );
						continue;
					}
					if ( preg_match( '/^label_font_size_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && (int) $v >= 10 && (int) $v <= 72 ) ? (string) (int) $v : '';
						continue;
					}
					if ( $key === 'label_font_weight' ) {
						$settings[ $key ] = in_array( $value, [ '300', '400', '500', '600', '700' ], true ) ? $value : '';
						continue;
					}
					if ( preg_match( '/^label_line_height_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && is_numeric( $v ) && (float) $v >= 0.5 && (float) $v <= 3 ) ? (string) (float) $v : '';
						continue;
					}
					if ( preg_match( '/^label_letter_spacing_(desktop|tablet|mobile)$/', $key ) ) {
						$v = trim( (string) $value );
						$settings[ $key ] = ( $v !== '' && is_numeric( $v ) ) ? (string) (float) $v : '';
						continue;
					}
					if ( $key === 'label_text_transform' ) {
						$settings[ $key ] = in_array( $value, [ 'none', 'lowercase', 'uppercase', 'capitalize' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'label_font_style' ) {
						$settings[ $key ] = in_array( $value, [ 'normal', 'italic', 'oblique' ], true ) ? $value : '';
						continue;
					}
					if ( $key === 'label_text_color' ) {
						$hex = is_string( $value ) ? ltrim( trim( $value ), '#' ) : '';
						if ( preg_match( '/^[a-fA-F0-9]{8}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) || preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) {
							$settings[ $key ] = '#' . $hex;
						} else {
							$settings[ $key ] = '';
						}
						continue;
					}
					if ( $key === 'label_spacing_bottom' ) {
						$v = trim( (string) $value );
						if ( $v === '' ) {
							$settings[ $key ] = '';
						} else {
							$num = (int) $value;
							$settings[ $key ] = ( $num >= 0 && $num <= 100 ) ? (string) $num : '';
						}
						continue;
					}
				}
				// Keys that end with [] or contain specific patterns should be split
				if ( strpos( $key, 'allowed_ips' ) !== false
					|| strpos( $key, 'blocked_ips' ) !== false
					|| strpos( $key, 'ip_addresses' ) !== false
					|| strpos( $key, 'blocked_usernames' ) !== false
					|| strpos( $key, 'filter_words' ) !== false
					|| strpos( $key, 'urls_to_warm' ) !== false
					|| strpos( $key, 'whitelist_plugin_slugs' ) !== false
					|| strpos( $key, 'whitelist_css_classes' ) !== false
					|| strpos( $key, 'whitelist_text_match' ) !== false
				) {
					// Split by newlines and filter empty values
					$lines = explode( "\n", $value );
					$filtered = array_filter( array_map( 'trim', $lines ), function( $v ) {
						return ! empty( $v );
					} );
					$settings[ $key ] = array_values( $filtered );
				} elseif ( $value === '1' || $value === 'true' ) {
					// Convert checkbox values to boolean
					$settings[ $key ] = true;
				} elseif ( $value === '0' || $value === 'false' ) {
					$settings[ $key ] = false;
				} else {
					// Regular text value
					$settings[ $key ] = sanitize_text_field( $value );
				}
			} else {
				// Other types (int, bool, etc.)
				$settings[ $key ] = $value;
			}
		}

		if ( $module_id === 'login-page-customizor' && isset( $settings['box_bg_gradient_type'] ) ) {
			$type  = ( isset( $settings['box_bg_gradient_type'] ) && $settings['box_bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
			$angle = isset( $settings['box_bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['box_bg_gradient_angle'] ) ) : 135;
			$c1    = isset( $settings['box_bg_gradient_color1'] ) ? (string) $settings['box_bg_gradient_color1'] : '#ffffff';
			$p1    = isset( $settings['box_bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['box_bg_gradient_pos1'] ) ) : 0;
			$c2    = isset( $settings['box_bg_gradient_color2'] ) ? (string) $settings['box_bg_gradient_color2'] : '#e8e8e8';
			$p2    = isset( $settings['box_bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['box_bg_gradient_pos2'] ) ) : 100;
			if ( $c1 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c1 ) ) {
				$c1 = '#ffffff';
			}
			if ( $c2 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c2 ) ) {
				$c2 = '#e8e8e8';
			}
			$settings['box_bg_gradient'] = wp_json_encode( [
				'type'  => $type,
				'angle' => $angle,
				'stops' => [
					[ 'color' => $c1, 'pos' => $p1 ],
					[ 'color' => $c2, 'pos' => $p2 ],
				],
			] );
			unset( $settings['box_bg_gradient_type'], $settings['box_bg_gradient_angle'], $settings['box_bg_gradient_color1'], $settings['box_bg_gradient_pos1'], $settings['box_bg_gradient_color2'], $settings['box_bg_gradient_pos2'] );
		}
		if ( $module_id === 'login-page-customizor' && isset( $settings['form_bg_gradient_type'] ) ) {
			$type  = ( isset( $settings['form_bg_gradient_type'] ) && $settings['form_bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
			$angle = isset( $settings['form_bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['form_bg_gradient_angle'] ) ) : 135;
			$c1    = isset( $settings['form_bg_gradient_color1'] ) ? (string) $settings['form_bg_gradient_color1'] : '#ffffff';
			$p1    = isset( $settings['form_bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['form_bg_gradient_pos1'] ) ) : 0;
			$c2    = isset( $settings['form_bg_gradient_color2'] ) ? (string) $settings['form_bg_gradient_color2'] : '#e8e8e8';
			$p2    = isset( $settings['form_bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['form_bg_gradient_pos2'] ) ) : 100;
			if ( $c1 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c1 ) ) {
				$c1 = '#ffffff';
			}
			if ( $c2 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c2 ) ) {
				$c2 = '#e8e8e8';
			}
			$settings['form_bg_gradient'] = wp_json_encode( [
				'type'  => $type,
				'angle' => $angle,
				'stops' => [
					[ 'color' => $c1, 'pos' => $p1 ],
					[ 'color' => $c2, 'pos' => $p2 ],
				],
			] );
			unset( $settings['form_bg_gradient_type'], $settings['form_bg_gradient_angle'], $settings['form_bg_gradient_color1'], $settings['form_bg_gradient_pos1'], $settings['form_bg_gradient_color2'], $settings['form_bg_gradient_pos2'] );
		}
		if ( $module_id === 'login-page-customizor' && isset( $settings['classic_bg_gradient_type'] ) ) {
			$type  = ( isset( $settings['classic_bg_gradient_type'] ) && $settings['classic_bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
			$angle = isset( $settings['classic_bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['classic_bg_gradient_angle'] ) ) : 135;
			$c1    = isset( $settings['classic_bg_gradient_color1'] ) ? (string) $settings['classic_bg_gradient_color1'] : '#f0f0f1';
			$p1    = isset( $settings['classic_bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['classic_bg_gradient_pos1'] ) ) : 0;
			$c2    = isset( $settings['classic_bg_gradient_color2'] ) ? (string) $settings['classic_bg_gradient_color2'] : '#e0e0e0';
			$p2    = isset( $settings['classic_bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['classic_bg_gradient_pos2'] ) ) : 100;
			if ( $c1 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c1 ) ) {
				$c1 = '#f0f0f1';
			}
			if ( $c2 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c2 ) ) {
				$c2 = '#e0e0e0';
			}
			$settings['classic_bg_gradient'] = wp_json_encode( [
				'type'  => $type,
				'angle' => $angle,
				'stops' => [
					[ 'color' => $c1, 'pos' => $p1 ],
					[ 'color' => $c2, 'pos' => $p2 ],
				],
			] );
			unset( $settings['classic_bg_gradient_type'], $settings['classic_bg_gradient_angle'], $settings['classic_bg_gradient_color1'], $settings['classic_bg_gradient_pos1'], $settings['classic_bg_gradient_color2'], $settings['classic_bg_gradient_pos2'] );
		}
		if ( $module_id === 'login-page-customizor' && ( isset( $settings['split_form_bg_gradient_type'] ) || isset( $settings['split_form_bg_gradient_color1'] ) || isset( $settings['split_form_bg_gradient_color2'] ) ) ) {
			$type  = ( isset( $settings['split_form_bg_gradient_type'] ) && $settings['split_form_bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
			$angle = isset( $settings['split_form_bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['split_form_bg_gradient_angle'] ) ) : 135;
			$c1    = isset( $settings['split_form_bg_gradient_color1'] ) ? trim( (string) $settings['split_form_bg_gradient_color1'] ) : '#f0f0f1';
			$p1    = isset( $settings['split_form_bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['split_form_bg_gradient_pos1'] ) ) : 0;
			$c2    = isset( $settings['split_form_bg_gradient_color2'] ) ? trim( (string) $settings['split_form_bg_gradient_color2'] ) : '#e0e0e0';
			$p2    = isset( $settings['split_form_bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['split_form_bg_gradient_pos2'] ) ) : 100;
			if ( $c1 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', $c1 ) ) { $c1 = '#f0f0f1'; }
			if ( $c2 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', $c2 ) ) { $c2 = '#e0e0e0'; }
			$settings['split_form_bg_gradient'] = wp_json_encode( [ 'type' => $type, 'angle' => $angle, 'stops' => [ [ 'color' => $c1, 'pos' => $p1 ], [ 'color' => $c2, 'pos' => $p2 ] ] ] );
			unset( $settings['split_form_bg_gradient_type'], $settings['split_form_bg_gradient_angle'], $settings['split_form_bg_gradient_color1'], $settings['split_form_bg_gradient_pos1'], $settings['split_form_bg_gradient_color2'], $settings['split_form_bg_gradient_pos2'] );
		}
		if ( $module_id === 'login-page-customizor' && ( isset( $settings['split_empty_bg_gradient_type'] ) || isset( $settings['split_empty_bg_gradient_color1'] ) || isset( $settings['split_empty_bg_gradient_color2'] ) ) ) {
			$type  = ( isset( $settings['split_empty_bg_gradient_type'] ) && $settings['split_empty_bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
			$angle = isset( $settings['split_empty_bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['split_empty_bg_gradient_angle'] ) ) : 135;
			$c1    = isset( $settings['split_empty_bg_gradient_color1'] ) ? trim( (string) $settings['split_empty_bg_gradient_color1'] ) : '#f0f0f1';
			$p1    = isset( $settings['split_empty_bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['split_empty_bg_gradient_pos1'] ) ) : 0;
			$c2    = isset( $settings['split_empty_bg_gradient_color2'] ) ? trim( (string) $settings['split_empty_bg_gradient_color2'] ) : '#e0e0e0';
			$p2    = isset( $settings['split_empty_bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['split_empty_bg_gradient_pos2'] ) ) : 100;
			if ( $c1 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', $c1 ) ) { $c1 = '#f0f0f1'; }
			if ( $c2 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', $c2 ) ) { $c2 = '#e0e0e0'; }
			$settings['split_empty_bg_gradient'] = wp_json_encode( [ 'type' => $type, 'angle' => $angle, 'stops' => [ [ 'color' => $c1, 'pos' => $p1 ], [ 'color' => $c2, 'pos' => $p2 ] ] ] );
			unset( $settings['split_empty_bg_gradient_type'], $settings['split_empty_bg_gradient_angle'], $settings['split_empty_bg_gradient_color1'], $settings['split_empty_bg_gradient_pos1'], $settings['split_empty_bg_gradient_color2'], $settings['split_empty_bg_gradient_pos2'] );
		}
		if ( $module_id === 'login-page-customizor' && isset( $settings['button_normal_bg_gradient_type'] ) ) {
			$type  = ( isset( $settings['button_normal_bg_gradient_type'] ) && $settings['button_normal_bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
			$angle = isset( $settings['button_normal_bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['button_normal_bg_gradient_angle'] ) ) : 135;
			$c1    = isset( $settings['button_normal_bg_gradient_color1'] ) ? (string) $settings['button_normal_bg_gradient_color1'] : '#2271b1';
			$p1    = isset( $settings['button_normal_bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['button_normal_bg_gradient_pos1'] ) ) : 0;
			$c2    = isset( $settings['button_normal_bg_gradient_color2'] ) ? (string) $settings['button_normal_bg_gradient_color2'] : '#135e96';
			$p2    = isset( $settings['button_normal_bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['button_normal_bg_gradient_pos2'] ) ) : 100;
			if ( $c1 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c1 ) ) { $c1 = '#2271b1'; }
			if ( $c2 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c2 ) ) { $c2 = '#135e96'; }
			$settings['button_normal_bg_gradient'] = wp_json_encode( [ 'type' => $type, 'angle' => $angle, 'stops' => [ [ 'color' => $c1, 'pos' => $p1 ], [ 'color' => $c2, 'pos' => $p2 ] ] ] );
			unset( $settings['button_normal_bg_gradient_type'], $settings['button_normal_bg_gradient_angle'], $settings['button_normal_bg_gradient_color1'], $settings['button_normal_bg_gradient_pos1'], $settings['button_normal_bg_gradient_color2'], $settings['button_normal_bg_gradient_pos2'] );
		}
		if ( $module_id === 'login-page-customizor' && isset( $settings['button_hover_bg_gradient_type'] ) ) {
			$type  = ( isset( $settings['button_hover_bg_gradient_type'] ) && $settings['button_hover_bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
			$angle = isset( $settings['button_hover_bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['button_hover_bg_gradient_angle'] ) ) : 135;
			$c1    = isset( $settings['button_hover_bg_gradient_color1'] ) ? (string) $settings['button_hover_bg_gradient_color1'] : '#135e96';
			$p1    = isset( $settings['button_hover_bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['button_hover_bg_gradient_pos1'] ) ) : 0;
			$c2    = isset( $settings['button_hover_bg_gradient_color2'] ) ? (string) $settings['button_hover_bg_gradient_color2'] : '#2271b1';
			$p2    = isset( $settings['button_hover_bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['button_hover_bg_gradient_pos2'] ) ) : 100;
			if ( $c1 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c1 ) ) { $c1 = '#135e96'; }
			if ( $c2 === '' || ! preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/', $c2 ) ) { $c2 = '#2271b1'; }
			$settings['button_hover_bg_gradient'] = wp_json_encode( [ 'type' => $type, 'angle' => $angle, 'stops' => [ [ 'color' => $c1, 'pos' => $p1 ], [ 'color' => $c2, 'pos' => $p2 ] ] ] );
			unset( $settings['button_hover_bg_gradient_type'], $settings['button_hover_bg_gradient_angle'], $settings['button_hover_bg_gradient_color1'], $settings['button_hover_bg_gradient_pos1'], $settings['button_hover_bg_gradient_color2'], $settings['button_hover_bg_gradient_pos2'] );
		}
		if ( $module_id === 'login-page-customizor' ) {
			foreach ( [ 'top', 'right', 'bottom', 'left' ] as $side ) {
				$key = 'field_normal_padding_' . $side;
				if ( array_key_exists( $key, $settings ) ) {
					$settings[ 'field_focus_padding_' . $side ] = $settings[ $key ];
				}
			}
			$normal_text = isset( $settings['button_normal_text_color'] ) ? strtolower( trim( (string) $settings['button_normal_text_color'] ) ) : '';
			$hover_text  = isset( $settings['button_hover_text_color'] ) ? strtolower( trim( (string) $settings['button_hover_text_color'] ) ) : '';
			if ( $hover_text === '#000000' && $normal_text !== '' && $normal_text !== '#000000' ) {
				$settings['button_hover_text_color'] = '';
			} elseif ( $hover_text === '' && $normal_text !== '' ) {
				$settings['button_hover_text_color'] = $settings['button_normal_text_color'];
			}
		}

		if ( $module_id === 'wider-admin-menu' ) {
			if ( ! class_exists( 'WiderAdminMenu\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'wider-admin-menu' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( isset( $settings['menu_width'] ) ) {
				$settings['menu_width'] = class_exists( 'WiderAdminMenu\\Module' )
					? \WiderAdminMenu\Module::sanitizeMenuWidth( $settings['menu_width'] )
					: max( 160, min( 300, absint( $settings['menu_width'] ) ) );
			}
		}

		if ( $module_id === 'media-rename-manager' ) {
			if ( ! class_exists( \MediaRenameManager\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'media-rename-manager' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( \MediaRenameManager\Module::class, false ) ) {
				$settings = \MediaRenameManager\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'limit-media-upload-size' ) {
			if ( ! class_exists( 'LimitMediaUploadSize\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'limit-media-upload-size' . DIRECTORY_SEPARATOR . 'module.php';
				if ( file_exists( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			$settings = class_exists( 'LimitMediaUploadSize\\Module' )
				? \LimitMediaUploadSize\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'block-usernames' ) {
			if ( ! class_exists( \BlockUsernames\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'block-usernames' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( \BlockUsernames\Module::class, false ) ) {
				[ $sanitized, $error ] = \BlockUsernames\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
				if ( $error !== null ) {
					wp_send_json_error(
						[
							'message' => $error,
							'code'    => 'invalid_blocked_usernames',
						]
					);
					return;
				}
				$settings = $sanitized;
			}
		}

		if ( $module_id === 'disable-thumbnail-generation' ) {
			if ( ! class_exists( \DisableThumbnailGeneration\Module::class, false ) && defined( 'WP_POWERSUITE_PLUGIN_DIR' ) ) {
				$module_file = WP_POWERSUITE_PLUGIN_DIR . 'modules/disable-thumbnail-generation/module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			$normalized = class_exists( \DisableThumbnailGeneration\Module::class, false )
				? \DisableThumbnailGeneration\Module::normalizeSettings( is_array( $raw_settings ) ? $raw_settings : [] )
				: [
					'disable_all'    => isset( $raw_settings['disable_all'] ) && (string) $raw_settings['disable_all'] === '1',
					'disabled_sizes' => [],
				];
			$settings['disable_all'] = $normalized['disable_all'];
			if ( isset( $settings['disabled_sizes'] ) && is_array( $settings['disabled_sizes'] ) ) {
				$valid_names = [];
				if ( class_exists( \DisableThumbnailGeneration\Module::class, false ) ) {
					foreach ( \DisableThumbnailGeneration\Module::getRegisteredImageSizes() as $size ) {
						if ( isset( $size['name'] ) && is_string( $size['name'] ) ) {
							$valid_names[] = $size['name'];
						}
					}
				}
				$settings['disabled_sizes'] = array_values(
					array_unique(
						array_filter(
							array_map( 'sanitize_key', $settings['disabled_sizes'] ),
							static function ( $name ) use ( $valid_names ) {
								return $name !== '' && ( $valid_names === [] || in_array( $name, $valid_names, true ) );
							}
						)
					)
				);
			} else {
				$settings['disabled_sizes'] = $normalized['disabled_sizes'];
			}
			if ( $settings['disable_all'] ) {
				$settings['disabled_sizes'] = [];
			}
		}

		if ( $module_id === 'webp-conversion' ) {
			if ( isset( $settings['quality'] ) ) {
				$settings['quality'] = max( 60, min( 100, (int) $settings['quality'] ) );
			}
			if ( isset( $settings['original_files'] ) ) {
				$settings['original_files'] = ( $settings['original_files'] === 'keep' ) ? 'keep' : 'delete';
			}
			if ( isset( $settings['exclusion_suffix'] ) && is_string( $settings['exclusion_suffix'] ) ) {
				$settings['exclusion_suffix'] = sanitize_text_field( $settings['exclusion_suffix'] );
			}
		}

		// IP Whitelist/Blacklist: normalize blocked page template / attachments (drop legacy widgets JSON).
		if ( $module_id === 'ip-whitelist-blacklist' ) {
			unset( $settings['blocked_page_widgets_json'] );
			if ( ! class_exists( \IPWhitelistBlacklist\Module::class, false ) && defined( 'WP_POWERSUITE_PLUGIN_DIR' ) ) {
				$module_file = WP_POWERSUITE_PLUGIN_DIR . 'modules/ip-whitelist-blacklist/module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			if ( isset( $settings['mode'] ) ) {
				$mode = sanitize_key( (string) $settings['mode'] );
				$settings['mode'] = in_array( $mode, [ 'blacklist', 'whitelist' ], true ) ? $mode : 'blacklist';
			}
			if ( isset( $settings['ip_header'] ) ) {
				$settings['ip_header'] = class_exists( \IPWhitelistBlacklist\Module::class, false )
					? \IPWhitelistBlacklist\Module::normalizeIpHeader( $settings['ip_header'] )
					: sanitize_text_field( (string) $settings['ip_header'] );
			}
			if ( isset( $settings['ip_addresses'] ) && is_array( $settings['ip_addresses'] ) ) {
				$validated_ips = class_exists( \IPWhitelistBlacklist\Module::class, false )
					? \IPWhitelistBlacklist\Module::filterValidListEntries( $settings['ip_addresses'] )
					: array_values( array_filter( array_map( 'sanitize_text_field', $settings['ip_addresses'] ) ) );
				$settings['ip_addresses'] = $validated_ips;

				if ( ( $settings['mode'] ?? 'blacklist' ) === 'whitelist' && $validated_ips !== [] ) {
					$client_ip = class_exists( \IPWhitelistBlacklist\Module::class, false )
						? \IPWhitelistBlacklist\Module::getResolvedClientIp( $settings )
						: '';
					if ( $client_ip === '' || ! \IPWhitelistBlacklist\Module::isClientIpInList( $client_ip, $validated_ips ) ) {
						wp_send_json_error(
							[
								'message' => __( 'Whitelist mode: your current IP must be in the list (or within a listed CIDR range) before you can save. Use “Add my IP to list” first.', 'wp-powersuite' ),
								'code'    => 'whitelist_lockout_risk',
							]
						);
						return;
					}
				}
			}
			if ( isset( $settings['blocked_page'] ) && is_array( $settings['blocked_page'] ) ) {
				$bp = &$settings['blocked_page'];
				$tpl = isset( $bp['blocked_template'] ) ? sanitize_key( (string) $bp['blocked_template'] ) : 'default';
				if ( $tpl === 'page' ) {
					$tpl = 'default';
				}
				if ( ! in_array( $tpl, [ 'default', 'html' ], true ) ) {
					$tpl = 'default';
				}
				$bp['blocked_template'] = $tpl;
				unset( $bp['blocked_page_id'] );
				$bp['logo_attachment_id'] = isset( $bp['logo_attachment_id'] ) ? max( 0, (int) $bp['logo_attachment_id'] ) : 0;
				$bp['blocked_html_attachment_id'] = isset( $bp['blocked_html_attachment_id'] ) ? max( 0, (int) $bp['blocked_html_attachment_id'] ) : 0;
				foreach ( [ 'bg_color' => '#f0f0f1', 'text_color' => '#1d2327' ] as $color_key => $fallback ) {
					if ( isset( $bp[ $color_key ] ) ) {
						$h = ltrim( (string) $bp[ $color_key ], '#' );
						if ( preg_match( '/^[a-fA-F0-9]{3}$|^[a-fA-F0-9]{6}$|^[a-fA-F0-9]{8}$/', $h ) ) {
							$bp[ $color_key ] = '#' . $h;
						} else {
							$bp[ $color_key ] = $fallback;
						}
					}
				}
				foreach ( [ 'link_url', 'link_text', 'link_target', 'custom_css', 'widgets' ] as $legacy_key ) {
					unset( $bp[ $legacy_key ] );
				}
				unset( $bp );
			}
		}

		// Coming Soon & Maintenance: validate launch_date when auto_disable is on
		if ( $module_id === 'coming-soon-maintenance' && ! empty( $settings['auto_disable'] ) && ! empty( $settings['launch_date'] ) ) {
			$launch_str = trim( (string) $settings['launch_date'] );
			if ( $launch_str !== '' ) {
				try {
					$wp_tz   = wp_timezone();
					$launch  = new \DateTime( $launch_str, $wp_tz );
					$now     = new \DateTime( 'now', $wp_tz );
					if ( $launch <= $now ) {
						wp_send_json_error(
							[
								'message' => __( 'Launch date must be in the future when auto-disable is enabled.', 'wp-powersuite' ),
								'code'    => 'invalid_launch_date',
							]
						);
						return;
					}
				} catch ( \Exception $e ) {
					wp_send_json_error(
						[
							'message' => __( 'Invalid launch date format.', 'wp-powersuite' ),
							'code'    => 'invalid_launch_date',
						]
					);
					return;
				}
			}
		}

		// magic-login: preserve message textareas, clamp numeric controls, and validate same-site redirects.
		if ( $module_id === 'magic-login' ) {
			$settings['expiry_minutes'] = isset( $settings['expiry_minutes'] ) ? max( 5, min( 60, (int) $settings['expiry_minutes'] ) ) : 15;
			$settings['rate_ip_window_minutes']    = isset( $settings['rate_ip_window_minutes'] ) ? max( 1, min( 1440, (int) $settings['rate_ip_window_minutes'] ) ) : 5;
			$settings['rate_email_window_minutes'] = isset( $settings['rate_email_window_minutes'] ) ? max( 1, min( 1440, (int) $settings['rate_email_window_minutes'] ) ) : 2;
			$settings['rate_ip_max']               = isset( $settings['rate_ip_max'] ) ? max( 0, min( 10000, (int) $settings['rate_ip_max'] ) ) : 5;
			$settings['rate_email_max']            = isset( $settings['rate_email_max'] ) ? max( 0, min( 1000, (int) $settings['rate_email_max'] ) ) : 3;
			$settings['login_code_digits']         = isset( $settings['login_code_digits'] ) ? max( 6, min( 10, (int) $settings['login_code_digits'] ) ) : 8;
			$settings['code_rate_max']             = isset( $settings['code_rate_max'] ) ? max( 1, min( 100, (int) $settings['code_rate_max'] ) ) : 10;
			$settings['code_rate_max_user']        = isset( $settings['code_rate_max_user'] ) ? max( 1, min( 50, (int) $settings['code_rate_max_user'] ) ) : 5;

			$bool_keys = [
				'enable_ajax', 'enable_honeypot', 'bypass_2fa', 'woo_login_form',
				'show_on_login', 'include_qr_code', 'enable_code_login', 'enable_auto_login',
				'enable_registration', 'show_users_column_stats', 'sc_show_password_link',
			];
			foreach ( $bool_keys as $bool_key ) {
				if ( array_key_exists( $bool_key, $settings ) ) {
					$settings[ $bool_key ] = ! empty( $settings[ $bool_key ] );
				}
			}

			$provider = isset( $settings['captcha_provider'] ) ? sanitize_key( (string) $settings['captcha_provider'] ) : 'none';
			$settings['captcha_provider'] = in_array( $provider, [ 'none', 'simple', 'recaptcha', 'turnstile' ], true ) ? $provider : 'none';

			if ( isset( $settings['registration_default_role'] ) ) {
				$settings['registration_default_role'] = sanitize_key( (string) $settings['registration_default_role'] );
			}
			if ( isset( $settings['sc_mode'] ) ) {
				$sm = sanitize_key( (string) $settings['sc_mode'] );
				$settings['sc_mode'] = in_array( $sm, [ 'auto', 'login', 'register' ], true ) ? $sm : 'auto';
			}
			foreach (
				[
					'style_bg_color',
					'style_border_color',
					'style_title_color',
					'style_desc_color',
					'style_input_border_color',
					'style_button_bg',
					'style_button_text_color',
				] as $hex_key
			) {
				if ( ! isset( $settings[ $hex_key ] ) ) {
					continue;
				}
				$cand = sanitize_hex_color( (string) $settings[ $hex_key ] );
				if ( $cand ) {
					$settings[ $hex_key ] = $cand;
				}
			}
			foreach ( [ 'style_border_radius', 'style_input_border_radius', 'style_button_radius' ] as $rad_key ) {
				if ( isset( $settings[ $rad_key ] ) ) {
					$settings[ $rad_key ] = max( 0, min( 64, (int) $settings[ $rad_key ] ) );
				}
			}
			foreach ( [ 'sc_title', 'sc_description', 'sc_button_text', 'sc_placeholder', 'sc_email_label' ] as $sck ) {
				if ( isset( $settings[ $sck ] ) && ! is_string( $settings[ $sck ] ) ) {
					$settings[ $sck ] = '';
				}
			}
			if ( class_exists( '\MagicLogin\Module' ) ) {
				$settings['error_messages'] = \MagicLogin\Module::normalizeErrorMessages( $settings['error_messages'] ?? [] );
			} elseif ( isset( $settings['error_messages'] ) && is_array( $settings['error_messages'] ) ) {
				$clean_err = [];
				foreach ( $settings['error_messages'] as $err_key => $err_val ) {
					$err_key = sanitize_key( (string) $err_key );
					$err_val = trim( sanitize_text_field( (string) $err_val ) );
					if ( $err_key !== '' && $err_val !== '' ) {
						$clean_err[ $err_key ] = $err_val;
					}
				}
				$settings['error_messages'] = $clean_err;
			} else {
				$settings['error_messages'] = [];
			}
			unset( $settings['error_generic'] );
			if ( isset( $settings['shortcode_css'] ) && ! is_string( $settings['shortcode_css'] ) ) {
				$settings['shortcode_css'] = '';
			}
			unset( $settings['debug_log'] );
			unset( $settings['email_body'], $settings['email_subject'] );
			unset(
				$settings['sc_redirect'],
				$settings['redirect_url'],
				$settings['replace_wp_login'],
				$settings['replace_wp_login_url'],
				$settings['enable_role_redirects'],
				$settings['role_redirects']
			);

			if ( ! function_exists( 'wp_roles' ) ) {
				require_once ABSPATH . 'wp-admin/includes/user.php';
			}
			$roles_obj   = wp_roles();
			$valid_roles = ( is_object( $roles_obj ) && isset( $roles_obj->roles ) ) ? array_keys( $roles_obj->roles ) : [];

			$allowed_roles = isset( $settings['allowed_roles'] ) && is_array( $settings['allowed_roles'] ) ? $settings['allowed_roles'] : [];
			$clean_allowed = [];
			$allowed_keys  = array_keys( $allowed_roles );
			$assoc_allowed = $allowed_keys !== [] && $allowed_keys !== range( 0, count( $allowed_roles ) - 1 );
			if ( $assoc_allowed ) {
				foreach ( $allowed_roles as $role_key => $val ) {
					if ( true !== $val && '1' !== $val && 1 !== $val ) {
						continue;
					}
					$role_key = sanitize_key( (string) $role_key );
					if ( $role_key !== '' && in_array( $role_key, $valid_roles, true ) ) {
						$clean_allowed[] = $role_key;
					}
				}
			} else {
				foreach ( $allowed_roles as $slug ) {
					$role_key = sanitize_key( (string) $slug );
					if ( $role_key !== '' && in_array( $role_key, $valid_roles, true ) ) {
						$clean_allowed[] = $role_key;
					}
				}
			}
			$clean_allowed = array_values( array_unique( $clean_allowed ) );
			if ( $valid_roles !== [] ) {
				$sorted_allowed = $clean_allowed;
				$sorted_valid   = array_values( $valid_roles );
				sort( $sorted_allowed, SORT_STRING );
				sort( $sorted_valid, SORT_STRING );
				if ( $sorted_allowed === $sorted_valid ) {
					$settings['allowed_roles'] = [];
				} else {
					$settings['allowed_roles'] = $clean_allowed;
				}
			} else {
				$settings['allowed_roles'] = $clean_allowed;
			}
		}

		// login-as-user: session duration, audit retention, blocked capabilities.
		if ( $module_id === 'login-as-user' ) {
			if ( class_exists( \LoginAsUser\Module::class ) ) {
				$settings = \LoginAsUser\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		// login-session-manager: session limits and display options.
		if ( $module_id === 'login-session-manager' ) {
			if ( class_exists( \LoginSessionManager\Module::class ) ) {
				$settings = \LoginSessionManager\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		// advanced-user-activity-log: session manager logging requires Login Session Manager.
		if ( $module_id === 'advanced-user-activity-log' ) {
			if ( ! OptionStoreModuleSettingsRepository::isModuleEnabled( 'login-session-manager' ) ) {
				$settings['log_session_manager'] = false;
			} elseif ( array_key_exists( 'log_session_manager', $settings ) ) {
				$settings['log_session_manager'] = ! empty( $settings['log_session_manager'] );
			}
		}

		// database-cleanup: thresholds, schedule, exclusions.
		if ( $module_id === 'database-cleanup' ) {
			if ( class_exists( \DatabaseCleanup\Module::class ) ) {
				$settings = \DatabaseCleanup\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		// admin-color-customizer: color schemes, modes, role mapping.
		if ( $module_id === 'admin-color-customizer' ) {
			if ( class_exists( \WP_PowerSuite\AdminColorCustomizer\Module::class ) ) {
				$settings = \WP_PowerSuite\AdminColorCustomizer\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		// redirect-after-login: validate URLs before change detection / save
		if ( $module_id === 'redirect-after-login' ) {
			$settings['enable_role_redirects'] = ! empty( $settings['enable_role_redirects'] );
			$settings['honor_redirect_to']     = ! empty( $settings['honor_redirect_to'] );
			if ( ! class_exists( 'WooCommerce' ) ) {
				if ( ! array_key_exists( 'woocommerce_sync', $settings ) ) {
					$existing_wc = $settings_repo->all( [] );
					$settings['woocommerce_sync'] = is_array( $existing_wc ) && array_key_exists( 'woocommerce_sync', $existing_wc )
						? ! empty( $existing_wc['woocommerce_sync'] )
						: true;
				} else {
					$settings['woocommerce_sync'] = ! empty( $settings['woocommerce_sync'] );
				}
			} else {
				$settings['woocommerce_sync'] = ! empty( $settings['woocommerce_sync'] );
			}

			$field_errors = [];

			if ( isset( $settings['fallback_url'] ) ) {
				$raw_fallback = trim( (string) $settings['fallback_url'] );
				$validated_fb = class_exists( \RedirectAfterLogin\Module::class )
					? \RedirectAfterLogin\Module::validateRedirectDestinationUrl( $raw_fallback )
					: $this->sanitizeRedirectDestinationUrl( $raw_fallback );
				if ( is_wp_error( $validated_fb ) ) {
					$field_errors['fallback_url'] = $validated_fb->get_error_message();
					$settings['fallback_url']     = '';
				} else {
					$settings['fallback_url'] = (string) $validated_fb;
				}
			} else {
				$settings['fallback_url'] = '';
			}

			if ( ! class_exists( \RedirectAfterLogin\FeatureGate::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$gate_file = WP_POWERSUITE_MODULES_DIR . 'redirect-after-login' . DIRECTORY_SEPARATOR . 'FeatureGate.php';
				if ( is_readable( $gate_file ) ) {
					require_once $gate_file;
				}
			}

			$unlocked           = class_exists( \RedirectAfterLogin\FeatureGate::class ) && \RedirectAfterLogin\FeatureGate::isPremiumLicensed();
			$can_edit_role_urls = $unlocked && ! empty( $settings['enable_role_redirects'] );
			if ( $can_edit_role_urls ) {
				$role_redirects = isset( $settings['role_redirects'] ) && is_array( $settings['role_redirects'] ) ? $settings['role_redirects'] : [];
				if ( ! function_exists( 'wp_roles' ) ) {
					require_once ABSPATH . 'wp-admin/includes/user.php';
				}
				$roles_obj   = wp_roles();
				$valid_roles = ( is_object( $roles_obj ) && isset( $roles_obj->roles ) ) ? array_keys( $roles_obj->roles ) : [];
				$clean_roles = [];
				foreach ( $role_redirects as $role_key => $role_url ) {
					$role_key = sanitize_key( (string) $role_key );
					if ( $role_key === '' || ! in_array( $role_key, $valid_roles, true ) ) {
						continue;
					}
					$raw_role_url = is_string( $role_url ) ? trim( $role_url ) : '';
					if ( $raw_role_url === '' ) {
						continue;
					}
					$validated_role = class_exists( \RedirectAfterLogin\Module::class )
						? \RedirectAfterLogin\Module::validateRedirectDestinationUrl( $raw_role_url )
						: $this->sanitizeRedirectDestinationUrl( $raw_role_url );
					if ( is_wp_error( $validated_role ) ) {
						$field_errors[ 'role_redirects.' . $role_key ] = $validated_role->get_error_message();
						continue;
					}
					if ( (string) $validated_role !== '' ) {
						$clean_roles[ $role_key ] = (string) $validated_role;
					}
				}
				$settings['role_redirects'] = $clean_roles;
			} else {
				$existing_store = $settings_repo->all( [] );
				$settings['role_redirects'] = (
					is_array( $existing_store )
					&& isset( $existing_store['role_redirects'] )
					&& is_array( $existing_store['role_redirects'] )
				)
					? $existing_store['role_redirects']
					: [];
			}

			if ( class_exists( \RedirectAfterLogin\FeatureGate::class ) ) {
				$existing_store = $settings_repo->all( [] );
				$settings       = \RedirectAfterLogin\FeatureGate::preservePremiumSettingsOnSave(
					$settings,
					is_array( $existing_store ) ? $existing_store : []
				);
			}

			if ( $field_errors !== [] ) {
				wp_send_json_error(
					[
						'message'      => __( 'Fix invalid redirect URLs before saving.', 'wp-powersuite' ),
						'code'         => 'validation_failed',
						'field_errors' => $field_errors,
					]
				);
				return;
			}
		}

		// reading-time-calculator: clamp numeric options and whitelist position
		if ( $module_id === 'reading-time-calculator' ) {
			if ( ! class_exists( \ReadingTimeCalculator\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'reading-time-calculator' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \ReadingTimeCalculator\Module::class )
				? \ReadingTimeCalculator\Module::normalizeSettings( $settings )
				: $settings;
		}

		// breadcrumbs: whitelist separator and placement; sanitize text and post types
		if ( $module_id === 'breadcrumbs' ) {
			$allowed_sep = [ '›', '/', '>', '→', '»', '|', '·' ];
			$sep         = isset( $settings['separator'] ) ? (string) $settings['separator'] : '›';
			$settings['separator'] = in_array( $sep, $allowed_sep, true ) ? $sep : '›';
			$ad = isset( $settings['auto_display'] ) ? (string) $settings['auto_display'] : 'none';
			$settings['auto_display'] = in_array( $ad, [ 'none', 'before', 'after' ], true ) ? $ad : 'none';
			$home = sanitize_text_field( (string) ( $settings['home_text'] ?? '' ) );
			$settings['home_text'] = function_exists( 'mb_substr' ) ? mb_substr( $home, 0, 120 ) : substr( $home, 0, 120 );
			if ( $settings['home_text'] === '' ) {
				$settings['home_text'] = __( 'Home', 'wp-powersuite' );
			}
			$allow = class_exists( \Breadcrumbs\Module::class )
				? \Breadcrumbs\Module::getAllowedPostTypes()
				: array_values( array_diff( get_post_types( [ 'public' => true ], 'names' ), [ 'attachment' ] ) );
			$allow_set = array_flip( $allow );
			$raw       = isset( $settings['post_types'] ) && is_array( $settings['post_types'] ) ? $settings['post_types'] : [];
			$clean     = [];
			foreach ( $raw as $slug ) {
				$k = sanitize_key( (string) $slug );
				if ( $k !== '' && isset( $allow_set[ $k ] ) ) {
					$clean[] = $k;
				}
			}
			$settings['post_types'] = array_values( array_unique( $clean ) );
			if ( $settings['post_types'] === [] ) {
				foreach ( [ 'post', 'page' ] as $fallback ) {
					if ( isset( $allow_set[ $fallback ] ) ) {
						$settings['post_types'][] = $fallback;
					}
				}
			}
		}

		if ( $module_id === 'social-share' ) {
			$settings = class_exists( \SocialShare\Module::class )
				? \SocialShare\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'ai-summarize' ) {
			if ( ! class_exists( \AISummarize\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'ai-summarize' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \AISummarize\Module::class )
				? \AISummarize\Module::normalizeSettings( is_array( $settings ) ? $settings : [] )
				: $settings;
		}

		if ( $module_id === 'custom-body-class' ) {
			if ( ! class_exists( \CustomBodyClass\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'custom-body-class' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings['classes'] = class_exists( \CustomBodyClass\Module::class )
				? \CustomBodyClass\Module::normalizeClassesString( $settings['classes'] ?? '' )
				: sanitize_text_field( (string) ( $settings['classes'] ?? '' ) );
		}

		// bulk-page-creator: allowlisted post type, status, numeric parent/author
		if ( $module_id === 'bulk-page-creator' ) {
			$pt = isset( $settings['post_type'] ) ? sanitize_key( (string) $settings['post_type'] ) : 'page';
			$settings['post_type'] = in_array( $pt, [ 'page', 'post' ], true ) ? $pt : 'page';
			$st = isset( $settings['post_status'] ) ? sanitize_key( (string) $settings['post_status'] ) : 'publish';
			$settings['post_status'] = in_array( $st, [ 'publish', 'draft', 'private' ], true ) ? $st : 'publish';

			$parent = isset( $settings['parent_page'] ) ? max( 0, (int) $settings['parent_page'] ) : 0;
			if ( $parent > 0 ) {
				$parent_post = get_post( $parent );
				if (
					! $parent_post instanceof \WP_Post
					|| $parent_post->post_type !== 'page'
					|| ! is_post_type_hierarchical( 'page' )
					|| ! current_user_can( 'edit_post', $parent )
				) {
					$parent = 0;
				}
			}
			$settings['parent_page'] = $parent;

			$author = isset( $settings['author_id'] ) ? max( 0, (int) $settings['author_id'] ) : 0;
			if ( $author > 0 ) {
				if ( ! get_userdata( $author ) ) {
					$author = 0;
				} elseif ( $author !== get_current_user_id() ) {
					$pto = get_post_type_object( $settings['post_type'] );
					$cap = $pto instanceof \WP_Post_Type ? $pto->cap->edit_others_posts : 'edit_others_posts';
					if ( ! current_user_can( $cap ) ) {
						$author = 0;
					}
				}
			}
			$settings['author_id'] = $author;
		}

		if ( $module_id === 'change-post-type' ) {
			$settings = class_exists( \ChangePostType\Module::class )
				? \ChangePostType\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'broken-link-checker' ) {
			if ( ! class_exists( \BrokenLinkChecker\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$module_file = WP_POWERSUITE_MODULES_DIR . 'broken-link-checker' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			if ( class_exists( \BrokenLinkChecker\Module::class ) ) {
				$settings = \BrokenLinkChecker\Module::normalizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		// compact-tables: clamp layout values and whitelist post types
		if ( $module_id === 'compact-tables' ) {
			if ( isset( $settings['post_types'] ) && is_array( $settings['post_types'] ) ) {
				$clean = [];
				foreach ( $settings['post_types'] as $pt ) {
					$k = sanitize_key( (string) $pt );
					if ( $k !== '' && post_type_exists( $k ) ) {
						$clean[] = $k;
					}
				}
				$settings['post_types'] = array_values( array_unique( $clean ) );
			} else {
				$settings['post_types'] = [ 'post', 'page' ];
			}
			if ( $settings['post_types'] === [] ) {
				$settings['post_types'] = [ 'post', 'page' ];
			}
			$settings['row_height']   = max( 24, min( 40, (int) ( $settings['row_height'] ?? 28 ) ) );
			$settings['cell_padding'] = max( 1, min( 10, (int) ( $settings['cell_padding'] ?? 3 ) ) );
			$settings['font_size']    = max( 10, min( 14, (int) ( $settings['font_size'] ?? 12 ) ) );
			$settings['compact_date'] = ! empty( $settings['compact_date'] );
		}

		// featured-image-column: whitelist post types with thumbnail support
		if ( $module_id === 'featured-image-column' ) {
			$allow = class_exists( \FeaturedImageColumn\Module::class )
				? \FeaturedImageColumn\Module::getAllowedPostTypes()
				: array_values(
					array_filter(
						get_post_types( [ 'public' => true ], 'names' ),
						static function ( string $post_type ): bool {
							return $post_type !== 'attachment' && post_type_supports( $post_type, 'thumbnail' );
						}
					)
				);
			$allow_set = array_flip( $allow );
			$raw       = isset( $settings['enabled_post_types'] ) && is_array( $settings['enabled_post_types'] )
				? $settings['enabled_post_types']
				: [];
			$clean     = [];
			foreach ( $raw as $slug ) {
				$k = sanitize_key( (string) $slug );
				if ( $k !== '' && isset( $allow_set[ $k ] ) ) {
					$clean[] = $k;
				}
			}
			$settings['enabled_post_types'] = array_values( array_unique( $clean ) );
			if ( $settings['enabled_post_types'] === [] ) {
				foreach ( [ 'post', 'page' ] as $fallback ) {
					if ( isset( $allow_set[ $fallback ] ) ) {
						$settings['enabled_post_types'][] = $fallback;
					}
				}
			}
		}

		// auto-set-featured-image: whitelist post types with thumbnail support
		if ( $module_id === 'auto-set-featured-image' ) {
			$allow = class_exists( \AutoSetFeaturedImage\Module::class )
				? \AutoSetFeaturedImage\Module::getAllowedPostTypes()
				: array_values(
					array_filter(
						get_post_types( [ 'public' => true ], 'names' ),
						static function ( string $post_type ): bool {
							return $post_type !== 'attachment' && post_type_supports( $post_type, 'thumbnail' );
						}
					)
				);
			$allow_set = array_flip( $allow );
			$raw       = isset( $settings['post_types'] ) && is_array( $settings['post_types'] ) ? $settings['post_types'] : [];
			$clean     = [];
			foreach ( $raw as $slug ) {
				$k = sanitize_key( (string) $slug );
				if ( $k !== '' && isset( $allow_set[ $k ] ) ) {
					$clean[] = $k;
				}
			}
			$settings['post_types'] = array_values( array_unique( $clean ) );
			if ( $settings['post_types'] === [] ) {
				foreach ( [ 'post', 'page' ] as $fallback ) {
					if ( isset( $allow_set[ $fallback ] ) ) {
						$settings['post_types'][] = $fallback;
					}
				}
			}
		}

		if ( $module_id === 'content-expiration' ) {
			$settings = class_exists( \ContentExpiration\Module::class )
				? \ContentExpiration\Module::normalizeSettings( $settings )
				: $settings;
		}

		// custom-excerpt-length: word count 0–500 (0 = theme default), short read-more string
		if ( $module_id === 'custom-excerpt-length' ) {
			$settings = class_exists( \CustomExcerptLength\Module::class )
				? \CustomExcerptLength\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'custom-post-order' ) {
			if ( ! class_exists( \CustomPostOrder\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'custom-post-order' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \CustomPostOrder\Module::class )
				? \CustomPostOrder\Module::normalizeSettings( is_array( $settings ) ? $settings : [] )
				: $settings;
			if ( class_exists( \CustomPostOrder\FeatureGate::class ) ) {
				$existing_store = $settings_repo->all( [] );
				$settings       = \CustomPostOrder\FeatureGate::preservePremiumSettingsOnSave(
					$settings,
					is_array( $existing_store ) ? $existing_store : []
				);
			}
		}

		if ( $module_id === 'custom-taxonomy-order' ) {
			if ( ! class_exists( \CustomTaxonomyOrder\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'custom-taxonomy-order' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \CustomTaxonomyOrder\Module::class )
				? \CustomTaxonomyOrder\Module::normalizeSettings( is_array( $settings ) ? $settings : [] )
				: $settings;
			if ( class_exists( \CustomTaxonomyOrder\FeatureGate::class ) ) {
				$existing_store = $settings_repo->all( [] );
				$settings       = \CustomTaxonomyOrder\FeatureGate::preservePremiumSettingsOnSave(
					$settings,
					is_array( $existing_store ) ? $existing_store : []
				);
			}
		}

		if ( $module_id === 'default-featured-image' ) {
			if ( ! class_exists( \DefaultFeaturedImage\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'default-featured-image' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \DefaultFeaturedImage\Module::class )
				? \DefaultFeaturedImage\Module::normalizeSettings( $settings )
				: [
					'attachment_id' => max( 0, (int) ( $settings['attachment_id'] ?? 0 ) ),
				];
		}

		// duplicate-post-type: whitelist enabled post types; default post + page
		if ( $module_id === 'duplicate-post-type' ) {
			$settings = class_exists( \DuplicatePostType\Module::class )
				? \DuplicatePostType\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'external-permalinks' ) {
			if ( ! class_exists( \ExternalPermalinks\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'external-permalinks' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \ExternalPermalinks\Module::class )
				? \ExternalPermalinks\Module::normalizeSettings( $settings )
				: $settings;
		}

		// post-views-counter: sanitize settings
		if ( $module_id === 'post-views-counter' ) {
			if ( ! class_exists( \PostViewsCounter\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'post-views-counter' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \PostViewsCounter\Module::class )
				? \PostViewsCounter\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'public-preview-for-drafts' ) {
			if ( ! class_exists( \PublicPreviewForDrafts\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'public-preview-for-drafts' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \PublicPreviewForDrafts\Module::class )
				? \PublicPreviewForDrafts\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'table-of-contents' ) {
			if ( ! class_exists( \TableOfContents\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'table-of-contents' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \TableOfContents\Module::class )
				? \TableOfContents\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'alpine-js-enqueue' ) {
			if ( ! class_exists( \AlpineJsEnqueue\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'alpine-js-enqueue' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \AlpineJsEnqueue\Module::class )
				? \AlpineJsEnqueue\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'accessibility-widget' ) {
			if ( ! class_exists( \AccessibilityWidget\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'accessibility-widget' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \AccessibilityWidget\Module::class )
				? \AccessibilityWidget\Module::normalizeSettings( $settings )
				: $settings;
		}

		// password-protection: store WordPress password hash; never return hash to the client
		if ( $module_id === 'password-protection' ) {
			$existing_pp = $settings_repo->all( [] );
			if ( ! is_array( $existing_pp ) ) {
				$existing_pp = [];
			}
			$raw_pw = isset( $raw_settings['site_password'] ) ? (string) $raw_settings['site_password'] : '';
			$trimmed = trim( $raw_pw );
			$mask = '••••••••••••';
			if ( $trimmed === '' ) {
				unset( $settings['site_password'], $settings['site_password_hash'] );
			} elseif ( $trimmed === $mask ) {
				if ( ! empty( $existing_pp['site_password_hash'] ) ) {
					$settings['site_password_hash'] = $existing_pp['site_password_hash'];
				}
				if ( empty( $settings['site_password_hash'] ?? '' ) && ! empty( $existing_pp['site_password'] ) ) {
					$settings['site_password'] = $existing_pp['site_password'];
				}
			} else {
				$settings['site_password_hash'] = wp_hash_password( $trimmed );
				unset( $settings['site_password'] );
			}
			if ( isset( $settings['site_password'] ) && trim( (string) $settings['site_password'] ) === $mask ) {
				unset( $settings['site_password'] );
			}

			unset( $settings['bypass_logged_in_users'], $settings['protect_login_screen'] );

			foreach ( [ 'logo_url', 'background_image' ] as $url_key ) {
				if ( isset( $settings[ $url_key ] ) ) {
					$settings[ $url_key ] = esc_url_raw( (string) $settings[ $url_key ] );
				}
			}
			if ( isset( $settings['logo_width'] ) ) {
				$settings['logo_width'] = (string) max( 50, min( 500, (int) $settings['logo_width'] ) );
			}
			if ( isset( $settings['overlay_opacity'] ) ) {
				$settings['overlay_opacity'] = (string) max( 0, min( 1, (float) $settings['overlay_opacity'] ) );
			}
			foreach ( [ 'overlay_color', 'background_color', 'form_bg_color', 'button_color', 'button_hover_color' ] as $color_key ) {
				if ( ! isset( $settings[ $color_key ] ) ) {
					continue;
				}
				$color = sanitize_hex_color( (string) $settings[ $color_key ] );
				if ( $color ) {
					$settings[ $color_key ] = $color;
				}
			}
		}

		// avif-conversion: explicit booleans, bounded numbers, preserve stats for comparison/save
		if ( $module_id === 'avif-conversion' ) {
			$ex      = $settings_repo->all( [] );
			if ( ! is_array( $ex ) ) {
				$ex = [];
			}

			$settings['convert_jpeg']          = ! empty( $raw_settings['convert_jpeg'] );
			$settings['convert_png']           = ! empty( $raw_settings['convert_png'] );
			$settings['keep_original']         = ! empty( $raw_settings['keep_original'] );
			$settings['replace_original']      = ! empty( $raw_settings['replace_original'] );
			if ( ! empty( $settings['replace_original'] ) ) {
				$settings['keep_original'] = true;
			}
			$settings['serve_avif_picture']    = ! empty( $raw_settings['serve_avif_picture'] );
			$settings['allow_external_binary'] = array_key_exists( 'allow_external_binary', $raw_settings )
				? ! empty( $raw_settings['allow_external_binary'] )
				: ! empty( $ex['allow_external_binary'] );
			$preset                            = isset( $raw_settings['preset'] ) ? sanitize_key( (string) $raw_settings['preset'] ) : 'balanced';
			$settings['preset']                = in_array( $preset, [ 'balanced', 'max_compression', 'max_quality', 'custom' ], true ) ? $preset : 'balanced';
			$settings['quality']               = isset( $raw_settings['quality'] ) ? max( 0, min( 100, (int) $raw_settings['quality'] ) ) : 80;
			$settings['encoding_speed']        = isset( $raw_settings['encoding_speed'] ) ? max( 0, min( 10, (int) $raw_settings['encoding_speed'] ) ) : 6;
			$settings['sync_max_bytes']        = isset( $raw_settings['sync_max_bytes'] )
				? max( 102400, min( 52428800, (int) $raw_settings['sync_max_bytes'] ) )
				: max( 102400, min( 52428800, (int) ( $ex['sync_max_bytes'] ?? 1048576 ) ) );
			$settings['min_input_bytes']       = isset( $raw_settings['min_input_bytes'] )
				? max( 0, min( 10485760, (int) $raw_settings['min_input_bytes'] ) )
				: max( 0, min( 10485760, (int) ( $ex['min_input_bytes'] ?? 5120 ) ) );
			$settings['max_dimension']         = isset( $raw_settings['max_dimension'] )
				? max( 0, min( 20000, (int) $raw_settings['max_dimension'] ) )
				: max( 0, min( 20000, (int) ( $ex['max_dimension'] ?? 0 ) ) );
			$suffix                            = isset( $raw_settings['exclusion_suffix'] ) ? (string) $raw_settings['exclusion_suffix'] : '-wpps';
			$settings['exclusion_suffix']      = substr( sanitize_text_field( $suffix ), 0, 40 );
			$bin                               = isset( $raw_settings['external_binary_path'] )
				? trim( (string) $raw_settings['external_binary_path'] )
				: ( isset( $ex['external_binary_path'] ) ? trim( (string) $ex['external_binary_path'] ) : '' );
			$settings['external_binary_path']  = '';
			if ( ! empty( $settings['allow_external_binary'] ) && $bin !== '' ) {
				$bin = sanitize_text_field( $bin );
				if ( $bin !== '' && ! preg_match( '/[\r\n;|&`$]/', $bin ) && strtoupper( substr( PHP_OS, 0, 3 ) ) !== 'WIN' ) {
					$real = realpath( $bin );
					if ( $real !== false && is_file( $real ) && is_executable( $real ) ) {
						$settings['external_binary_path'] = $real;
					}
				}
			}

			foreach ( [ 'stat_images_converted', 'stat_bytes_saved', 'last_processed' ] as $sk ) {
				if ( ! array_key_exists( $sk, $settings ) && array_key_exists( $sk, $ex ) ) {
					$settings[ $sk ] = $ex[ $sk ];
				}
			}
		}

		$content_protection_modules = [ 'disable-keyboard-keys', 'disable-right-click', 'disable-drag-drop', 'disable-text-selection' ];
		if ( in_array( $module_id, $content_protection_modules, true ) ) {
			// Checkbox may arrive as '' / '0' / '1' from the admin UI — coerce to strict bool.
			$settings['apply_to_visitors'] = ! empty( $raw_settings['apply_to_visitors'] ) && (string) $raw_settings['apply_to_visitors'] !== '0';
			if ( isset( $settings['roles'] ) && is_array( $settings['roles'] ) ) {
				if ( ! function_exists( 'wp_roles' ) ) {
					require_once ABSPATH . 'wp-admin/includes/user.php';
				}
				$roles_obj    = wp_roles();
				$valid_roles = is_object( $roles_obj ) && isset( $roles_obj->roles ) ? array_keys( $roles_obj->roles ) : [];
				$settings['roles'] = array_values(
					array_unique(
						array_filter(
							array_map( 'sanitize_key', $settings['roles'] ),
							static function ( $slug ) use ( $valid_roles ) {
								return $slug !== '' && in_array( $slug, $valid_roles, true );
							}
						)
					)
				);
			} else {
				$settings['roles'] = [];
			}

			$allowed_positions = [ 'top-right', 'top-left', 'top-center', 'bottom-right', 'bottom-left', 'bottom-center' ];
			if ( isset( $settings['message_position'] ) ) {
				$pos = sanitize_key( (string) $settings['message_position'] );
				$settings['message_position'] = in_array( $pos, $allowed_positions, true ) ? $pos : 'top-right';
			}
			if ( isset( $settings['message_duration'] ) ) {
				$settings['message_duration'] = min( 120000, max( 500, absint( $settings['message_duration'] ) ) );
			}
			if ( isset( $settings['message'] ) ) {
				$settings['message'] = sanitize_text_field( (string) $settings['message'] );
			}
		}

		if ( $module_id === 'analytics-integration' ) {
			if ( ! class_exists( \AnalyticsIntegration\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$module_file = WP_POWERSUITE_MODULES_DIR . 'analytics-integration' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			if ( class_exists( \AnalyticsIntegration\Module::class, false ) ) {
				$existing_analytics = $settings_repo->all( [] );
				$existing_analytics = is_array( $existing_analytics ) ? $existing_analytics : [];
				$incoming_ga4_id    = strtoupper( trim( (string) ( $raw_settings['ga4_id'] ?? ( $settings['ga4_id'] ?? '' ) ) ) );
				if ( $incoming_ga4_id !== '' && ! \AnalyticsIntegration\Module::isValidGa4Id( $incoming_ga4_id ) ) {
					wp_send_json_error(
						[
							'message' => __( 'Invalid GA4 Measurement ID. Use the format G-XXXXXXXXXX (10 characters after G-).', 'wp-powersuite' ),
							'code'    => 'invalid_ga4_id',
						]
					);
					return;
				}
				$settings = \AnalyticsIntegration\Module::normalizeSettings(
					is_array( $settings ) ? $settings : [],
					$existing_analytics
				);
			}
		}

		if ( $module_id === 'disable-gutenberg' && class_exists( \DisableGutenberg\Module::class ) ) {
			$settings = \DisableGutenberg\Module::normalizeSettings( is_array( $settings ) ? $settings : [] );
		}

		if ( $module_id === 'disable-heartbeat' ) {
			if ( ! class_exists( \DisableHeartbeat\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$module_file = WP_POWERSUITE_MODULES_DIR . 'disable-heartbeat' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			$settings = class_exists( \DisableHeartbeat\Module::class, false )
				? \DisableHeartbeat\Module::normalizeSettings( is_array( $settings ) ? $settings : [] )
				: $settings;
		}

		if ( $module_id === 'two-factor-authentication' ) {
			if ( ! class_exists( \TwoFactorAuthentication\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$module_file = WP_POWERSUITE_MODULES_DIR . 'two-factor-authentication' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			if ( class_exists( \TwoFactorAuthentication\Module::class, false ) ) {
				$settings = \TwoFactorAuthentication\Module::normalizeSettings( is_array( $settings ) ? $settings : [] );
			}

			if ( ApplicationPasswordConflictPolicy::isDisableApplicationPasswordsEnabled() ) {
				$settings['restrict_api_login_to_app_passwords'] = false;
			}

			if ( ApplicationPasswordConflictPolicy::wouldBlockTwoFactorSettingsSave( $settings ) ) {
				wp_send_json_error(
					[
						'message' => ApplicationPasswordConflictPolicy::getTwoFactorSettingsConflictMessage(),
						'code'    => 'application_password_conflict',
					]
				);
				return;
			}
		}

		if ( $module_id === 'ai-alt-text-generator' ) {
			if ( defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$dependencies = WP_POWERSUITE_MODULES_DIR . 'ai-alt-text-generator/includes/dependencies.php';
				if ( is_readable( $dependencies ) ) {
					require_once $dependencies;
				}
			}
			if ( ! class_exists( \AIAltTextGenerator\Settings::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$settings_file = WP_POWERSUITE_MODULES_DIR . 'ai-alt-text-generator/includes/class-settings.php';
				if ( is_readable( $settings_file ) ) {
					require_once $settings_file;
				}
			}
			$existing_ai_alt = $settings_repo->all( [] );
			if ( class_exists( \AIAltTextGenerator\Settings::class ) ) {
				$settings = \AIAltTextGenerator\Settings::normalize(
					is_array( $settings ) ? $settings : [],
					is_array( $existing_ai_alt ) ? $existing_ai_alt : []
				);
			}
		}

		if ( $module_id === 'basic-forms' ) {
			if ( ! class_exists( \BasicForms\AiSettings::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				require_once WP_POWERSUITE_MODULES_DIR . 'basic-forms/includes/AiSettings.php';
				require_once WP_POWERSUITE_MODULES_DIR . 'basic-forms/includes/FeatureGate.php';
				require_once WP_POWERSUITE_MODULES_DIR . 'basic-forms/includes/FormAiModels.php';
			}
			$existing_bf = $settings_repo->all( [] );
			if ( class_exists( \BasicForms\AiSettings::class ) ) {
				$settings = \BasicForms\AiSettings::normalize(
					is_array( $raw_settings ) ? $raw_settings : [],
					is_array( $existing_bf ) ? $existing_bf : []
				);
				\BasicForms\AiSettings::flushCache();
			}
		}

		if ( $module_id === 'hide-admin-bar' ) {
			if ( ! class_exists( \HideAdminBar\Module::class, false ) && defined( 'WP_POWERSUITE_PLUGIN_DIR' ) ) {
				$module_file = WP_POWERSUITE_PLUGIN_DIR . 'modules/hide-admin-bar/module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			if ( class_exists( \HideAdminBar\Module::class ) ) {
				$settings = \HideAdminBar\Module::normalizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'remove-admin-bar-items' ) {
			if ( ! class_exists( \RemoveAdminBarItems\Module::class, false ) && defined( 'WP_POWERSUITE_PLUGIN_DIR' ) ) {
				$module_file = WP_POWERSUITE_PLUGIN_DIR . 'modules/remove-admin-bar-items/module.php';
				if ( is_readable( $module_file ) ) {
					require_once $module_file;
				}
			}
			if ( class_exists( \RemoveAdminBarItems\Module::class ) ) {
				$settings = \RemoveAdminBarItems\Module::normalizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'disable-keyboard-keys' ) {
			$key_toggle_keys = [
				'dev_tools',
				'search',
				'ctrl_c',
				'ctrl_v',
				'ctrl_a',
				'ctrl_u',
				'ctrl_p',
				'ctrl_h',
				'ctrl_l',
				'ctrl_k',
				'ctrl_o',
				'ctrl_e',
				'ctrl_f3',
				'alt_d',
				'f6',
				'f12',
			];
			foreach ( $key_toggle_keys as $kk ) {
				$settings[ $kk ] = ! empty( $raw_settings[ $kk ] );
			}
		}

		// OAuth sign-in: merge, secret preservation, and redirect validation (before change comparison).
		if ( class_exists( \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::class )
			&& \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::isSocialModule( $module_id )
			&& is_array( $settings ) ) {
			$existing = $settings_repo->all( [] );
			$existing = is_array( $existing ) ? $existing : [];
			$settings = \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::normalizeModuleSettings(
				$module_id,
				$settings,
				is_array( $raw_settings ) ? $raw_settings : []
			);
			$settings = \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::normalizeSocialAccountsSettings(
				$settings,
				is_array( $raw_settings ) ? $raw_settings : []
			);
			if ( is_array( $existing ) ) {
				$settings = array_merge( $existing, $settings );
			}
			$settings = \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::preserveSecretOnSave( $settings, $existing, $module_id );
			$settings = \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::sanitizeRedirectAfterLogin( $settings, $existing );
			if ( $module_id === 'google-sign-in' ) {
				if ( ! array_key_exists( 'store_avatar_url', is_array( $raw_settings ) ? $raw_settings : [] )
					&& ! array_key_exists( 'store_avatar_url', $settings ) ) {
					$settings['store_avatar_url'] = true;
				} else {
					$settings['store_avatar_url'] = ! empty( $settings['store_avatar_url'] );
				}
			}
		}

		// Compare against stored settings.
		$current_settings = $settings_repo->all( [] );
		
		// Normalize arrays for comparison (sort arrays and filter out false/empty values)
		$normalize_for_comparison = function( $value ) use ( &$normalize_for_comparison ) {
			if ( is_array( $value ) ) {
				$normalized = [];
				foreach ( $value as $k => $v ) {
					// For associative arrays (like roles), filter out false values
					$normalized_v = $normalize_for_comparison( $v );
					// Skip false/null/empty in associative arrays (except system-summary sections which use 0)
					if ( ! is_numeric( $k ) && ( $normalized_v === false || $normalized_v === null || $normalized_v === '' ) ) {
						continue;
					}
					$normalized[ $k ] = $normalized_v;
				}
				ksort( $normalized );
				return $normalized;
			}
			return $value;
		};
		
		$normalized_current = $normalize_for_comparison( $current_settings );
		$normalized_new = $normalize_for_comparison( $settings );
		
		if ( $normalized_current === $normalized_new ) {
			if ( $module_id === 'password-protection' ) {
				$settings = $this->redactPasswordProtectionSettingsForResponse( $settings );
			}
			if ( $module_id === 'email-smtp' && class_exists( 'EmailSmtp\\Module' ) ) {
				$settings = \EmailSmtp\Module::redactSettingsForResponse( $settings );
			}
			if ( class_exists( \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::class )
				&& \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::isSocialModule( $module_id ) ) {
				$settings = \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::redactSecretsForResponse( $settings, $module_id );
			}
			if ( $module_id === 'change-login-url' ) {
				if ( ! class_exists( 'ChangeLoginURL\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
					$mod_file = WP_POWERSUITE_MODULES_DIR . 'change-login-url' . DIRECTORY_SEPARATOR . 'module.php';
					if ( file_exists( $mod_file ) ) {
						require_once $mod_file;
					}
				}
				if ( class_exists( 'ChangeLoginURL\\Module' ) ) {
					\ChangeLoginURL\Module::refreshRoutingState();
				}
			}
			wp_send_json_success(
				[
					'module_id' => $module_id,
					'settings'  => $settings,
					'message'   => __( 'No changes were made.', 'wp-powersuite' ),
					'no_changes' => true,
				]
			);
			return;
		}


		// Preserve API key when empty (user did not change it)
		if ( $module_id === 'ai-alt-text-generator' && ( ! isset( $settings['api_key'] ) || trim( (string) $settings['api_key'] ) === '' ) ) {
			$existing = $settings_repo->all( [] );
			if ( is_array( $existing ) && ! empty( $existing['api_key'] ) ) {
				$settings['api_key'] = $existing['api_key'];
			}
		}

		// Merge with existing module settings for custom-image-sizes (ensures custom_sizes persists)
		if ( $module_id === 'custom-image-sizes' ) {
			$existing = $settings_repo->all( [] );
			if ( is_array( $existing ) ) {
				$settings = array_merge( $existing, $settings );
			}
		}

		// OAuth sign-in: global linking policy lives under social_accounts.* (not per-module keys)
		if ( in_array( $module_id, [ 'google-sign-in', 'facebook-sign-in', 'github-sign-in' ], true ) && is_array( $settings ) ) {
			if ( isset( $settings['social_accounts'] ) && is_array( $settings['social_accounts'] ) ) {
				$sa = $settings['social_accounts'];
				$settings['social_accounts'] = [
					'linking_enabled'                => array_key_exists( 'linking_enabled', $sa )
						? (bool) $sa['linking_enabled']
						: ! empty( $sa['linking_enabled'] ),
					'email_on_login'                 => in_array( (string) ( $sa['email_on_login'] ?? 'auto' ), [ 'auto', 'manual' ], true )
						? (string) $sa['email_on_login']
						: 'auto',
					'require_email_match_to_connect' => array_key_exists( 'require_email_match_to_connect', $sa )
						? (bool) $sa['require_email_match_to_connect']
						: ! empty( $sa['require_email_match_to_connect'] ),
					'show_on_my_account'             => array_key_exists( 'show_on_my_account', $sa )
						? (bool) $sa['show_on_my_account']
						: ! empty( $sa['show_on_my_account'] ),
					'oauth_debug'                    => array_key_exists( 'oauth_debug', $sa )
						? (bool) $sa['oauth_debug']
						: ! empty( $sa['oauth_debug'] ),
				];
				if ( class_exists( \WP_PowerSuite\OAuth\Accounts\AccountLinkSettings::class ) ) {
					\WP_PowerSuite\OAuth\Accounts\AccountLinkSettings::saveFromModuleSettings( $settings['social_accounts'] );
				}
				unset( $settings['social_accounts'] );
			}
			$bd = isset( $settings['button_display'] ) ? (string) $settings['button_display'] : 'label';
			if ( $bd === 'icon' ) {
				$settings['button_label_inline'] = false;
			}
		}

		// email-reports: normalize custom_recipients, sections, subject_format
		if ( $module_id === 'email-reports' ) {
			if ( isset( $settings['custom_recipients'] ) ) {
				$raw = $settings['custom_recipients'];
				$raw = is_string( $raw ) ? $raw : ( is_array( $raw ) ? implode( "\n", $raw ) : '' );
				$lines = preg_split( '/\r\n|\r|\n/', $raw );
				$emails = array_filter( array_map( 'trim', $lines ), 'is_email' );
				$settings['custom_recipients'] = implode( "\n", $emails );
			}
			if ( isset( $settings['sections'] ) && is_array( $settings['sections'] ) ) {
				$settings['sections'] = array_values( array_filter( array_map( 'sanitize_key', $settings['sections'] ) ) );
			}
			if ( isset( $settings['subject_format'] ) ) {
				$settings['subject_format'] = sanitize_text_field( $settings['subject_format'] );
			}
		}

		if ( $module_id === 'advanced-security-headers' ) {
			if ( ! class_exists( 'AdvancedSecurityHeaders\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'advanced-security-headers' . DIRECTORY_SEPARATOR . 'module.php';
				if ( file_exists( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( 'AdvancedSecurityHeaders\\Module' ) ) {
				$settings = \AdvancedSecurityHeaders\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'link-shortener' ) {
			if ( ! class_exists( \LinkShortener\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'link-shortener' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( \LinkShortener\Module::class, false ) ) {
				$settings = \LinkShortener\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'disable-file-editing' ) {
			if ( ! class_exists( \DisableFileEditing\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'disable-file-editing' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( \DisableFileEditing\Module::class, false ) ) {
				$settings = \DisableFileEditing\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'disable-password-reset' ) {
			if ( ! class_exists( \DisablePasswordReset\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'disable-password-reset' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( \DisablePasswordReset\Module::class, false ) ) {
				$settings = \DisablePasswordReset\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'disable-rest-api' ) {
			if ( ! class_exists( \DisableRESTAPI\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'disable-rest-api' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( \DisableRESTAPI\Module::class, false ) ) {
				$settings = \DisableRESTAPI\Module::sanitizeSettings( is_array( $settings ) ? $settings : [] );
			}
		}

		if ( $module_id === 'change-login-url' ) {
			if ( ! class_exists( 'ChangeLoginURL\\Module' ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'change-login-url' . DIRECTORY_SEPARATOR . 'module.php';
				if ( file_exists( $mod_file ) ) {
					require_once $mod_file;
				}
			}
			if ( class_exists( 'ChangeLoginURL\\Module' ) ) {
				$existing = $settings_repo->all( [] );
				$existing = is_array( $existing ) ? $existing : [];
				[ $sanitized, $error ] = \ChangeLoginURL\Module::sanitizeSettings( is_array( $settings ) ? $settings : [], $existing );
				if ( $error !== null ) {
					wp_send_json_error(
						[
							'message' => $error,
							'code'    => 'invalid_login_slug',
						]
					);
					return;
				}
				$settings = $sanitized;
			}
		}

		if ( $module_id === '404-redirect' ) {
			if ( ! class_exists( \Redirect404\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . '404-redirect' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			if ( class_exists( \Redirect404\Module::class ) ) {
				$raw = is_array( $settings ) ? $settings : [];
				$error = \Redirect404\Module::validateSettingsForSave( $raw );
				if ( $error !== null ) {
					wp_send_json_error(
						[
							'message' => $error,
							'code'    => '404_redirect_301_ack_required',
						]
					);
					return;
				}
				$settings = \Redirect404\Module::normalizeSettings( $raw );
			}
		}

		if ( $module_id === 'track-404s' ) {
			if ( ! class_exists( \Track404s\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'track-404s' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			if ( class_exists( \Track404s\Module::class ) ) {
				$normalized = \Track404s\Module::normalizeSettings( is_array( $settings ) ? $settings : [] );
				$normalized['ignore_patterns'] = implode( ', ', $normalized['ignore_patterns'] ?? [] );
				$settings = $normalized;
			}
		}

		if ( $module_id === 'cloudflare-turnstile' ) {
			if ( ! class_exists( \CloudflareTurnstile\FeatureGate::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$gate_file = WP_POWERSUITE_MODULES_DIR . 'cloudflare-turnstile' . DIRECTORY_SEPARATOR . 'FeatureGate.php';
				if ( is_readable( $gate_file ) ) {
					require_once $gate_file;
				}
			}
			if ( class_exists( \CloudflareTurnstile\FeatureGate::class ) ) {
				$existing_store = $settings_repo->all( [] );
				$settings       = \CloudflareTurnstile\FeatureGate::preservePremiumSettingsOnSave(
					is_array( $settings ) ? $settings : [],
					is_array( $existing_store ) ? $existing_store : []
				);
			}
		}

		if ( $module_id === 'google-recaptcha' ) {
			if ( ! class_exists( \GoogleRecaptcha\FeatureGate::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$gate_file = WP_POWERSUITE_MODULES_DIR . 'google-recaptcha' . DIRECTORY_SEPARATOR . 'FeatureGate.php';
				if ( is_readable( $gate_file ) ) {
					require_once $gate_file;
				}
			}
			if ( class_exists( \GoogleRecaptcha\FeatureGate::class ) ) {
				$existing_store = $settings_repo->all( [] );
				$settings       = \GoogleRecaptcha\FeatureGate::preservePremiumSettingsOnSave(
					is_array( $settings ) ? $settings : [],
					is_array( $existing_store ) ? $existing_store : []
				);
			}
		}

		if ( $module_id === 'simple-captcha' ) {
			if ( ! class_exists( \SimpleCaptcha\FeatureGate::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$gate_file = WP_POWERSUITE_MODULES_DIR . 'simple-captcha' . DIRECTORY_SEPARATOR . 'FeatureGate.php';
				if ( is_readable( $gate_file ) ) {
					require_once $gate_file;
				}
			}
			if ( class_exists( \SimpleCaptcha\FeatureGate::class ) ) {
				$existing_store = $settings_repo->all( [] );
				$settings       = \SimpleCaptcha\FeatureGate::preservePremiumSettingsOnSave(
					is_array( $settings ) ? $settings : [],
					is_array( $existing_store ) ? $existing_store : []
				);
			}
		}

		if ( $module_id === 'email-smtp' ) {
			if ( ! class_exists( 'EmailSmtp\\Module', false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'email-smtp' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			if ( class_exists( 'EmailSmtp\\Module' ) ) {
				$settings = \EmailSmtp\Module::normalizeSettings( is_array( $settings ) ? $settings : [] );
				if ( ! empty( $settings['smtp_password'] ) ) {
					$settings['smtp_password'] = \EmailSmtp\Includes\SmtpSettingsHelper::migrateStoredPassword(
						(string) $settings['smtp_password'],
						[ \EmailSmtp\Module::class, 'encryptPassword' ]
					);
				}
				$provider = (string) ( $settings['provider'] ?? 'smtp' );
				if (
					$provider === 'smtp'
					&& ( $settings['smtp_auth'] ?? '' ) === '1'
					&& ( $settings['smtp_host'] ?? '' ) !== ''
					&& ( $settings['smtp_username'] ?? '' ) !== ''
					&& empty( $settings['smtp_password'] )
					&& ! defined( 'WP_POWERSUITE_SMTP_PASSWORD' )
				) {
					wp_send_json_error(
						[
							'message' => __( 'SMTP password is required when authentication is enabled.', 'wp-powersuite' ),
							'code'    => 'smtp_password_required',
						]
					);
					return;
				}
			}
		}

		if ( $module_id === 'force-ssl-admin' ) {
			$settings['force_admin'] = ! empty( $settings['force_admin'] );
			$settings['force_login'] = ! empty( $settings['force_login'] );

			$redirect_code             = isset( $settings['redirect_code'] ) ? (string) $settings['redirect_code'] : '301';
			$settings['redirect_code'] = in_array( $redirect_code, [ '301', '302' ], true ) ? $redirect_code : '301';

			$existing = $settings_repo->all( [] );
			$existing = is_array( $existing ) ? $existing : [];

			if ( ! empty( $settings['regenerate_emergency_key'] ) ) {
				$settings['emergency_key'] = \ForceSslAdmin\Module::generateEmergencyKey();
			} elseif ( ! empty( $existing['emergency_key'] ) && is_string( $existing['emergency_key'] ) ) {
				$settings['emergency_key'] = preg_replace( '/[^a-zA-Z0-9]/', '', $existing['emergency_key'] );
			} else {
				$settings['emergency_key'] = \ForceSslAdmin\Module::generateEmergencyKey();
			}

			if ( strlen( (string) $settings['emergency_key'] ) < 20 ) {
				$settings['emergency_key'] = \ForceSslAdmin\Module::generateEmergencyKey();
			}

			unset( $settings['regenerate_emergency_key'] );
		}

		if ( $module_id === 'custom-admin-footer-text' ) {
			unset( $settings['footer_text'] );
		}

		if ( $module_id === 'login-email-notification' ) {
			if ( ! class_exists( \LoginEmailNotification\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'login-email-notification' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$settings = class_exists( \LoginEmailNotification\Module::class )
				? \LoginEmailNotification\Module::normalizeSettings( $settings )
				: $settings;
		}

		if ( $module_id === 'coming-soon-maintenance' ) {
			if ( ! class_exists( \ComingSoonMaintenance\Module::class, false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod = WP_POWERSUITE_MODULES_DIR . 'coming-soon-maintenance' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod ) ) {
					require_once $mod;
				}
			}
			$existing = $settings_repo->all( [] );
			$existing = is_array( $existing ) ? $existing : [];
			$defaults = class_exists( \ComingSoonMaintenance\Module::class )
				? \ComingSoonMaintenance\Module::getDefaultSettings()
				: [];
			$settings = array_merge( $defaults, $existing, is_array( $settings ) ? $settings : [] );
			foreach ( [ 'enabled', 'show_logo', 'show_subscribe', 'auto_disable', 'logo_bg_enabled', 'use_existing_page', 'exclude_woocommerce' ] as $bool_key ) {
				if ( array_key_exists( $bool_key, $settings ) ) {
					$settings[ $bool_key ] = ! empty( $settings[ $bool_key ] ) ? 1 : 0;
				}
			}
			foreach ( [ 'bypass_token', 'fetch_secret' ] as $secret_key ) {
				if ( empty( $settings[ $secret_key ] ) && ! empty( $existing[ $secret_key ] ) ) {
					$settings[ $secret_key ] = $existing[ $secret_key ];
				}
			}
			$desktop_width = isset( $settings['logo_width_desktop'] ) && is_numeric( $settings['logo_width_desktop'] ) && (int) $settings['logo_width_desktop'] > 0
				? max( 40, min( 600, (int) $settings['logo_width_desktop'] ) )
				: ( isset( $settings['logo_max_width'] ) ? max( 40, min( 600, (int) $settings['logo_max_width'] ) ) : 180 );
			$settings['logo_max_width']      = $desktop_width;
			$settings['logo_width_desktop']  = $desktop_width;
		}

		OptionStoreModuleSettingsRepository::clearCache();
		$success = $settings_repo->save( $settings );
		OptionStoreModuleSettingsRepository::clearCache();

		if ( ! $success ) {
			wp_send_json_error(
				[
					'message' => __( 'Failed to save module settings. Please try again.', 'wp-powersuite' ),
					'code'    => 'save_failed',
				]
			);
			return;
		}

		if ( $module_id === 'cloudflare-turnstile' ) {
			$site_key = trim( (string) ( $settings['site_key'] ?? '' ) );
			$has_secret = isset( $settings['secret_key'] ) && trim( (string) $settings['secret_key'] ) !== '';
			if ( $site_key === '' || ! $has_secret ) {
				delete_transient( 'wpps_turnstile_cf_probe' );
			}
		}

		if ( $module_id === 'password-protection' ) {
			if ( defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$pwd_mod = WP_POWERSUITE_MODULES_DIR . 'password-protection' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $pwd_mod ) ) {
					require_once $pwd_mod;
				}
			}
			if ( class_exists( '\PasswordProtection\Module' ) ) {
				\PasswordProtection\Module::clearAllRateLimitTransients();
			}
		}

		// Special handling for ads-txt-manager: write content to ads.txt file when writable
		if ( $module_id === 'ads-txt-manager' && isset( $settings['content'] ) ) {
			if ( ! class_exists( '\AdsTxtManager\Module', false ) && defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
				$mod_file = WP_POWERSUITE_MODULES_DIR . 'ads-txt-manager' . DIRECTORY_SEPARATOR . 'module.php';
				if ( is_readable( $mod_file ) ) {
					require_once $mod_file;
				}
			}

			if ( class_exists( '\AdsTxtManager\Module', false ) ) {
				$ads_module   = new \AdsTxtManager\Module();
				$write_result = $ads_module->writeAdsTxtFile( (string) $settings['content'] );
				if ( empty( $write_result['success'] ) ) {
					wp_send_json_error(
						[
							'message' => $write_result['message'] ?? __( 'Failed to write ads.txt file.', 'wp-powersuite' ),
							'code'    => 'file_write_failed',
						]
					);
					return;
				}
			}
			// When file is not writable: content is already saved to OptionStore.
			// Virtual fallback in module.php serves /ads.txt from OptionStore on the frontend.
		}

		do_action( 'wp_powersuite_module_settings_saved', $module_id, $settings );

		if ( $module_id === 'password-protection' ) {
			$settings = $this->redactPasswordProtectionSettingsForResponse( $settings );
		}
		if ( $module_id === 'email-smtp' && class_exists( 'EmailSmtp\\Module' ) ) {
			$settings = \EmailSmtp\Module::redactSettingsForResponse( $settings );
		}
		if ( class_exists( \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::class )
			&& \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::isSocialModule( $module_id ) ) {
			$settings = \WP_PowerSuite\OAuth\Helpers\OAuthSettingsHelper::redactSecretsForResponse( $settings, $module_id );
		}

		wp_send_json_success(
			[
				'module_id' => $module_id,
				'settings'  => $settings,
				'message'   => __( 'Settings saved successfully.', 'wp-powersuite' ),
			]
		);
	}
	/**
	 * Remove secrets from password-protection settings before returning them via AJAX.
	 *
	 * @param array<string, mixed> $settings Module settings.
	 * @return array<string, mixed>
	 */
	private function redactPasswordProtectionSettingsForResponse( array $settings ): array {
		$had_secret = ! empty( $settings['site_password_hash'] ) || ! empty( $settings['site_password'] );
		unset( $settings['site_password_hash'], $settings['site_password'] );
		if ( $had_secret ) {
			$settings['site_password'] = '••••••••••••';
		}
		return $settings;
	}

	/**
	 * Check if array contains nested arrays.
	 *
	 * @param array $array Array to check.
	 * @return bool True if contains nested arrays, false otherwise.
	 */
	private function hasNestedArrays( array $array ): bool {
		foreach ( $array as $value ) {
			if ( is_array( $value ) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Sanitize nested array recursively.
	 *
	 * @param array $array Array to sanitize.
	 * @return array Sanitized array.
	 */
	private function sanitizeNestedArray( array $array ): array {
		$sanitized = [];
		
		foreach ( $array as $key => $value ) {
			// Skip null values
			if ( $value === null ) {
				continue;
			}
			
			$key = sanitize_key( $key );

			if ( is_string( $value ) && str_ends_with( $key, '_id' ) ) {
				$sanitized[ $key ] = max( 0, (int) $value );
				continue;
			}

			if ( is_array( $value ) ) {
				if ( empty( $value ) ) {
					$sanitized[ $key ] = [];
				} else {
					// Recursively sanitize nested arrays
					$sanitized[ $key ] = $this->sanitizeNestedArray( $value );
				}
			} elseif ( is_string( $value ) ) {
				// Convert checkbox values
				if ( $value === '1' || $value === 'true' ) {
					$sanitized[ $key ] = true;
				} elseif ( $value === '0' || $value === 'false' ) {
					$sanitized[ $key ] = false;
				} else {
					$sanitized[ $key ] = sanitize_text_field( $value );
				}
			} else {
				$sanitized[ $key ] = $value;
			}
		}
		
		return $sanitized;
	}

	/** @param mixed $value Raw JSON string. @return string Sanitized gradient JSON. */
	private function sanitize_login_page_gradient( $value ): string {
		$default = '{"type":"linear","angle":135,"stops":[{"color":"#ffffff","pos":0},{"color":"#e8e8e8","pos":100}]}';
		if ( ! is_string( $value ) || $value === '' ) {
			return $default;
		}
		$data = json_decode( $value, true );
		if ( ! is_array( $data ) || empty( $data['stops'] ) || ! is_array( $data['stops'] ) ) {
			return $default;
		}
		$type  = ( isset( $data['type'] ) && ( $data['type'] === 'linear' || $data['type'] === 'radial' ) ) ? $data['type'] : 'linear';
		$angle = isset( $data['angle'] ) ? max( 0, min( 360, (int) $data['angle'] ) ) : 135;
		$stops = [];
		foreach ( $data['stops'] as $stop ) {
			if ( ! is_array( $stop ) || ! isset( $stop['color'] ) ) {
				continue;
			}
			$hex = is_string( $stop['color'] ) ? ltrim( trim( $stop['color'] ), '#' ) : '';
			if ( $hex === '' || ( ! preg_match( '/^[a-fA-F0-9]{6}$/', $hex ) && ! preg_match( '/^[a-fA-F0-9]{3}$/', $hex ) ) ) {
				$hex = 'ffffff';
			}
			$color = '#' . $hex;
			$pos   = isset( $stop['pos'] ) ? max( 0, min( 100, (int) $stop['pos'] ) ) : 0;
			$stops[] = [ 'color' => $color, 'pos' => $pos ];
		}
		if ( count( $stops ) < 2 ) {
			return $default;
		}
		$out = [ 'type' => $type, 'angle' => $angle, 'stops' => $stops ];
		$encoded = wp_json_encode( $out );
		return is_string( $encoded ) ? $encoded : $default;
	}

	/**
	 * Sanitize Header/Footer Code for header-footer-code module.
	 *
	 * Strips PHP, ASP, null bytes, control chars. Enforces length. Requires unfiltered_html.
	 *
	 * @since 1.0.0
	 * @param mixed $value Raw code from POST.
	 * @return string Sanitized code.
	 */
	private function sanitizeHeaderFooterCode( $value ): string {
		if ( ! is_string( $value ) ) {
			return '';
		}
		$this->ensureHeaderFooterCodeModuleLoaded();
		$value = wp_unslash( $value );
		$value = str_replace( "\0", '', $value );
		$value = preg_replace( '/<\?php.*?\?>/is', '', $value );
		$value = preg_replace( '/<\?.*?\?>/s', '', $value );
		$value = preg_replace( '/<\?=.*?\?>/s', '', $value );
		$value = preg_replace( '/<%.*?%>/s', '', $value );
		$value = preg_replace( '/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/', '', $value );
		$max   = \HeaderFooterCode\Module::getMaxCodeLength();
		if ( strlen( $value ) > $max ) {
			$value = substr( $value, 0, $max );
		}
		return $value;
	}

	public function loadModuleSettings(): void {
		$module_id = $this->getPostedModuleId();
		if ( ! $module_id ) {
			wp_send_json_error(
				[
					'message' => __( 'Invalid module ID provided.', 'wp-powersuite' ),
					'code'    => 'invalid_module_id',
				]
			);
			return;
		}

		if ( TemporarySessionGuard::isActive() && TemporarySessionGuard::blocksModule( $module_id ) ) {
			wp_send_json_error( TemporarySessionGuard::deniedPayload() );
			return;
		}

		if ( in_array( $module_id, [ 'wp-config-editor', 'quick-debug-toggle' ], true ) && ! WpConfigHelper::canManageWpConfig() ) {
			wp_send_json_error(
				[
					'message' => __( 'You do not have permission to access this module settings screen.', 'wp-powersuite' ),
					'code'    => 'insufficient_permissions',
				]
			);
			return;
		}

		$registry = \WP_PowerSuite\Core\ModuleRegistry::getInstance();
		$registry->init();
		$module_meta = $registry->getModuleMetadata( $module_id );

		// Resolve admin.php path: use _directory from registry, or fallback from plugin dir + module_id
		$admin_file = null;
		$module_dir = null;

		if ( is_array( $module_meta ) && ! empty( $module_meta['_directory'] ) ) {
			$candidate = $module_meta['_directory'] . DIRECTORY_SEPARATOR . 'admin.php';
			if ( file_exists( $candidate ) && is_readable( $candidate ) ) {
				$admin_file = $candidate;
				$module_dir = $module_meta['_directory'];
			}
		}

		if ( $admin_file === null && defined( 'WP_POWERSUITE_PLUGIN_DIR' ) && is_array( $module_meta ) && $module_meta !== [] && ( ! isset( $module_meta['_directory'] ) || $module_meta['_directory'] !== '' ) ) {
			$fallback_dir = WP_POWERSUITE_PLUGIN_DIR . 'modules' . DIRECTORY_SEPARATOR . $module_id;
			$fallback_file = $fallback_dir . DIRECTORY_SEPARATOR . 'admin.php';
			if ( file_exists( $fallback_file ) && is_readable( $fallback_file ) ) {
				$admin_file = $fallback_file;
				$module_dir = $fallback_dir;
				if ( ! isset( $module_meta['_directory'] ) ) {
					$module_meta['_directory'] = $fallback_dir;
					$module_meta['class']      = $module_meta['class'] ?? '';
				}
			}
		}

		if ( $admin_file === null || $module_dir === null ) {
			wp_send_json_error(
				[
					'message' => $module_meta === null ? __( 'Module not found.', 'wp-powersuite' ) : __( 'Module settings file not found.', 'wp-powersuite' ),
					'code'    => $admin_file === null ? 'settings_file_not_found' : 'module_not_found',
				]
			);
			return;
		}

		$module_meta['_directory'] = $module_dir;

		if ( ! function_exists( 'get_editable_roles' ) ) {
			require_once ABSPATH . 'wp-admin/includes/user.php';
		}

		$module_class = $module_meta['class'] ?? '';
		if ( ! empty( $module_class ) && ! class_exists( $module_class ) ) {
			$dir = $module_meta['_directory'];
			$module_file_capital = $dir . DIRECTORY_SEPARATOR . 'Module.php';
			$module_file_lower   = $dir . DIRECTORY_SEPARATOR . 'module.php';
			if ( file_exists( $module_file_capital ) && is_readable( $module_file_capital ) ) {
				require_once $module_file_capital;
			} elseif ( file_exists( $module_file_lower ) && is_readable( $module_file_lower ) ) {
				require_once $module_file_lower;
			}
		}

		// Capture output
		ob_start();
		
		try {
			// Make variables available to admin.php
			$module_id   = $module_id;
			$module_meta = $module_meta;

			$module_settings_ui = WP_POWERSUITE_SRC_DIR . 'Admin' . DIRECTORY_SEPARATOR . 'ModuleSettingsUi.php';
			if ( is_readable( $module_settings_ui ) ) {
				require_once $module_settings_ui;
			}

			include $admin_file;
			$html = ob_get_clean();
			
			if ( $html === false ) {
				throw new \Exception( 'Failed to capture output from admin.php' );
			}
		} catch ( \Throwable $e ) {
			ob_end_clean();
			wp_send_json_error(
				[
					'message' => __( 'Error loading module settings: ', 'wp-powersuite' ) . $e->getMessage(),
					'code'    => 'include_error',
				]
			);
			return;
		}

		wp_send_json_success(
			[
				'module_id' => $module_id,
				'html'      => $html,
			]
		);
	}
	/**
	 * Load Header & Footer Code module class in admin/AJAX (module runtime is frontend-only).
	 */
	private function ensureHeaderFooterCodeModuleLoaded(): void {
		if ( class_exists( \HeaderFooterCode\Module::class, false ) ) {
			return;
		}
		if ( ! defined( 'WP_POWERSUITE_MODULES_DIR' ) ) {
			return;
		}
		$mod = WP_POWERSUITE_MODULES_DIR . 'header-footer-code' . DIRECTORY_SEPARATOR . 'module.php';
		if ( is_readable( $mod ) ) {
			require_once $mod;
		}
	}

	private function sanitizeRedirectDestinationUrl( string $url ): string {
		$url = trim( $url );
		if ( $url === '' ) {
			return '';
		}
		$url = esc_url_raw( $url );
		if ( $url === '' ) {
			return '';
		}
		$validated = wp_validate_redirect( $url, false );
		return ( is_string( $validated ) && $validated !== '' ) ? $validated : '';
	}

}
