<?php
/**
 * Login Screen Customizer
 *
 * Allows administrators to customize the WordPress login screen through the
 * Guard Dog visual customizer.
 *
 * @package GuardDog
 * @since 1.9.40
 */

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

class Guard_Dog_Login_Screen_Customizer extends Guard_Dog_Customizer {

	/**
	 * Get unique ID for this customizer.
	 *
	 * @return string
	 */
	public function get_customizer_id() {
		return 'login_screen';
	}

	/**
	 * Get title for this customizer.
	 *
	 * @return string
	 */
	public function get_customizer_title() {
		return __( 'WordPress Login Screen Customizer', 'guard-dog' );
	}

	/**
	 * Get description for this customizer.
	 *
	 * @return string
	 */
	public function get_customizer_description() {
		return __( 'Customize the native WordPress login experience used by wp-login.php, your custom login URL, password reset, and Guard Dog login security screens.', 'guard-dog' );
	}

	/**
	 * Register sections and controls.
	 */
	protected function register_controls() {
		$this->add_section(
			'status',
			__( 'Status', 'guard-dog' ),
			array(
				'description' => __( 'Turn the custom login page styling on when you are ready to use it.', 'guard-dog' ),
				'priority'    => 5,
			)
		);

		$this->add_control(
			'enabled',
			'checkbox',
			'status',
			array(
				'checkbox_label' => __( 'Enable custom login styling', 'guard-dog' ),
				'description'    => __( 'When disabled, Guard Dog leaves the WordPress login page styling untouched.', 'guard-dog' ),
				'default'        => false,
			)
		);

		$this->add_section(
			'branding',
			__( 'Branding', 'guard-dog' ),
			array(
				'description' => __( 'Swap the WordPress logo for your own and control where it links.', 'guard-dog' ),
				'priority'    => 10,
			)
		);

		$this->add_control(
			'logo',
			'image',
			'branding',
			array(
				'label'        => __( 'Logo', 'guard-dog' ),
				'description'  => __( 'Upload a logo for the top of the login form. Leave empty to keep the default WordPress mark.', 'guard-dog' ),
				'storage_type' => 'url',
				'default'      => '',
			)
		);

		$this->add_control(
			'logo_width',
			'number',
			'branding',
			array(
				'label'       => __( 'Logo Width (px)', 'guard-dog' ),
				'description' => __( 'Maximum rendered width for your login logo.', 'guard-dog' ),
				'default'     => 180,
				'min'         => 40,
				'max'         => 420,
				'step'        => 5,
			)
		);

		$this->add_control(
			'logo_height',
			'number',
			'branding',
			array(
				'label'       => __( 'Logo Height (px)', 'guard-dog' ),
				'description' => __( 'Maximum rendered height for your login logo.', 'guard-dog' ),
				'default'     => 80,
				'min'         => 30,
				'max'         => 240,
				'step'        => 5,
			)
		);

		$this->add_control(
			'logo_link_url',
			'text',
			'branding',
			array(
				'label'       => __( 'Logo Link URL', 'guard-dog' ),
				'description' => __( 'Where the login logo should send visitors.', 'guard-dog' ),
				'default'     => home_url( '/' ),
				'input_type'  => 'url',
				'placeholder' => home_url( '/' ),
			)
		);

		$this->add_control(
			'logo_alt_text',
			'text',
			'branding',
			array(
				'label'       => __( 'Logo Screen Reader Text', 'guard-dog' ),
				'description' => __( 'Accessible text for the logo link.', 'guard-dog' ),
				'default'     => get_bloginfo( 'name' ),
				'placeholder' => get_bloginfo( 'name' ),
			)
		);

		$this->add_control(
			'logo_bottom_spacing',
			'number',
			'branding',
			array(
				'label'       => __( 'Logo Bottom Spacing (px)', 'guard-dog' ),
				'description' => __( 'Space between the logo and the content below it.', 'guard-dog' ),
				'default'     => 24,
				'min'         => 0,
				'max'         => 72,
				'step'        => 1,
			)
		);

		$this->add_section(
			'background',
			__( 'Page Background', 'guard-dog' ),
			array(
				'description' => __( 'Set the full-page backdrop behind the login form.', 'guard-dog' ),
				'priority'    => 20,
			)
		);

		$this->add_control(
			'background_color',
			'color',
			'background',
			array(
				'label'       => __( 'Background Color', 'guard-dog' ),
				'description' => __( 'Fallback background color for the login page.', 'guard-dog' ),
				'default'     => '#f0f0f1',
			)
		);

		$this->add_control(
			'background_image',
			'image',
			'background',
			array(
				'label'        => __( 'Background Image', 'guard-dog' ),
				'description'  => __( 'Optional full-page background image. It will be centered and cover the screen.', 'guard-dog' ),
				'storage_type' => 'url',
				'default'      => '',
			)
		);

		$this->add_control(
			'background_overlay_color',
			'color',
			'background',
			array(
				'label'       => __( 'Background Overlay Color', 'guard-dog' ),
				'description' => __( 'Optional tint layered over the page background.', 'guard-dog' ),
				'default'     => '#000000',
			)
		);

		$this->add_control(
			'background_overlay_opacity',
			'number',
			'background',
			array(
				'label'       => __( 'Background Overlay Opacity (%)', 'guard-dog' ),
				'description' => __( 'Use 0 for no overlay and higher values for stronger tinting.', 'guard-dog' ),
				'default'     => 0,
				'min'         => 0,
				'max'         => 90,
				'step'        => 5,
			)
		);

		$this->add_control(
			'content_top_spacing',
			'number',
			'background',
			array(
				'label'       => __( 'Top Spacing (vh)', 'guard-dog' ),
				'description' => __( 'Controls how far down the login panel starts on the page.', 'guard-dog' ),
				'default'     => 7,
				'min'         => 0,
				'max'         => 20,
				'step'        => 1,
			)
		);

		$this->add_section(
			'form',
			__( 'Form Container', 'guard-dog' ),
			array(
				'description' => __( 'Control the login form surface, width, corners, and shadow.', 'guard-dog' ),
				'priority'    => 30,
			)
		);

		$this->add_control(
			'form_width',
			'number',
			'form',
			array(
				'label'       => __( 'Form Width (px)', 'guard-dog' ),
				'description' => __( 'Maximum width of the login form area.', 'guard-dog' ),
				'default'     => 360,
				'min'         => 300,
				'max'         => 640,
				'step'        => 10,
			)
		);

		$this->add_control(
			'form_padding',
			'number',
			'form',
			array(
				'label'       => __( 'Form Padding (px)', 'guard-dog' ),
				'description' => __( 'Interior spacing inside the login form card.', 'guard-dog' ),
				'default'     => 26,
				'min'         => 16,
				'max'         => 48,
				'step'        => 1,
			)
		);

		$this->add_control(
			'form_background',
			'color',
			'form',
			array(
				'label'       => __( 'Form Background', 'guard-dog' ),
				'description' => __( 'Background color for the form card.', 'guard-dog' ),
				'default'     => '#ffffff',
			)
		);

		$this->add_control(
			'form_text_color',
			'color',
			'form',
			array(
				'label'       => __( 'Form Text Color', 'guard-dog' ),
				'description' => __( 'Primary text color inside the form.', 'guard-dog' ),
				'default'     => '#1d2327',
			)
		);

		$this->add_control(
			'form_border_color',
			'color',
			'form',
			array(
				'label'       => __( 'Form Border Color', 'guard-dog' ),
				'description' => __( 'Border color for the login form.', 'guard-dog' ),
				'default'     => '#c3c4c7',
			)
		);

		$this->add_control(
			'form_border_radius',
			'number',
			'form',
			array(
				'label'       => __( 'Form Border Radius (px)', 'guard-dog' ),
				'description' => __( 'Rounded corners for the form card and related notices.', 'guard-dog' ),
				'default'     => 8,
				'min'         => 0,
				'max'         => 30,
				'step'        => 1,
			)
		);

		$this->add_control(
			'form_shadow',
			'checkbox',
			'form',
			array(
				'checkbox_label' => __( 'Show form shadow', 'guard-dog' ),
				'description'    => __( 'Adds subtle depth behind the login form.', 'guard-dog' ),
				'default'        => true,
			)
		);

		$this->add_section(
			'controls',
			__( 'Fields & Button', 'guard-dog' ),
			array(
				'description' => __( 'Style text fields, focus states, and the primary login button.', 'guard-dog' ),
				'priority'    => 40,
			)
		);

		$this->add_control(
			'input_border_color',
			'color',
			'controls',
			array(
				'label'       => __( 'Field Border Color', 'guard-dog' ),
				'description' => __( 'Border color for username, email, and password fields.', 'guard-dog' ),
				'default'     => '#8c8f94',
			)
		);

		$this->add_control(
			'input_focus_color',
			'color',
			'controls',
			array(
				'label'       => __( 'Field Focus Color', 'guard-dog' ),
				'description' => __( 'Highlight color when a field or button receives focus.', 'guard-dog' ),
				'default'     => '#2271b1',
			)
		);

		$this->add_control(
			'button_background',
			'color',
			'controls',
			array(
				'label'       => __( 'Button Background', 'guard-dog' ),
				'description' => __( 'Primary login button background color.', 'guard-dog' ),
				'default'     => '#2271b1',
			)
		);

		$this->add_control(
			'button_text_color',
			'color',
			'controls',
			array(
				'label'       => __( 'Button Text Color', 'guard-dog' ),
				'description' => __( 'Primary login button text color.', 'guard-dog' ),
				'default'     => '#ffffff',
			)
		);

		$this->add_control(
			'button_hover_background',
			'color',
			'controls',
			array(
				'label'       => __( 'Button Hover Background', 'guard-dog' ),
				'description' => __( 'Primary login button hover and focus color.', 'guard-dog' ),
				'default'     => '#135e96',
			)
		);

		$this->add_section(
			'content',
			__( 'Content & Links', 'guard-dog' ),
			array(
				'description' => __( 'Control optional helper text and the links below the form.', 'guard-dog' ),
				'priority'    => 50,
			)
		);

		$this->add_control(
			'login_message',
			'textarea',
			'content',
			array(
				'label'       => __( 'Default Message Above Form', 'guard-dog' ),
				'description' => __( 'Optional message shown above the standard login form and used as a fallback for states without their own message. Basic HTML is allowed.', 'guard-dog' ),
				'default'     => '',
				'rows'        => 3,
				'allow_html'  => true,
			)
		);

		$this->add_control(
			'footer_text',
			'textarea',
			'content',
			array(
				'label'       => __( 'Footer Text', 'guard-dog' ),
				'description' => __( 'Optional small text shown below the login screen.', 'guard-dog' ),
				'default'     => '',
				'rows'        => 2,
				'allow_html'  => true,
			)
		);

		$this->add_control(
			'link_color',
			'color',
			'content',
			array(
				'label'       => __( 'Link Color', 'guard-dog' ),
				'description' => __( 'Color for navigation, privacy, and language links.', 'guard-dog' ),
				'default'     => '#50575e',
			)
		);

		$this->add_control(
			'link_hover_color',
			'color',
			'content',
			array(
				'label'       => __( 'Link Hover Color', 'guard-dog' ),
				'description' => __( 'Hover and focus color for login page links.', 'guard-dog' ),
				'default'     => '#135e96',
			)
		);

		$this->add_section(
			'state_messages',
			__( 'State Messages', 'guard-dog' ),
			array(
				'description' => __( 'Add optional helper text for specific WordPress and Guard Dog login states.', 'guard-dog' ),
				'priority'    => 60,
			)
		);

		$this->add_control(
			'lost_password_message',
			'textarea',
			'state_messages',
			array(
				'label'       => __( 'Lost Password Message', 'guard-dog' ),
				'description' => __( 'Shown above the password recovery form. Leave blank to use the default message fallback.', 'guard-dog' ),
				'default'     => '',
				'rows'        => 3,
				'allow_html'  => true,
			)
		);

		$this->add_control(
			'reset_password_message',
			'textarea',
			'state_messages',
			array(
				'label'       => __( 'Reset Password Message', 'guard-dog' ),
				'description' => __( 'Shown above the password reset form. Leave blank to use the default message fallback.', 'guard-dog' ),
				'default'     => '',
				'rows'        => 3,
				'allow_html'  => true,
			)
		);

		$this->add_control(
			'two_factor_message',
			'textarea',
			'state_messages',
			array(
				'label'       => __( 'Two-Factor Challenge Message', 'guard-dog' ),
				'description' => __( 'Shown when a user must complete an existing 2FA challenge.', 'guard-dog' ),
				'default'     => '',
				'rows'        => 3,
				'allow_html'  => true,
			)
		);

		$this->add_control(
			'two_factor_setup_message',
			'textarea',
			'state_messages',
			array(
				'label'       => __( 'Two-Factor Setup Message', 'guard-dog' ),
				'description' => __( 'Shown when 2FA setup is required before login can continue.', 'guard-dog' ),
				'default'     => '',
				'rows'        => 3,
				'allow_html'  => true,
			)
		);

		$this->add_control(
			'password_expired_message',
			'textarea',
			'state_messages',
			array(
				'label'       => __( 'Expired Password Message', 'guard-dog' ),
				'description' => __( 'Shown when a user must create a new password before continuing.', 'guard-dog' ),
				'default'     => '',
				'rows'        => 3,
				'allow_html'  => true,
			)
		);

		$this->add_section(
			'notices',
			__( 'Notices & Errors', 'guard-dog' ),
			array(
				'description' => __( 'Style login notices and errors without changing what security messages say.', 'guard-dog' ),
				'priority'    => 70,
			)
		);

		$this->add_control(
			'notice_background',
			'color',
			'notices',
			array(
				'label'       => __( 'Notice Background', 'guard-dog' ),
				'description' => __( 'Background color for informational login notices.', 'guard-dog' ),
				'default'     => '#ffffff',
			)
		);

		$this->add_control(
			'notice_border_color',
			'color',
			'notices',
			array(
				'label'       => __( 'Notice Accent Color', 'guard-dog' ),
				'description' => __( 'Left border color for informational login notices.', 'guard-dog' ),
				'default'     => '#72aee6',
			)
		);

		$this->add_control(
			'notice_text_color',
			'color',
			'notices',
			array(
				'label'       => __( 'Notice Text Color', 'guard-dog' ),
				'description' => __( 'Text color for informational login notices.', 'guard-dog' ),
				'default'     => '#1d2327',
			)
		);

		$this->add_control(
			'error_background',
			'color',
			'notices',
			array(
				'label'       => __( 'Error Background', 'guard-dog' ),
				'description' => __( 'Background color for login error notices.', 'guard-dog' ),
				'default'     => '#ffffff',
			)
		);

		$this->add_control(
			'error_border_color',
			'color',
			'notices',
			array(
				'label'       => __( 'Error Accent Color', 'guard-dog' ),
				'description' => __( 'Left border color for login error notices.', 'guard-dog' ),
				'default'     => '#d63638',
			)
		);

		$this->add_control(
			'error_text_color',
			'color',
			'notices',
			array(
				'label'       => __( 'Error Text Color', 'guard-dog' ),
				'description' => __( 'Text color for login error notices.', 'guard-dog' ),
				'default'     => '#1d2327',
			)
		);

		$this->add_section(
			'action_links',
			__( 'Action Links', 'guard-dog' ),
			array(
				'description' => __( 'Control helper links around the native login form.', 'guard-dog' ),
				'priority'    => 80,
			)
		);

		$this->add_control(
			'support_link_text',
			'text',
			'action_links',
			array(
				'label'       => __( 'Support Link Text', 'guard-dog' ),
				'description' => __( 'Optional help link shown below the login form.', 'guard-dog' ),
				'default'     => '',
				'placeholder' => __( 'Need help signing in?', 'guard-dog' ),
			)
		);

		$this->add_control(
			'support_link_url',
			'text',
			'action_links',
			array(
				'label'       => __( 'Support Link URL', 'guard-dog' ),
				'description' => __( 'Destination for the optional support link.', 'guard-dog' ),
				'default'     => '',
				'input_type'  => 'url',
				'placeholder' => home_url( '/contact/' ),
			)
		);

		$this->add_control(
			'lost_password_link_text',
			'text',
			'action_links',
			array(
				'label'       => __( 'Lost Password Link Text', 'guard-dog' ),
				'description' => __( 'Optional replacement text for the password recovery link.', 'guard-dog' ),
				'default'     => '',
				'placeholder' => __( 'Lost your password?', 'guard-dog' ),
			)
		);

		$this->add_control(
			'privacy_link_text',
			'text',
			'action_links',
			array(
				'label'       => __( 'Privacy Link Text', 'guard-dog' ),
				'description' => __( 'Optional replacement text for the privacy policy link.', 'guard-dog' ),
				'default'     => '',
				'placeholder' => __( 'Privacy Policy', 'guard-dog' ),
			)
		);

		$this->add_control(
			'back_to_site_text',
			'text',
			'action_links',
				array(
					'label'       => __( 'Back to Site Link Text', 'guard-dog' ),
					'description' => __( 'Optional replacement text for the homepage link.', 'guard-dog' ),
					'default'     => '',
					// Translators: %s is the site name.
					'placeholder' => sprintf( __( 'Go to %s', 'guard-dog' ), get_bloginfo( 'name' ) ),
				)
			);

		$this->add_control(
			'hide_lost_password_link',
			'checkbox',
			'action_links',
			array(
				'checkbox_label' => __( 'Hide lost password link', 'guard-dog' ),
				'description'    => __( 'Removes the password recovery link from the login navigation.', 'guard-dog' ),
				'default'        => false,
			)
		);

		$this->add_control(
			'hide_register_link',
			'checkbox',
			'action_links',
			array(
				'checkbox_label' => __( 'Hide registration link', 'guard-dog' ),
				'description'    => __( 'Removes the registration link from the login navigation when registration is enabled.', 'guard-dog' ),
				'default'        => false,
			)
		);

		$this->add_control(
			'hide_back_to_site',
			'checkbox',
			'action_links',
			array(
				'checkbox_label' => __( 'Hide "Back to site" link', 'guard-dog' ),
				'description'    => __( 'Removes the link that points visitors back to the homepage.', 'guard-dog' ),
				'default'        => false,
			)
		);

		$this->add_control(
			'hide_privacy_link',
			'checkbox',
			'action_links',
			array(
				'checkbox_label' => __( 'Hide privacy policy link', 'guard-dog' ),
				'description'    => __( 'Visually hides the privacy policy link on the login screen.', 'guard-dog' ),
				'default'        => false,
			)
		);

		$this->add_control(
			'hide_language_switcher',
			'checkbox',
			'action_links',
			array(
				'checkbox_label' => __( 'Hide language switcher', 'guard-dog' ),
				'description'    => __( 'Disables the WordPress language selector on the login screen.', 'guard-dog' ),
				'default'        => false,
			)
		);
	}

	/**
	 * Initialize customizer and login hooks.
	 */
	protected function init_hooks() {
		parent::init_hooks();

		if ( is_admin() || wp_doing_ajax() ) {
			return;
		}

		add_action( 'login_head', array( $this, 'print_login_styles' ), 100 );
		add_filter( 'login_headerurl', array( $this, 'filter_login_header_url' ) );
		add_filter( 'login_headertext', array( $this, 'filter_login_header_text' ) );
		add_filter( 'login_body_class', array( $this, 'filter_login_body_class' ), 10, 2 );
		add_filter( 'login_display_language_dropdown', array( $this, 'filter_language_dropdown' ) );
		add_filter( 'login_site_html_link', array( $this, 'filter_site_link' ) );
		add_filter( 'login_message', array( $this, 'filter_login_message' ) );
		add_filter( 'gettext', array( $this, 'filter_login_link_text' ), 10, 3 );
		add_action( 'login_footer', array( $this, 'render_action_link_adjustments' ), 18 );
		add_action( 'login_footer', array( $this, 'render_support_link' ), 19 );
		add_action( 'login_footer', array( $this, 'render_footer_text' ), 20 );
	}

	/**
	 * Check whether login screen customization is enabled.
	 *
	 * @param array|null $settings Optional settings override.
	 * @return bool
	 */
	public function is_enabled( $settings = null ) {
		$settings = $this->get_effective_settings( $settings );

		return $this->to_bool( $settings['enabled'] );
	}

	/**
	 * Output login screen CSS on wp-login.php.
	 */
	public function print_login_styles() {
		if ( ! $this->is_enabled() ) {
			return;
		}

		$css = $this->get_login_css();
		if ( '' === trim( $css ) ) {
			return;
		}

		echo "\n<style id=\"guard-dog-login-screen-customizer\" type=\"text/css\">\n";
		// The CSS is composed from sanitized scalar settings in get_effective_settings().
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo $css;
		echo "\n</style>\n";
	}

	/**
	 * Filter the login logo URL.
	 *
	 * @param string $url Original URL.
	 * @return string
	 */
	public function filter_login_header_url( $url ) {
		if ( ! $this->is_enabled() ) {
			return $url;
		}

		$settings = $this->get_effective_settings();

		return $settings['logo_link_url'] ?: home_url( '/' );
	}

	/**
	 * Filter the accessible login logo text.
	 *
	 * @param string $text Original text.
	 * @return string
	 */
	public function filter_login_header_text( $text ) {
		if ( ! $this->is_enabled() ) {
			return $text;
		}

		$settings = $this->get_effective_settings();

		return $settings['logo_alt_text'] ?: get_bloginfo( 'name' );
	}

	/**
	 * Add a body class when the customizer is enabled.
	 *
	 * @param string[] $classes Existing body classes.
	 * @param string   $action  Login action.
	 * @return string[]
	 */
	public function filter_login_body_class( $classes, $action = '' ) {
		unset( $action );

		if ( ! $this->is_enabled() ) {
			return $classes;
		}

		return $this->get_login_body_classes( $classes, $this->get_effective_settings() );
	}

	/**
	 * Optionally hide the language dropdown.
	 *
	 * @param bool $display Whether WordPress should display the dropdown.
	 * @return bool
	 */
	public function filter_language_dropdown( $display ) {
		if ( ! $this->is_enabled() ) {
			return $display;
		}

		$settings = $this->get_effective_settings();

		return $this->to_bool( $settings['hide_language_switcher'] ) ? false : $display;
	}

	/**
	 * Optionally remove the "Back to site" link markup.
	 *
	 * @param string $link Link markup.
	 * @return string
	 */
	public function filter_site_link( $link ) {
		if ( ! $this->is_enabled() ) {
			return $link;
		}

		$settings = $this->get_effective_settings();

		if ( $this->to_bool( $settings['hide_back_to_site'] ) ) {
			return '';
		}

		$back_to_site_text = trim( (string) $settings['back_to_site_text'] );

		if ( '' === $back_to_site_text ) {
			return $link;
		}

		return sprintf(
			'<a href="%1$s">%2$s</a>',
			esc_url( home_url( '/' ) ),
			esc_html( $back_to_site_text )
		);
	}

	/**
	 * Add an optional message above the login form.
	 *
	 * @param string $message Existing login message markup.
	 * @return string
	 */
	public function filter_login_message( $message ) {
		if ( ! $this->is_enabled() ) {
			return $message;
		}

		$settings      = $this->get_effective_settings();
		$login_message = $this->get_message_for_current_login_state( $settings );

		if ( '' === $login_message ) {
			return $message;
		}

		return '<div class="message guard-dog-login-message">' . wpautop( wp_kses_post( $login_message ) ) . '</div>' . $message;
	}

	/**
	 * Filter selected login action link text without changing security/error messages.
	 *
	 * @param string $translation Translated text.
	 * @param string $text        Original text.
	 * @param string $domain      Text domain.
	 * @return string
	 */
	public function filter_login_link_text( $translation, $text, $domain ) {
		unset( $domain );

		if ( ! $this->is_enabled() ) {
			return $translation;
		}

		$settings = $this->get_effective_settings();

		if ( 'Lost your password?' === $text && '' !== trim( (string) $settings['lost_password_link_text'] ) ) {
			return $settings['lost_password_link_text'];
		}

		if ( 'Privacy Policy' === $text && '' !== trim( (string) $settings['privacy_link_text'] ) ) {
			return $settings['privacy_link_text'];
		}

		return $translation;
	}

	/**
	 * Clean up the native login navigation when individual action links are hidden.
	 */
	public function render_action_link_adjustments() {
		if ( ! $this->is_enabled() ) {
			return;
		}

		$settings      = $this->get_effective_settings();
		$hide_lost     = $this->to_bool( $settings['hide_lost_password_link'] );
		$hide_register = $this->to_bool( $settings['hide_register_link'] );

		if ( ! $hide_lost && ! $hide_register ) {
			return;
		}

		?>
		<script>
		(function() {
			var nav = document.getElementById('nav');

			if (!nav) {
				return;
			}

			var hideLost = <?php echo wp_json_encode( $hide_lost ); ?>;
			var hideRegister = <?php echo wp_json_encode( $hide_register ); ?>;
			var links = Array.prototype.slice.call(nav.querySelectorAll('a')).filter(function(link) {
				var href = link.getAttribute('href') || '';

				if (hideLost && href.indexOf('action=lostpassword') !== -1) {
					return false;
				}

				if (hideRegister && href.indexOf('action=register') !== -1) {
					return false;
				}

				return true;
			});

			nav.textContent = '';

			if (!links.length) {
				nav.style.display = 'none';
				return;
			}

			links.forEach(function(link, index) {
				if (index > 0) {
					nav.appendChild(document.createTextNode(' | '));
				}

				nav.appendChild(link);
			});
		})();
		</script>
		<?php
	}

	/**
	 * Render an optional support/help link below the login screen.
	 */
	public function render_support_link() {
		if ( ! $this->is_enabled() ) {
			return;
		}

		$settings          = $this->get_effective_settings();
		$support_link_text = trim( (string) $settings['support_link_text'] );
		$support_link_url  = trim( (string) $settings['support_link_url'] );

		if ( '' === $support_link_text || '' === $support_link_url ) {
			return;
		}

		printf(
			'<p class="guard-dog-login-support-link"><a href="%1$s">%2$s</a></p>',
			esc_url( $support_link_url ),
			esc_html( $support_link_text )
		);
	}

	/**
	 * Render optional footer text on the login page.
	 */
	public function render_footer_text() {
		if ( ! $this->is_enabled() ) {
			return;
		}

		$settings    = $this->get_effective_settings();
		$footer_text = trim( (string) $settings['footer_text'] );

		if ( '' === $footer_text ) {
			return;
		}

		echo '<div class="guard-dog-login-footer-text">';
		echo wp_kses_post( wpautop( $footer_text ) );
		echo '</div>';
	}

	/**
	 * Get the configured helper message for the active login state.
	 *
	 * @param array $settings Effective settings.
	 * @return string
	 */
	private function get_message_for_current_login_state( array $settings ) {
		$state = $this->get_current_login_state();
		$map   = array(
			'lost_password'    => 'lost_password_message',
			'reset_password'   => 'reset_password_message',
			'two_factor'       => 'two_factor_message',
			'two_factor_setup' => 'two_factor_setup_message',
			'password_expired' => 'password_expired_message',
		);

		if ( isset( $map[ $state ] ) && '' !== trim( (string) $settings[ $map[ $state ] ] ) ) {
			return trim( (string) $settings[ $map[ $state ] ] );
		}

		return trim( (string) $settings['login_message'] );
	}

	/**
	 * Determine which native login state is currently rendering.
	 *
	 * @return string
	 */
	private function get_current_login_state() {
		if ( ! empty( $GLOBALS['guard_dog_login_screen_state'] ) ) {
			return sanitize_key( $GLOBALS['guard_dog_login_screen_state'] );
		}

		global $action;

		switch ( $action ) {
			case 'lostpassword':
			case 'retrievepassword':
				return 'lost_password';

			case 'rp':
			case 'resetpass':
				return 'reset_password';

			case 'guard_dog_2fa_setup':
				return 'two_factor_setup';

			default:
				return 'login';
		}
	}

	/**
	 * Generate preview HTML output based on settings.
	 *
	 * @param array $settings Settings to use for generation.
	 * @return string
	 */
	public function generate_output( $settings ) {
		$settings   = $this->get_effective_settings( $settings );
		$is_enabled = $this->is_enabled( $settings );
		$classes    = array( 'login', 'login-action-login', 'wp-core-ui', 'admin-color-modern', 'locale-en-us' );

		if ( $is_enabled ) {
			$classes = $this->get_login_body_classes( $classes, $settings );
		}

		$logo_text     = $is_enabled ? ( $settings['logo_alt_text'] ?: get_bloginfo( 'name' ) ) : __( 'Powered by WordPress', 'guard-dog' );
		$logo_url      = $is_enabled ? ( $settings['logo_link_url'] ?: home_url( '/' ) ) : __( 'https://wordpress.org/', 'guard-dog' );
		$site_name     = get_bloginfo( 'name' );
		$login_message = $is_enabled ? trim( (string) $settings['login_message'] ) : '';
		$footer_text   = $is_enabled ? trim( (string) $settings['footer_text'] ) : '';
		$support_text  = $is_enabled ? trim( (string) $settings['support_link_text'] ) : '';
		$support_url   = $is_enabled ? trim( (string) $settings['support_link_url'] ) : '';
		$lost_text     = $is_enabled && '' !== trim( (string) $settings['lost_password_link_text'] ) ? trim( (string) $settings['lost_password_link_text'] ) : __( 'Lost your password?', 'guard-dog' );
		$privacy_text  = $is_enabled && '' !== trim( (string) $settings['privacy_link_text'] ) ? trim( (string) $settings['privacy_link_text'] ) : __( 'Privacy Policy', 'guard-dog' );
		// Translators: %s is the site name.
		$back_text     = $is_enabled && '' !== trim( (string) $settings['back_to_site_text'] ) ? trim( (string) $settings['back_to_site_text'] ) : sprintf( __( 'Go to %s', 'guard-dog' ), $site_name );

		ob_start();
		?>
		<!DOCTYPE html>
		<html lang="en">
		<head>
			<meta charset="UTF-8">
			<meta name="viewport" content="width=device-width, initial-scale=1.0">
			<title><?php echo esc_html(
				sprintf(
					/* Translators: %s is the site name. */
					__( 'Log In - %s - WordPress', 'guard-dog' ),
					$site_name
				)
			); ?></title>
			<style type="text/css">
				<?php
				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				echo $this->get_preview_base_css();
				if ( $is_enabled ) {
					// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					echo $this->get_login_css( $settings );
				}
				?>
			</style>
		</head>
		<body class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
			<div id="login">
				<h1 role="presentation" class="wp-login-logo">
					<a href="<?php echo esc_url( $logo_url ); ?>"><?php echo esc_html( $logo_text ); ?></a>
				</h1>

				<?php if ( '' !== $login_message ) : ?>
					<div class="message guard-dog-login-message">
						<?php echo wp_kses_post( wpautop( $login_message ) ); ?>
					</div>
				<?php endif; ?>

				<form name="loginform" id="loginform" action="#" method="post">
					<p>
						<label for="user_login"><?php esc_html_e( 'Username or Email Address', 'guard-dog' ); ?></label>
						<input type="text" name="log" id="user_login" class="input" value="" size="20" autocomplete="username">
					</p>
					<div class="user-pass-wrap">
						<label for="user_pass"><?php esc_html_e( 'Password', 'guard-dog' ); ?></label>
						<div class="wp-pwd">
							<input type="password" name="pwd" id="user_pass" class="input password-input" value="" size="20" autocomplete="current-password">
						</div>
					</div>
					<p class="forgetmenot">
						<input name="rememberme" type="checkbox" id="rememberme" value="forever">
						<label for="rememberme"><?php esc_html_e( 'Remember Me', 'guard-dog' ); ?></label>
					</p>
					<p class="submit">
						<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php esc_attr_e( 'Log In', 'guard-dog' ); ?>">
					</p>
				</form>

				<?php if ( ! $is_enabled || ! $this->to_bool( $settings['hide_lost_password_link'] ) ) : ?>
					<p id="nav">
						<a href="#"><?php echo esc_html( $lost_text ); ?></a>
					</p>
				<?php endif; ?>

				<?php if ( ! $is_enabled || ! $this->to_bool( $settings['hide_back_to_site'] ) ) : ?>
					<p id="backtoblog"><a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php echo esc_html( $back_text ); ?></a></p>
				<?php endif; ?>

				<?php if ( ! $is_enabled || ! $this->to_bool( $settings['hide_privacy_link'] ) ) : ?>
					<div class="privacy-policy-page-link"><a href="#"><?php echo esc_html( $privacy_text ); ?></a></div>
				<?php endif; ?>
			</div>

			<?php if ( '' !== $support_text && '' !== $support_url ) : ?>
				<p class="guard-dog-login-support-link"><a href="<?php echo esc_url( $support_url ); ?>"><?php echo esc_html( $support_text ); ?></a></p>
			<?php endif; ?>

			<?php if ( ! $is_enabled || ! $this->to_bool( $settings['hide_language_switcher'] ) ) : ?>
				<div class="language-switcher">
					<form id="language-switcher" method="get">
						<label for="language-switcher-locales">
							<span class="dashicons dashicons-translation" aria-hidden="true"></span>
							<span class="screen-reader-text"><?php esc_html_e( 'Language', 'guard-dog' ); ?></span>
						</label>
						<select id="language-switcher-locales" name="wp_lang">
							<option>English (United States)</option>
						</select>
						<input type="submit" class="button" value="<?php esc_attr_e( 'Change', 'guard-dog' ); ?>">
					</form>
				</div>
			<?php endif; ?>

			<?php if ( '' !== $footer_text ) : ?>
				<div class="guard-dog-login-footer-text">
					<?php echo wp_kses_post( wpautop( $footer_text ) ); ?>
				</div>
			<?php endif; ?>
		</body>
		</html>
		<?php

		return ob_get_clean();
	}

	/**
	 * Get generated login CSS.
	 *
	 * @param array|null $settings Optional settings.
	 * @return string
	 */
	public function get_login_css( $settings = null ) {
		$settings = $this->get_effective_settings( $settings );

		$logo_css       = '';
		$background_css = '';
		$form_shadow    = $this->to_bool( $settings['form_shadow'] ) ? '0 18px 45px rgba(0, 0, 0, 0.12)' : 'none';
		$field_radius   = min( 12, max( 0, (int) floor( $settings['form_border_radius'] / 2 ) ) );

		if ( ! empty( $settings['logo'] ) ) {
			$logo_css = sprintf(
				'
body.login.guard-dog-login-customized h1 a {
	background-image: url("%1$s");
	background-size: contain;
	background-position: center center;
	background-repeat: no-repeat;
	width: %2$dpx;
	height: %3$dpx;
	max-width: 100%%;
	text-indent: -9999px;
	overflow: hidden;
	display: block;
}',
				esc_url( $settings['logo'] ),
				(int) $settings['logo_width'],
				(int) $settings['logo_height']
			);
		}

		if ( ! empty( $settings['background_image'] ) ) {
			$background_css = sprintf(
				'
body.login.guard-dog-login-customized {
	background-image: url("%s");
	background-size: cover;
	background-position: center center;
	background-repeat: no-repeat;
	background-attachment: fixed;
}',
				esc_url( $settings['background_image'] )
			);
		}

		$css = sprintf(
			'
body.login.guard-dog-login-customized {
	background-color: %1$s;
	min-height: 100vh;
}

body.login.guard-dog-login-customized #login {
	box-sizing: border-box;
	width: min(calc(100%% - 40px), %2$dpx);
	padding-top: clamp(32px, 7vh, 72px);
}

body.login.guard-dog-login-customized form {
	background: %3$s;
	border-color: %4$s;
	border-radius: %5$dpx;
	box-shadow: %6$s;
	color: %7$s;
}

body.login.guard-dog-login-customized label,
body.login.guard-dog-login-customized .forgetmenot label {
	color: %7$s;
}

body.login.guard-dog-login-customized form .input,
body.login.guard-dog-login-customized input[type="text"],
body.login.guard-dog-login-customized input[type="password"],
body.login.guard-dog-login-customized input[type="email"] {
	border-color: %8$s;
	border-radius: %9$dpx;
	color: %7$s;
}

body.login.guard-dog-login-customized form .input:focus,
body.login.guard-dog-login-customized input[type="text"]:focus,
body.login.guard-dog-login-customized input[type="password"]:focus,
body.login.guard-dog-login-customized input[type="email"]:focus {
	border-color: %10$s;
	box-shadow: 0 0 0 1px %10$s;
	outline: 2px solid transparent;
}

body.login.guard-dog-login-customized .button-primary,
body.login.guard-dog-login-customized .wp-core-ui .button-primary {
	background: %11$s;
	border-color: %11$s;
	color: %12$s;
	border-radius: %9$dpx;
	text-shadow: none;
	box-shadow: none;
}

body.login.guard-dog-login-customized .button-primary:hover,
body.login.guard-dog-login-customized .button-primary:focus {
	background: %13$s;
	border-color: %13$s;
	color: %12$s;
	box-shadow: 0 0 0 1px #fff, 0 0 0 3px %10$s;
}

body.login.guard-dog-login-customized #nav,
body.login.guard-dog-login-customized #backtoblog,
body.login.guard-dog-login-customized .privacy-policy-page-link,
body.login.guard-dog-login-customized .language-switcher,
body.login.guard-dog-login-customized .guard-dog-login-footer-text {
	text-align: center;
}

body.login.guard-dog-login-customized #nav a,
body.login.guard-dog-login-customized #backtoblog a,
body.login.guard-dog-login-customized .privacy-policy-page-link a,
body.login.guard-dog-login-customized .guard-dog-login-footer-text a {
	color: %14$s;
}

body.login.guard-dog-login-customized #nav a:hover,
body.login.guard-dog-login-customized #nav a:focus,
body.login.guard-dog-login-customized #backtoblog a:hover,
body.login.guard-dog-login-customized #backtoblog a:focus,
body.login.guard-dog-login-customized .privacy-policy-page-link a:hover,
body.login.guard-dog-login-customized .privacy-policy-page-link a:focus,
body.login.guard-dog-login-customized .guard-dog-login-footer-text a:hover,
body.login.guard-dog-login-customized .guard-dog-login-footer-text a:focus,
body.login.guard-dog-login-customized h1 a:hover,
body.login.guard-dog-login-customized h1 a:focus {
	color: %15$s;
}

body.login.guard-dog-login-customized #login_error,
body.login.guard-dog-login-customized .message,
body.login.guard-dog-login-customized .notice {
	border-radius: %9$dpx;
}

body.login.guard-dog-login-customized.guard-dog-login-hide-back-to-site #backtoblog,
body.login.guard-dog-login-customized.guard-dog-login-hide-privacy-link .privacy-policy-page-link {
	display: none;
}

body.login.guard-dog-login-customized .guard-dog-login-footer-text {
	box-sizing: border-box;
	width: min(calc(100%% - 40px), %2$dpx);
	margin: 16px auto 0;
	font-size: 12px;
	line-height: 1.6;
	color: %14$s;
}
%16$s
%17$s
',
			$settings['background_color'],
			(int) $settings['form_width'],
			$settings['form_background'],
			$settings['form_border_color'],
			(int) $settings['form_border_radius'],
			$form_shadow,
			$settings['form_text_color'],
			$settings['input_border_color'],
			$field_radius,
			$settings['input_focus_color'],
			$settings['button_background'],
			$settings['button_text_color'],
			$settings['button_hover_background'],
			$settings['link_color'],
			$settings['link_hover_color'],
			$logo_css,
			$background_css
		);

		$overlay_opacity = (float) $settings['background_overlay_opacity'] / 100;
		$overlay_css     = '';

		if ( $overlay_opacity > 0 ) {
			$overlay_css = sprintf(
				'
body.login.guard-dog-login-customized:before {
	content: "";
	position: fixed;
	inset: 0;
	background: %1$s;
	opacity: %2$s;
	pointer-events: none;
	z-index: 0;
}',
				$settings['background_overlay_color'],
				esc_attr( number_format( $overlay_opacity, 2, '.', '' ) )
			);
		}

		$css .= sprintf(
			'
body.login.guard-dog-login-customized #login,
body.login.guard-dog-login-customized .language-switcher,
body.login.guard-dog-login-customized .guard-dog-login-support-link,
body.login.guard-dog-login-customized .guard-dog-login-footer-text {
	position: relative;
	z-index: 1;
}

body.login.guard-dog-login-customized #login {
	padding-top: clamp(16px, %1$dvh, 120px);
}

body.login.guard-dog-login-customized h1 a {
	margin-bottom: %2$dpx;
}

body.login.guard-dog-login-customized form {
	padding: %3$dpx 24px;
}

body.login.guard-dog-login-customized .message,
body.login.guard-dog-login-customized .notice {
	background: %4$s;
	border-left-color: %5$s;
	color: %6$s;
}

body.login.guard-dog-login-customized #login_error {
	background: %7$s;
	border-left-color: %8$s;
	color: %9$s;
}

body.login.guard-dog-login-customized .message a,
body.login.guard-dog-login-customized .notice a,
body.login.guard-dog-login-customized #login_error a,
body.login.guard-dog-login-customized .guard-dog-login-support-link a {
	color: %10$s;
}

body.login.guard-dog-login-customized .message a:hover,
body.login.guard-dog-login-customized .message a:focus,
body.login.guard-dog-login-customized .notice a:hover,
body.login.guard-dog-login-customized .notice a:focus,
body.login.guard-dog-login-customized #login_error a:hover,
body.login.guard-dog-login-customized #login_error a:focus,
body.login.guard-dog-login-customized .guard-dog-login-support-link a:hover,
body.login.guard-dog-login-customized .guard-dog-login-support-link a:focus {
	color: %11$s;
}

body.login.guard-dog-login-customized .guard-dog-login-support-link {
	box-sizing: border-box;
	width: min(calc(100%% - 40px), %12$dpx);
	margin: 12px auto 0;
	font-size: 13px;
	line-height: 1.5;
	text-align: center;
}

body.login.guard-dog-login-hide-lost-password #nav a[href*="action=lostpassword"],
body.login.guard-dog-login-hide-register #nav a[href*="action=register"] {
	display: none;
}
%13$s
',
			(int) $settings['content_top_spacing'],
			(int) $settings['logo_bottom_spacing'],
			(int) $settings['form_padding'],
			$settings['notice_background'],
			$settings['notice_border_color'],
			$settings['notice_text_color'],
			$settings['error_background'],
			$settings['error_border_color'],
			$settings['error_text_color'],
			$settings['link_color'],
			$settings['link_hover_color'],
			(int) $settings['form_width'],
			$overlay_css
		);

		return trim( $css );
	}

	/**
	 * Render action buttons without theme export for this runtime-only customizer.
	 */
	protected function render_actions() {
		?>
		<button type="button" class="button button-primary customizer-save">
			<span class="dashicons dashicons-yes"></span>
			<?php esc_html_e( 'Save Changes', 'guard-dog' ); ?>
		</button>

		<button type="button" class="button customizer-reset">
			<span class="dashicons dashicons-image-rotate"></span>
			<?php esc_html_e( 'Reset to Defaults', 'guard-dog' ); ?>
		</button>
		<?php
	}

	/**
	 * Render status indicator.
	 */
	protected function render_status() {
		$is_enabled = $this->is_enabled();
		?>
		<div class="customizer-status <?php echo $is_enabled ? 'database-settings' : 'theme-override'; ?>">
			<span class="status-indicator <?php echo $is_enabled ? 'database-settings' : 'theme-override'; ?>">
				<span class="dashicons <?php echo $is_enabled ? 'dashicons-visibility' : 'dashicons-hidden'; ?>"></span>
				<?php
				echo esc_html(
					$is_enabled
						? __( 'Login page customization is enabled', 'guard-dog' )
						: __( 'Login page customization is disabled', 'guard-dog' )
				);
				?>
			</span>
		</div>
		<?php
	}

	/**
	 * Check if current page is this customizer page.
	 *
	 * @param string $hook Current admin page hook.
	 * @return bool
	 */
	protected function is_customizer_page( $hook ) {
		return strpos( $hook, 'guard-dog-login-customizer' ) !== false;
	}

	/**
	 * Render the customizer page.
	 */
	public static function render_page() {
		$instance = new self();
		$instance->render();
	}

	/**
	 * Get default settings.
	 *
	 * @return array
	 */
	private function get_default_settings() {
		return array(
			'enabled'                => false,
			'logo'                   => '',
			'logo_width'             => 180,
			'logo_height'            => 80,
			'logo_link_url'          => home_url( '/' ),
			'logo_alt_text'          => get_bloginfo( 'name' ),
			'logo_bottom_spacing'    => 24,
			'background_color'       => '#f0f0f1',
			'background_image'       => '',
			'background_overlay_color' => '#000000',
			'background_overlay_opacity' => 0,
			'content_top_spacing'    => 7,
			'form_width'             => 360,
			'form_padding'           => 26,
			'form_background'        => '#ffffff',
			'form_text_color'        => '#1d2327',
			'form_border_color'      => '#c3c4c7',
			'form_border_radius'     => 8,
			'form_shadow'            => true,
			'input_border_color'     => '#8c8f94',
			'input_focus_color'      => '#2271b1',
			'button_background'      => '#2271b1',
			'button_text_color'      => '#ffffff',
			'button_hover_background' => '#135e96',
			'login_message'          => '',
			'lost_password_message'  => '',
			'reset_password_message' => '',
			'two_factor_message'     => '',
			'two_factor_setup_message' => '',
			'password_expired_message' => '',
			'footer_text'            => '',
			'link_color'             => '#50575e',
			'link_hover_color'       => '#135e96',
			'notice_background'      => '#ffffff',
			'notice_border_color'    => '#72aee6',
			'notice_text_color'      => '#1d2327',
			'error_background'       => '#ffffff',
			'error_border_color'     => '#d63638',
			'error_text_color'       => '#1d2327',
			'support_link_text'      => '',
			'support_link_url'       => '',
			'lost_password_link_text' => '',
			'privacy_link_text'      => '',
			'back_to_site_text'      => '',
			'hide_lost_password_link' => false,
			'hide_register_link'     => false,
			'hide_back_to_site'      => false,
			'hide_privacy_link'      => false,
			'hide_language_switcher' => false,
		);
	}

	/**
	 * Merge, normalize, and sanitize settings for output.
	 *
	 * @param array|null $settings Optional settings.
	 * @return array
	 */
	private function get_effective_settings( $settings = null ) {
		$defaults = $this->get_default_settings();

		if ( null === $settings ) {
			$settings = array();
			foreach ( $defaults as $key => $default ) {
				$settings[ $key ] = $this->get_setting( $key, $default );
			}
		} else {
			$settings = array_merge( $defaults, is_array( $settings ) ? $settings : array() );
		}

		$settings['enabled']                = $this->to_bool( $settings['enabled'] );
		$settings['logo']                   = esc_url_raw( $settings['logo'] );
		$settings['logo_width']             = $this->clamp_absint( $settings['logo_width'], 40, 420, 180 );
		$settings['logo_height']            = $this->clamp_absint( $settings['logo_height'], 30, 240, 80 );
		$settings['logo_link_url']          = esc_url_raw( $settings['logo_link_url'] );
		$settings['logo_alt_text']          = sanitize_text_field( $settings['logo_alt_text'] );
		$settings['logo_bottom_spacing']    = $this->clamp_absint( $settings['logo_bottom_spacing'], 0, 72, 24 );
		$settings['background_color']       = $this->sanitize_color_with_fallback( $settings['background_color'], $defaults['background_color'] );
		$settings['background_image']       = esc_url_raw( $settings['background_image'] );
		$settings['background_overlay_color'] = $this->sanitize_color_with_fallback( $settings['background_overlay_color'], $defaults['background_overlay_color'] );
		$settings['background_overlay_opacity'] = $this->clamp_absint( $settings['background_overlay_opacity'], 0, 90, 0 );
		$settings['content_top_spacing']    = $this->clamp_absint( $settings['content_top_spacing'], 0, 20, 7 );
		$settings['form_width']             = $this->clamp_absint( $settings['form_width'], 300, 640, 360 );
		$settings['form_padding']           = $this->clamp_absint( $settings['form_padding'], 16, 48, 26 );
		$settings['form_background']        = $this->sanitize_color_with_fallback( $settings['form_background'], $defaults['form_background'] );
		$settings['form_text_color']        = $this->sanitize_color_with_fallback( $settings['form_text_color'], $defaults['form_text_color'] );
		$settings['form_border_color']      = $this->sanitize_color_with_fallback( $settings['form_border_color'], $defaults['form_border_color'] );
		$settings['form_border_radius']     = $this->clamp_absint( $settings['form_border_radius'], 0, 30, 8 );
		$settings['form_shadow']            = $this->to_bool( $settings['form_shadow'] );
		$settings['input_border_color']     = $this->sanitize_color_with_fallback( $settings['input_border_color'], $defaults['input_border_color'] );
		$settings['input_focus_color']      = $this->sanitize_color_with_fallback( $settings['input_focus_color'], $defaults['input_focus_color'] );
		$settings['button_background']      = $this->sanitize_color_with_fallback( $settings['button_background'], $defaults['button_background'] );
		$settings['button_text_color']      = $this->sanitize_color_with_fallback( $settings['button_text_color'], $defaults['button_text_color'] );
		$settings['button_hover_background'] = $this->sanitize_color_with_fallback( $settings['button_hover_background'], $defaults['button_hover_background'] );
		$settings['login_message']          = wp_kses_post( $settings['login_message'] );
		$settings['lost_password_message']  = wp_kses_post( $settings['lost_password_message'] );
		$settings['reset_password_message'] = wp_kses_post( $settings['reset_password_message'] );
		$settings['two_factor_message']     = wp_kses_post( $settings['two_factor_message'] );
		$settings['two_factor_setup_message'] = wp_kses_post( $settings['two_factor_setup_message'] );
		$settings['password_expired_message'] = wp_kses_post( $settings['password_expired_message'] );
		$settings['footer_text']            = wp_kses_post( $settings['footer_text'] );
		$settings['link_color']             = $this->sanitize_color_with_fallback( $settings['link_color'], $defaults['link_color'] );
		$settings['link_hover_color']       = $this->sanitize_color_with_fallback( $settings['link_hover_color'], $defaults['link_hover_color'] );
		$settings['notice_background']      = $this->sanitize_color_with_fallback( $settings['notice_background'], $defaults['notice_background'] );
		$settings['notice_border_color']    = $this->sanitize_color_with_fallback( $settings['notice_border_color'], $defaults['notice_border_color'] );
		$settings['notice_text_color']      = $this->sanitize_color_with_fallback( $settings['notice_text_color'], $defaults['notice_text_color'] );
		$settings['error_background']       = $this->sanitize_color_with_fallback( $settings['error_background'], $defaults['error_background'] );
		$settings['error_border_color']     = $this->sanitize_color_with_fallback( $settings['error_border_color'], $defaults['error_border_color'] );
		$settings['error_text_color']       = $this->sanitize_color_with_fallback( $settings['error_text_color'], $defaults['error_text_color'] );
		$settings['support_link_text']      = sanitize_text_field( $settings['support_link_text'] );
		$settings['support_link_url']       = esc_url_raw( $settings['support_link_url'] );
		$settings['lost_password_link_text'] = sanitize_text_field( $settings['lost_password_link_text'] );
		$settings['privacy_link_text']      = sanitize_text_field( $settings['privacy_link_text'] );
		$settings['back_to_site_text']      = sanitize_text_field( $settings['back_to_site_text'] );
		$settings['hide_lost_password_link'] = $this->to_bool( $settings['hide_lost_password_link'] );
		$settings['hide_register_link']     = $this->to_bool( $settings['hide_register_link'] );
		$settings['hide_back_to_site']      = $this->to_bool( $settings['hide_back_to_site'] );
		$settings['hide_privacy_link']      = $this->to_bool( $settings['hide_privacy_link'] );
		$settings['hide_language_switcher'] = $this->to_bool( $settings['hide_language_switcher'] );

		return $settings;
	}

	/**
	 * Convert mixed values to bool.
	 *
	 * @param mixed $value Raw value.
	 * @return bool
	 */
	private function to_bool( $value ) {
		if ( is_bool( $value ) ) {
			return $value;
		}

		$value = strtolower( trim( (string) $value ) );

		return ! in_array( $value, array( '', '0', 'false', 'no', 'off' ), true );
	}

	/**
	 * Build the body classes for customized login screens.
	 *
	 * @param string[] $classes  Existing classes.
	 * @param array    $settings Effective settings.
	 * @return string[]
	 */
	private function get_login_body_classes( array $classes, array $settings ) {
		$classes[] = 'guard-dog-login-customized';

		if ( ! empty( $settings['background_image'] ) ) {
			$classes[] = 'guard-dog-login-has-background-image';
		}

		if ( $this->to_bool( $settings['hide_back_to_site'] ) ) {
			$classes[] = 'guard-dog-login-hide-back-to-site';
		}

		if ( $this->to_bool( $settings['hide_privacy_link'] ) ) {
			$classes[] = 'guard-dog-login-hide-privacy-link';
		}

		if ( $this->to_bool( $settings['hide_lost_password_link'] ) ) {
			$classes[] = 'guard-dog-login-hide-lost-password';
		}

		if ( $this->to_bool( $settings['hide_register_link'] ) ) {
			$classes[] = 'guard-dog-login-hide-register';
		}

		return array_values( array_unique( array_filter( $classes ) ) );
	}

	/**
	 * Clamp an absolute integer.
	 *
	 * @param mixed $value   Raw value.
	 * @param int   $min     Minimum.
	 * @param int   $max     Maximum.
	 * @param int   $default Default.
	 * @return int
	 */
	private function clamp_absint( $value, $min, $max, $default ) {
		if ( '' === $value || null === $value || ! is_numeric( $value ) ) {
			return $default;
		}

		return min( $max, max( $min, absint( $value ) ) );
	}

	/**
	 * Sanitize a hex color with fallback.
	 *
	 * @param mixed  $value    Raw color.
	 * @param string $fallback Fallback color.
	 * @return string
	 */
	private function sanitize_color_with_fallback( $value, $fallback ) {
		$value = sanitize_hex_color( $value );

		return $value ?: $fallback;
	}

	/**
	 * Get minimal WordPress login CSS for the iframe preview.
	 *
	 * @return string
	 */
	private function get_preview_base_css() {
		return '
html {
	background: #f0f0f1;
	min-height: 100%;
}

body.login {
	min-height: 100vh;
	margin: 0;
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
	color: #3c434a;
	background: #f0f0f1;
}

body.login * {
	box-sizing: border-box;
}

.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

#login {
	width: 320px;
	padding: 5% 0 0;
	margin: auto;
}

.login h1 {
	text-align: center;
	margin: 0;
}

.login h1 a {
	color: #3c434a;
	font-size: 20px;
	font-weight: 400;
	line-height: 1.3;
	margin: 0 auto 24px;
	padding: 0;
	text-decoration: none;
	width: auto;
	min-height: 84px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.login form {
	margin: 24px 0;
	padding: 26px 24px;
	font-weight: 400;
	overflow: hidden;
	background: #fff;
	border: 1px solid #c3c4c7;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}

.login label {
	font-size: 14px;
	line-height: 1.5;
	display: inline-block;
	margin-bottom: 3px;
}

.login form .input,
.login input[type="text"],
.login input[type="password"] {
	font-size: 24px;
	line-height: 1.33333333;
	width: 100%;
	border: 1px solid #8c8f94;
	padding: 3px 5px;
	margin: 0 6px 16px 0;
	min-height: 40px;
	background: #fff;
}

.login .forgetmenot {
	font-weight: 400;
	float: left;
	margin-bottom: 0;
}

.login .button-primary {
	float: right;
	min-height: 32px;
	padding: 0 12px;
	background: #2271b1;
	border: 1px solid #2271b1;
	color: #fff;
	cursor: pointer;
}

#login form p.submit {
	margin: 0;
	padding: 0;
}

.login #nav,
.login #backtoblog {
	font-size: 13px;
	padding: 0 24px;
}

.login #nav {
	margin: 24px 0 0;
}

#backtoblog {
	margin: 16px 0;
	word-wrap: break-word;
}

.login #nav a,
.login #backtoblog a,
.privacy-policy-page-link a {
	text-decoration: none;
	color: #50575e;
}

.login .privacy-policy-page-link {
	text-align: center;
	width: 100%;
	margin: 3em 0 2em;
	font-size: 13px;
}

.message {
	border-left: 4px solid #72aee6;
	padding: 12px;
	margin: 0 0 16px;
	background: #fff;
	box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
}

.message p {
	margin: 0;
}

.language-switcher {
	padding: 0;
	overflow: visible;
	background: none;
	border: none;
	box-shadow: none;
	text-align: center;
	margin: 0 auto;
}

#language-switcher {
	margin: 0;
	padding: 0;
}

#language-switcher select {
	margin-right: 6px;
	min-height: 32px;
}
';
	}
}
