<?php
/**
 * Global Settings Page
 *
 * @package suspended Dennis_SEO
 */

defined( 'ABSPATH' ) || exit;

class Dennis_SEO_Settings {

	const OPTION_KEY = 'dennis_seo_settings';
	const MENU_SLUG  = 'dennis-geo';

	/**
	 * Default settings.
	 */
	private static array $defaults = array(
		// Site Identity.
		'site_name'            => '',
		'site_type'            => 'LocalBusiness',
		'business_description' => '',
		'org_logo'             => '',
		'org_phone'            => '',
		'org_email'            => '',
		'org_address_street'   => '',
		'org_address_city'     => '',
		'org_address_zip'      => '',
		'org_address_country'  => 'SE',
		'org_service_area'     => '',
		'org_opening_hours'    => '',
		'org_price_range'      => '',
		'org_geo_lat'          => '',
		'org_geo_lng'          => '',
		'org_parent_name'      => '',
		'org_parent_url'       => '',

		// Social.
		'default_og_image'     => '',
		'social_facebook'      => '',
		'social_instagram'     => '',
		'social_linkedin'      => '',
		'social_google'        => '',

		// Title & Meta.
		'title_separator'      => '|',
		'title_template_post'  => '{title} {sep} {site_name}',
		'title_template_page'  => '{title} {sep} {site_name}',
		'homepage_title'       => '',
		'homepage_description' => '',
		'archive_desc_category' => '',
		'archive_desc_tag'      => '',
		'archive_desc_post_type' => '',

		// Schema.
		'schema_default_post'  => 'Article',
		'schema_default_page'  => 'WebPage',
		'service_offer_text'   => '',
		'schema_organization'  => true,
		'schema_breadcrumbs'   => true,
		'schema_website'       => true,

		// llms.txt.
		'llms_enabled'         => true,
		'llms_default'         => 'all',
		'llms_header'          => '',
		'llms_link_tag'        => true,
		'llms_full_content'    => '',

		// AI Crawlers.
		'crawler_gptbot'             => 'allow',
		'crawler_chatgpt_user'       => 'allow',
		'crawler_google_extended'    => 'allow',
		'crawler_claudebot'          => 'allow',
		'crawler_perplexitybot'      => 'allow',
		'crawler_applebot_extended'  => 'allow',
		'crawler_meta_external'      => 'block',
		'crawler_ccbot'              => 'block',
		'crawler_bytespider'         => 'block',
		'crawler_amazonbot'          => 'allow',
		'crawler_anthropic_ai'       => 'allow',
		'crawler_cohere_ai'          => 'allow',
		'custom_robots_rules'        => '',

		// Advanced.
		'keep_data_on_uninstall'     => true,
	);

	public function __construct() {
		add_action( 'admin_menu', array( $this, 'add_menu' ) );
		add_action( 'admin_init', array( $this, 'register_settings' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_media' ) );

		// Append AI crawler rules to robots.txt.
		add_filter( 'robots_txt', array( $this, 'filter_robots_txt' ), 100, 2 );
	}

	/**
	 * Get a setting value.
	 */
	/**
	 * Enqueue media library on our settings page.
	 */
	public function enqueue_media( string $hook ): void {
		if ( false === strpos( $hook, self::MENU_SLUG ) ) {
			return;
		}
		wp_enqueue_media();
		wp_enqueue_style( 'dseo-settings', DENNIS_SEO_URL . 'assets/css/admin-settings.css', array(), DENNIS_SEO_VERSION );
		wp_enqueue_script( 'dseo-settings', DENNIS_SEO_URL . 'assets/js/admin-settings.js', array(), DENNIS_SEO_VERSION, true );
		wp_localize_script( 'dseo-settings', 'dseoSettings', array(
			'ajaxUrl' => admin_url( 'admin-ajax.php' ),
			'nonce'   => wp_create_nonce( 'dennis_seo_generate_key' ),
			'homeUrl' => home_url(),
			'statusNonce' => wp_create_nonce( 'dennis_seo_check_status' ),
			'i18n'    => array(
				'generatingKey'   => __( 'Generating key...', 'dennis-geo' ),
				'checkoutOpened'  => __( 'Checkout opened in a new tab', 'dennis-geo' ),
				'dennisWorking'   => __( "Once payment goes through, Dennis gets to work — reading your site and filling in every SEO field. Give him a minute or two. It normally doesn't take long, he's really efficient.", 'dennis-geo' ),
				'notifyWhenDone'  => __( "We'll let you know here when he's finished. Feel free to close this and come back later.", 'dennis-geo' ),
				'close'           => __( 'Close', 'dennis-geo' ),
				'errorTryAgain'   => __( 'Error — try again', 'dennis-geo' ),
				'populated'       => __( 'Your site has been populated!', 'dennis-geo' ),
				'reloadResults'   => __( 'All SEO & GEO fields are now filled. Reload the page to see the results.', 'dennis-geo' ),
				'reloadPage'      => __( 'Reload page', 'dennis-geo' ),
				'dennisGeo'       => __( 'Dennis GEO:', 'dennis-geo' ),
				'populatedNotice' => __( 'Your site has been populated! Reload the page to see the new SEO & GEO fields.', 'dennis-geo' ),
				'reloadNow'       => __( 'Reload now', 'dennis-geo' ),
				'selectImage'     => __( 'Select Image', 'dennis-geo' ),
				'useImage'        => __( 'Use this image', 'dennis-geo' ),
				'coordsExtracted' => __( 'Coordinates extracted! Remember to save your settings.', 'dennis-geo' ),
				'coordsNotFound'  => __( 'Could not find coordinates in that URL. Make sure you copied the full URL from Google Maps.', 'dennis-geo' ),
			),
		) );
	}

	public static function get( string $key, $default = null ) {
		$opts = get_option( self::OPTION_KEY, array() );
		if ( isset( $opts[ $key ] ) ) {
			return $opts[ $key ];
		}
		if ( null !== $default ) {
			return $default;
		}
		return self::$defaults[ $key ] ?? '';
	}

	/**
	 * Get all settings merged with defaults.
	 */
	public static function get_all(): array {
		return wp_parse_args( get_option( self::OPTION_KEY, array() ), self::$defaults );
	}

	public function add_menu(): void {
		// Settings is now a submenu under the parent registered by class-audit.php.
		add_submenu_page(
			Dennis_SEO_Audit::MENU_SLUG,
			__( 'Settings', 'dennis-geo' ),
			__( 'Settings', 'dennis-geo' ),
			'manage_options',
			self::MENU_SLUG,
			array( $this, 'render_page' )
		);
	}

	public function register_settings(): void {
		register_setting( 'dennis_seo_group', self::OPTION_KEY, array(
			'type'              => 'array',
			'sanitize_callback' => array( $this, 'sanitize_settings' ),
		) );
	}

	public function sanitize_settings( $input ): array {
		$clean = array();
		$text_fields = array(
			'site_name', 'site_type', 'business_description', 'org_logo',
			'org_phone', 'org_email', 'org_address_street', 'org_address_city',
			'org_address_zip', 'org_address_country', 'org_service_area',
			'org_opening_hours', 'org_price_range', 'org_geo_lat', 'org_geo_lng',
			'org_parent_name', 'org_parent_url',
			'default_og_image',
			'social_facebook', 'social_instagram', 'social_linkedin', 'social_google',
			'title_separator', 'title_template_post', 'title_template_page',
			'homepage_title', 'homepage_description',
			'archive_desc_category', 'archive_desc_tag', 'archive_desc_post_type',
			'service_offer_text',
			'schema_default_post', 'schema_default_page',
			'llms_default', 'llms_header',
			'crawler_gptbot', 'crawler_chatgpt_user', 'crawler_google_extended',
			'crawler_claudebot', 'crawler_perplexitybot', 'crawler_applebot_extended',
			'crawler_meta_external', 'crawler_ccbot',
			'crawler_bytespider', 'crawler_amazonbot', 'crawler_anthropic_ai', 'crawler_cohere_ai',
		);
		foreach ( $text_fields as $field ) {
			$clean[ $field ] = isset( $input[ $field ] ) ? sanitize_text_field( $input[ $field ] ) : '';
		}

		// Textarea fields.
		$clean['custom_robots_rules'] = isset( $input['custom_robots_rules'] ) ? sanitize_textarea_field( $input['custom_robots_rules'] ) : '';
		$clean['llms_full_content']   = isset( $input['llms_full_content'] ) ? sanitize_textarea_field( $input['llms_full_content'] ) : '';

		// Boolean fields.
		$clean['schema_organization'] = ! empty( $input['schema_organization'] );
		$clean['schema_breadcrumbs']  = ! empty( $input['schema_breadcrumbs'] );
		$clean['schema_website']      = ! empty( $input['schema_website'] );
		$clean['keep_data_on_uninstall']  = ! empty( $input['keep_data_on_uninstall'] );
		$clean['llms_enabled']        = ! empty( $input['llms_enabled'] );

		$clean['llms_link_tag']       = ! empty( $input['llms_link_tag'] );

		// Bust all caches.
		delete_transient( 'dennis_seo_llms_txt' );
		delete_transient( 'dennis_seo_llms_full_txt' );
		delete_transient( 'dennis_seo_sitemap_post' );
		delete_transient( 'dennis_seo_sitemap_page' );

		return $clean;
	}

	/**
	 * Filter robots.txt output.
	 */
	public function filter_robots_txt( string $output, bool $public ): string {
		// Add sitemap reference.
		$output .= "\nSitemap: " . home_url( '/sitemap.xml' ) . "\n";

		// AI crawler rules.
		$crawlers = array(
			'crawler_gptbot'            => 'GPTBot',
			'crawler_chatgpt_user'      => 'ChatGPT-User',
			'crawler_google_extended'   => 'Google-Extended',
			'crawler_claudebot'         => 'ClaudeBot',
			'crawler_perplexitybot'     => 'PerplexityBot',
			'crawler_applebot_extended' => 'Applebot-Extended',
			'crawler_meta_external'     => 'Meta-ExternalAgent',
			'crawler_ccbot'             => 'CCBot',
			'crawler_bytespider'        => 'Bytespider',
			'crawler_amazonbot'         => 'Amazonbot',
			'crawler_anthropic_ai'      => 'anthropic-ai',
			'crawler_cohere_ai'         => 'cohere-ai',
		);

		foreach ( $crawlers as $key => $user_agent ) {
			$setting = self::get( $key, 'allow' );
			if ( 'block' === $setting ) {
				$output .= "\nUser-agent: {$user_agent}\nDisallow: /\n";
			}
		}

		// Custom rules.
		$custom = self::get( 'custom_robots_rules', '' );
		if ( ! empty( trim( $custom ) ) ) {
			$output .= "\n" . $custom . "\n";
		}

		return $output;
	}

	/**
	 * Render the settings page.
	 */
	public function render_page(): void {
		$opts = self::get_all();
		?>

		<div class="dseo-settings">

			<?php
			// Import/export messages via transient.
			$dseo_notice = get_transient( 'dseo_admin_notice_' . get_current_user_id() );
			if ( $dseo_notice ) {
				delete_transient( 'dseo_admin_notice_' . get_current_user_id() );
				$msg_class = 'success' === $dseo_notice['type'] ? 'notice-success' : 'notice-error';
				echo '<div class="notice ' . esc_attr( $msg_class ) . ' is-dismissible" style="margin:0 0 16px;"><p>' . esc_html( $dseo_notice['message'] ) . '</p></div>';
			}
			?>

			<!-- AI Integration Modal -->
			<div class="dseo-ai-modal-overlay" id="dseo-ai-modal">
				<div class="dseo-ai-modal">
					<div class="dseo-ai-modal-header" style="position:relative;"><img src="<?php echo esc_url( plugins_url( 'assets/dennis-white.png', dirname( __FILE__ ) ) ); ?>" alt="" class="dseo-modal-logo" style="position:absolute;top:16px;right:16px;width:75px;height:75px;opacity:0.7;">
						<h3><span class="dashicons dashicons-superhero-alt" style="margin-right:6px;"></span> <?php esc_html_e( 'Autopopulate All Fields with AI', 'dennis-geo' ); ?></h3>
						<p><?php esc_html_e( 'Fill every SEO & GEO field in one click.', 'dennis-geo' ); ?></p>
					</div>
					<div class="dseo-ai-modal-body">
						<p><?php esc_html_e( 'Our AI reads every page on your site and automatically fills all your SEO & GEO fields with keyword-optimized, professionally crafted content — in minutes, not hours.', 'dennis-geo' ); ?></p>
						<ul>
							<li><?php esc_html_e( 'SEO titles & meta descriptions optimized for search and AI', 'dennis-geo' ); ?></li>
							<li><?php esc_html_e( 'Unique focus keyphrases for every page — no cannibalization', 'dennis-geo' ); ?></li>
							<li><?php esc_html_e( 'Schema types selected for maximum rich result visibility', 'dennis-geo' ); ?></li>
							<li><?php esc_html_e( 'llms.txt descriptions and llms-full.txt content generated', 'dennis-geo' ); ?></li>
							<li><?php esc_html_e( 'Open Graph titles & descriptions for social sharing', 'dennis-geo' ); ?></li>
						</ul>
						<p><strong><?php esc_html_e( 'One-time payment of $19. All fields on all pages. No subscription.', 'dennis-geo' ); ?></strong></p>
					<p style="font-size:12px;color:#666;margin:8px 0 0;"><?php esc_html_e( 'Fields you\'ve already filled are left as they are. Want all fields rewritten?', 'dennis-geo' ); ?> <a href="#" id="dseo-goto-clear" style="color:#7e5cef;"><?php esc_html_e( 'Clear Dennis GEO fields', 'dennis-geo' ); ?></a> <?php esc_html_e( 'first.', 'dennis-geo' ); ?></p>
					</div>
					<div class="dseo-ai-modal-footer">
						<button type="button" class="dseo-ai-modal-cta" id="dseo-populate-btn">
							<?php esc_html_e( 'Checkout — $19', 'dennis-geo' ); ?> &rarr;
						</button>
						<button type="button" class="dseo-ai-modal-close" onclick="document.getElementById('dseo-ai-modal').classList.remove('active');"><?php esc_html_e( 'Maybe later', 'dennis-geo' ); ?></button>
						<p style="font-size:10px;color:#999;margin-top:-5px;width:100%;"><?php esc_html_e( 'By using this service, your page content will be sent to dennisgeo.com for AI processing.', 'dennis-geo' ); ?></p>
					</div>
				</div>
			</div>

			<div class="dseo-header">
				<span class="dashicons dashicons-search" style="font-size:28px;width:28px;height:28px;color:#2271b1;"></span>
				<h1><?php esc_html_e( 'Dennis GEO – AI Visibility for WordPress', 'dennis-geo' ); ?></h1>
				<span class="dseo-version">v<?php echo esc_html( DENNIS_SEO_VERSION ); ?></span>
				<?php if ( Dennis_SEO_Audit::quick_score() < 80 ) : ?>
				<button type="button" class="dseo-ai-btn" onclick="document.getElementById('dseo-ai-modal').classList.add('active');" style="margin-left:auto;">
					<span class="dashicons dashicons-superhero-alt"></span> <?php esc_html_e( 'Autopopulate with AI', 'dennis-geo' ); ?>
				</button>
				<?php else : ?>
				<a href="https://dennisgeo.com/wordpress-ai-visibility-audit.html" target="_blank" class="dseo-ai-btn" style="margin-left:auto; background:linear-gradient(135deg,#00a32a,#007017); box-shadow:none;">
					<span class="dashicons dashicons-awards"></span> <?php esc_html_e( 'Complete GEO Audit?', 'dennis-geo' ); ?>
				</a>
				<?php endif; ?>
				<span style="font-size:12px; color:#757575;">by <a href="https://aistaffing.se" target="_blank" rel="noopener" style="color:#2271b1; text-decoration:none;">aistaffing.se</a></span>
			</div>

			<!-- Active endpoints info -->
			<div class="dseo-endpoints" style="margin-bottom:20px;">
				<div class="dseo-endpoint">
					<div class="dseo-endpoint-label"><?php esc_html_e( 'Sitemap', 'dennis-geo' ); ?></div>
					<div class="dseo-endpoint-url"><a href="<?php echo esc_url( home_url( '/sitemap.xml' ) ); ?>" target="_blank"><?php echo esc_html( home_url( '/sitemap.xml' ) ); ?></a></div>
				</div>
				<div class="dseo-endpoint">
					<div class="dseo-endpoint-label"><?php esc_html_e( 'llms.txt', 'dennis-geo' ); ?></div>
					<div class="dseo-endpoint-url"><a href="<?php echo esc_url( home_url( '/llms.txt' ) ); ?>" target="_blank"><?php echo esc_html( home_url( '/llms.txt' ) ); ?></a></div>
				</div>
				<div class="dseo-endpoint">
					<div class="dseo-endpoint-label"><?php esc_html_e( 'llms-full.txt', 'dennis-geo' ); ?></div>
					<div class="dseo-endpoint-url"><a href="<?php echo esc_url( home_url( '/llms-full.txt' ) ); ?>" target="_blank"><?php echo esc_html( home_url( '/llms-full.txt' ) ); ?></a></div>
				</div>
			</div>

			<form method="post" action="options.php">
				<?php settings_fields( 'dennis_seo_group' ); ?>

				<!-- Tabbed navigation -->
				<div class="dseo-settings-tabs">
					<div class="dseo-settings-tab active" data-stab="identity"><span class="dashicons dashicons-building"></span> <?php esc_html_e( 'Site Identity', 'dennis-geo' ); ?></div>
					<div class="dseo-settings-tab" data-stab="social"><span class="dashicons dashicons-share"></span> <?php esc_html_e( 'Social', 'dennis-geo' ); ?></div>
					<div class="dseo-settings-tab" data-stab="titles"><span class="dashicons dashicons-editor-textcolor"></span> <?php esc_html_e( 'Titles & Meta', 'dennis-geo' ); ?></div>
					<div class="dseo-settings-tab" data-stab="schema"><span class="dashicons dashicons-editor-code"></span> <?php esc_html_e( 'Schema', 'dennis-geo' ); ?></div>
					<div class="dseo-settings-tab" data-stab="llms"><span class="dashicons dashicons-cloud"></span> <?php esc_html_e( 'llms.txt', 'dennis-geo' ); ?></div>
					<div class="dseo-settings-tab" data-stab="llmsfull"><span class="dashicons dashicons-media-text"></span> <?php esc_html_e( 'llms-full.txt', 'dennis-geo' ); ?></div>
					<div class="dseo-settings-tab" data-stab="crawlers"><span class="dashicons dashicons-visibility"></span> <?php esc_html_e( 'AI Crawlers', 'dennis-geo' ); ?></div>
				</div>

				<!-- Site Identity Panel -->
				<div class="dseo-settings-panel active" data-spanel="identity">
					<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16px;">
						<div>
							<h2><?php esc_html_e( 'Site Identity', 'dennis-geo' ); ?></h2>
							<p class="description"><?php esc_html_e( 'Basic information about your business. Used in schema markup, llms.txt, and title templates.', 'dennis-geo' ); ?></p>
						</div>
						<?php if ( Dennis_SEO_Audit::quick_score() < 80 ) : ?>
						<button type="button" class="dseo-ai-btn dseo-ai-btn-sm" onclick="document.getElementById('dseo-ai-modal').classList.add('active');">
							<span class="dashicons dashicons-superhero-alt"></span> <?php esc_html_e( 'Fill with AI', 'dennis-geo' ); ?>
						</button>
						<?php else : ?>
						<a href="https://dennisgeo.com/wordpress-ai-visibility-audit.html" target="_blank" class="dseo-ai-btn dseo-ai-btn-sm" style="background:linear-gradient(135deg,#00a32a,#007017); box-shadow:none;">
							<span class="dashicons dashicons-awards"></span> <?php esc_html_e( 'Complete GEO Audit?', 'dennis-geo' ); ?>
						</a>
						<?php endif; ?>
					</div>
					<table class="form-table">
						<?php
						$this->render_text( $opts, 'site_name', __( 'Site Name', 'dennis-geo' ), __( 'Used in title templates, schema, and llms.txt.', 'dennis-geo' ) );
						$this->render_select( $opts, 'site_type', __( 'Site Type', 'dennis-geo' ), array(
							'LocalBusiness' => 'Local Business',
							'Organization'  => 'Organization',
							'Corporation'   => 'Corporation',
							'EducationalOrganization' => 'Educational Organization',
							'GovernmentOrganization'  => 'Government Organization',
							'MedicalOrganization'     => 'Medical Organization',
							'NGO'                     => 'Non-Profit / NGO',
							'SportsOrganization'      => 'Sports Organization',
						) );
						$this->render_textarea( $opts, 'business_description', __( 'Business Description', 'dennis-geo' ), __( '1-2 sentences. Used in schema and llms.txt header.', 'dennis-geo' ) );
						$this->render_image( $opts, 'org_logo', __( 'Logo', 'dennis-geo' ), __( 'Used in schema.org Organization markup.', 'dennis-geo' ) );
						$this->render_text( $opts, 'org_phone', __( 'Phone', 'dennis-geo' ) );
						$this->render_text( $opts, 'org_email', __( 'Email', 'dennis-geo' ) );
						$this->render_text( $opts, 'org_address_street', __( 'Street Address', 'dennis-geo' ) );
						$this->render_text( $opts, 'org_address_city', __( 'City', 'dennis-geo' ) );
						$this->render_text( $opts, 'org_address_zip', __( 'Postal Code', 'dennis-geo' ) );
						$this->render_text( $opts, 'org_address_country', __( 'Country Code', 'dennis-geo' ), __( 'ISO 3166-1 alpha-2 (e.g. SE, US, GB)', 'dennis-geo' ) );
						$this->render_text( $opts, 'org_service_area', __( 'Service Area', 'dennis-geo' ), __( 'Comma-separated cities/regions.', 'dennis-geo' ) );
					$this->render_text( $opts, 'org_opening_hours', __( 'Opening Hours', 'dennis-geo' ), __( 'e.g. Mo-Fr 08:00-17:00 (use English two-letter day codes: Mo, Tu, We, Th, Fr, Sa, Su)', 'dennis-geo' ) );
					$this->render_text( $opts, 'org_price_range', __( 'Price Range', 'dennis-geo' ), __( 'Use $ to $$$$ symbols (e.g. $$ for moderate pricing)', 'dennis-geo' ) );
					$this->render_text( $opts, 'org_geo_lat', __( 'Latitude', 'dennis-geo' ), __( 'e.g. 57.7089', 'dennis-geo' ) );
					$this->render_text( $opts, 'org_geo_lng', __( 'Longitude', 'dennis-geo' ), __( 'e.g. 11.9746', 'dennis-geo' ) );
					if ( empty( $opts['org_geo_lat'] ) || empty( $opts['org_geo_lng'] ) ) : ?>
					<tr>
						<th></th>
						<td>
							<div style="background:#f0f6fc;border:1px solid #c3c4c7;border-radius:4px;padding:14px 16px;margin-top:-8px;">
								<p style="margin:0 0 8px;font-size:13px;"><strong><?php esc_html_e( 'How to find your coordinates:', 'dennis-geo' ); ?></strong></p>
								<ol style="margin:0 0 10px;padding-left:18px;font-size:13px;line-height:1.6;">
									<li><?php /* translators: %s: Google Maps link */ printf( wp_kses( __( 'Open %s and search for your address.', 'dennis-geo' ), array( 'a' => array( 'href' => array(), 'target' => array(), 'rel' => array() ) ) ), '<a href="https://www.google.com/maps" target="_blank" rel="noopener">Google Maps</a>' ); ?></li>
									<li><?php esc_html_e( 'Copy the URL from your browser address bar.', 'dennis-geo' ); ?></li>
									<li><?php esc_html_e( 'Paste it below and click Extract.', 'dennis-geo' ); ?></li>
								</ol>
								<div style="display:flex;gap:8px;align-items:center;">
									<input type="text" id="dseo-maps-url" placeholder="<?php esc_attr_e( 'Paste Google Maps URL here...', 'dennis-geo' ); ?>" style="flex:1;min-width:0;" class="regular-text">
									<button type="button" class="button" onclick="dseoExtractCoords();"><?php esc_html_e( 'Extract', 'dennis-geo' ); ?></button>
								</div>
								<p id="dseo-maps-msg" style="margin:6px 0 0;font-size:12px;display:none;"></p>
							</div>
						</td>
					</tr>
					<?php endif;
					$this->render_text( $opts, 'org_parent_name', __( 'Parent Organization Name', 'dennis-geo' ), __( 'Leave blank if none. Used in schema parentOrganization.', 'dennis-geo' ) );
					$this->render_text( $opts, 'org_parent_url', __( 'Parent Organization URL', 'dennis-geo' ), __( 'e.g. https://parent-company.com', 'dennis-geo' ) );
						?>
					</table>
				</div>

				<!-- Social Panel -->
				<div class="dseo-settings-panel" data-spanel="social">
					<h2><?php esc_html_e( 'Social Profiles & Sharing', 'dennis-geo' ); ?></h2>
					<p class="description"><?php esc_html_e( 'Social profile URLs are used in Organization schema (sameAs). The default OG image is used when pages have no featured image.', 'dennis-geo' ); ?></p>
					<table class="form-table">
						<?php
						$this->render_image( $opts, 'default_og_image', __( 'Default OG Image', 'dennis-geo' ), __( 'Default image for social sharing (1200x630px recommended). Used when a page has no featured image or custom OG image.', 'dennis-geo' ) );
						$this->render_text( $opts, 'social_facebook', __( 'Facebook URL', 'dennis-geo' ) );
						$this->render_text( $opts, 'social_instagram', __( 'Instagram URL', 'dennis-geo' ) );
						$this->render_text( $opts, 'social_linkedin', __( 'LinkedIn URL', 'dennis-geo' ) );
						$this->render_text( $opts, 'social_google', __( 'Google Business URL', 'dennis-geo' ) );
						?>
					</table>
				</div>

				<!-- Titles Panel -->
				<div class="dseo-settings-panel" data-spanel="titles">
					<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16px;">
						<div>
							<h2><?php esc_html_e( 'Title & Meta Defaults', 'dennis-geo' ); ?></h2>
							<p class="description"><?php esc_html_e( 'Default title patterns. Variables: {title}, {site_name}, {sep}', 'dennis-geo' ); ?></p>
						</div>
						<?php if ( Dennis_SEO_Audit::quick_score() < 80 ) : ?>
						<button type="button" class="dseo-ai-btn dseo-ai-btn-sm" onclick="document.getElementById('dseo-ai-modal').classList.add('active');">
							<span class="dashicons dashicons-superhero-alt"></span> <?php esc_html_e( 'Fill with AI', 'dennis-geo' ); ?>
						</button>
						<?php else : ?>
						<a href="https://dennisgeo.com/wordpress-ai-visibility-audit.html" target="_blank" class="dseo-ai-btn dseo-ai-btn-sm" style="background:linear-gradient(135deg,#00a32a,#007017); box-shadow:none;">
							<span class="dashicons dashicons-awards"></span> <?php esc_html_e( 'Complete GEO Audit?', 'dennis-geo' ); ?>
						</a>
						<?php endif; ?>
					</div>
					<table class="form-table">
						<?php
						$this->render_select( $opts, 'title_separator', __( 'Title Separator', 'dennis-geo' ), array(
							'|' => '|  (pipe)',
							'-' => '-  (hyphen)',
							'–' => '–  (en dash)',
							'—' => '—  (em dash)',
							'·' => '·  (middle dot)',
						) );
						$this->render_text( $opts, 'title_template_post', __( 'Post Title Template', 'dennis-geo' ) );
						$this->render_text( $opts, 'title_template_page', __( 'Page Title Template', 'dennis-geo' ) );
						$this->render_text( $opts, 'homepage_title', __( 'Homepage Title', 'dennis-geo' ), __( 'Leave blank to use the default template.', 'dennis-geo' ) );
						$this->render_textarea( $opts, 'homepage_description', __( 'Homepage Meta Description', 'dennis-geo' ) );
						?>
						<tr><td colspan="2"><hr style="margin:16px 0;border:none;border-top:1px solid #e0e0e0;"></td></tr>
						<tr><td colspan="2"><h3 style="margin:0 0 4px;"><?php esc_html_e( 'Archive Meta Descriptions', 'dennis-geo' ); ?></h3><p class="description"><?php esc_html_e( 'Use {name} for the category/tag/post type name. Leave blank for no meta description on archives.', 'dennis-geo' ); ?></p></td></tr>
						<?php
						$this->render_text( $opts, 'archive_desc_category', __( 'Category Archives', 'dennis-geo' ), __( 'e.g. "Articles about {name}"', 'dennis-geo' ) );
						$this->render_text( $opts, 'archive_desc_tag', __( 'Tag Archives', 'dennis-geo' ), __( 'e.g. "Posts tagged {name}"', 'dennis-geo' ) );
						$this->render_text( $opts, 'archive_desc_post_type', __( 'Post Type Archives', 'dennis-geo' ), __( 'e.g. "Browse all {name}"', 'dennis-geo' ) );
						?>
					</table>
				</div>

				<!-- Schema Panel -->
				<div class="dseo-settings-panel" data-spanel="schema">
					<h2><?php esc_html_e( 'Schema Defaults', 'dennis-geo' ); ?></h2>
					<p class="description"><?php esc_html_e( 'Controls the automatic JSON-LD structured data output on your site.', 'dennis-geo' ); ?></p>
					<table class="form-table">
						<?php
						$schema_types = array( 'Article' => 'Article', 'BlogPosting' => 'BlogPosting', 'WebPage' => 'WebPage' );
						$this->render_select( $opts, 'schema_default_post', __( 'Default Schema for Posts', 'dennis-geo' ), $schema_types );
						$schema_page_types = array( 'WebPage' => 'WebPage', 'Article' => 'Article', 'AboutPage' => 'AboutPage', 'ContactPage' => 'ContactPage' );
						$this->render_select( $opts, 'schema_default_page', __( 'Default Schema for Pages', 'dennis-geo' ), $schema_page_types );
						$this->render_checkbox( $opts, 'schema_organization', __( 'Output Organization/LocalBusiness schema on all pages', 'dennis-geo' ) );
						$this->render_checkbox( $opts, 'schema_breadcrumbs', __( 'Output BreadcrumbList schema', 'dennis-geo' ) );
						$this->render_checkbox( $opts, 'schema_website', __( 'Output WebSite + SearchAction schema on homepage', 'dennis-geo' ) );
						$this->render_text( $opts, 'service_offer_text', __( 'Service Page Offer Text', 'dennis-geo' ), __( 'Text shown in Service schema offers (e.g. "Free estimate", "Free consultation"). Leave blank to disable.', 'dennis-geo' ) );
						?>
					</table>
				</div>

				<!-- llms.txt Panel -->
				<div class="dseo-settings-panel" data-spanel="llms">
					<h2><?php esc_html_e( 'llms.txt Configuration', 'dennis-geo' ); ?></h2>
					<p class="description"><?php esc_html_e( 'llms.txt is a standard that helps AI systems like ChatGPT, Claude, and Perplexity understand your site structure. Served at /llms.txt.', 'dennis-geo' ); ?></p>

					<table class="form-table">
						<?php
						$this->render_checkbox( $opts, 'llms_enabled', __( 'Enable llms.txt', 'dennis-geo' ) );
						$this->render_select( $opts, 'llms_default', __( 'Default Inclusion', 'dennis-geo' ), array(
							'all'   => __( 'All published pages and posts', 'dennis-geo' ),
							'pages' => __( 'Pages only (not posts)', 'dennis-geo' ),
							'none'  => __( 'None (opt-in per page)', 'dennis-geo' ),
						) );
						$this->render_textarea( $opts, 'llms_header', __( 'llms.txt Header Text', 'dennis-geo' ), __( 'Appears as a blockquote after the site name.', 'dennis-geo' ) );
						$this->render_checkbox( $opts, 'llms_link_tag', __( 'Add <link rel="llms-txt"> to HTML head', 'dennis-geo' ) );
						?>
					</table>
				</div>

				<!-- llms-full.txt Panel -->
				<div class="dseo-settings-panel" data-spanel="llmsfull">
					<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16px;">
						<div>
							<h2><?php esc_html_e( 'llms-full.txt Configuration', 'dennis-geo' ); ?></h2>
							<p class="description"><?php esc_html_e( 'llms-full.txt contains the complete text of your site in one file. AI systems can read this to understand everything without crawling each page. Served at /llms-full.txt. Leave the content field empty to disable.', 'dennis-geo' ); ?></p>
						</div>
						<?php if ( Dennis_SEO_Audit::quick_score() < 80 ) : ?>
						<button type="button" class="dseo-ai-btn dseo-ai-btn-sm" onclick="document.getElementById('dseo-ai-modal').classList.add('active');">
							<span class="dashicons dashicons-superhero-alt"></span> <?php esc_html_e( 'Generate with AI', 'dennis-geo' ); ?>
						</button>
						<?php else : ?>
						<a href="https://dennisgeo.com/wordpress-ai-visibility-audit.html" target="_blank" class="dseo-ai-btn dseo-ai-btn-sm" style="background:linear-gradient(135deg,#00a32a,#007017); box-shadow:none;">
							<span class="dashicons dashicons-awards"></span> <?php esc_html_e( 'Complete GEO Audit?', 'dennis-geo' ); ?>
						</a>
						<?php endif; ?>
					</div>

					<table class="form-table">
						<?php $this->render_textarea_large( $opts, 'llms_full_content', __( 'llms-full.txt Content', 'dennis-geo' ), __( 'Full site content in Markdown format. Leave empty to disable /llms-full.txt.', 'dennis-geo' ) ); ?>
					</table>
				</div>

				<!-- AI Crawlers Panel -->
				<div class="dseo-settings-panel" data-spanel="crawlers">
					<h2><?php esc_html_e( 'AI Crawler Management', 'dennis-geo' ); ?></h2>
					<p class="description"><?php esc_html_e( 'Control which AI crawlers can access your site. Rules are appended to robots.txt automatically.', 'dennis-geo' ); ?></p>

					<table class="dseo-crawler-table">
						<thead>
							<tr>
								<th><?php esc_html_e( 'Crawler', 'dennis-geo' ); ?></th>
								<th><?php esc_html_e( 'Operator', 'dennis-geo' ); ?></th>
								<th><?php esc_html_e( 'Access', 'dennis-geo' ); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php
							$crawlers = array(
								'crawler_gptbot'            => array( 'GPTBot', 'OpenAI', 'Training + ChatGPT browsing' ),
								'crawler_chatgpt_user'      => array( 'ChatGPT-User', 'OpenAI', 'Real-time browsing' ),
								'crawler_google_extended'   => array( 'Google-Extended', 'Google', 'Gemini AI training' ),
								'crawler_claudebot'         => array( 'ClaudeBot', 'Anthropic', 'Claude training' ),
								'crawler_perplexitybot'     => array( 'PerplexityBot', 'Perplexity', 'Search indexing' ),
								'crawler_applebot_extended' => array( 'Applebot-Extended', 'Apple', 'Apple Intelligence' ),
								'crawler_meta_external'     => array( 'Meta-ExternalAgent', 'Meta', 'Meta AI training' ),
								'crawler_ccbot'             => array( 'CCBot', 'Common Crawl', 'Open dataset' ),
								'crawler_bytespider'        => array( 'Bytespider', 'ByteDance', 'TikTok AI features' ),
								'crawler_amazonbot'         => array( 'Amazonbot', 'Amazon', 'Alexa / Amazon AI' ),
								'crawler_anthropic_ai'      => array( 'anthropic-ai', 'Anthropic', 'Claude web fetching' ),
								'crawler_cohere_ai'         => array( 'cohere-ai', 'Cohere', 'Cohere model training' ),
							);
							foreach ( $crawlers as $key => $info ) :
								$val = $opts[ $key ] ?? 'allow';
							?>
							<tr>
								<td>
									<span class="crawler-name"><?php echo esc_html( $info[0] ); ?></span><br>
									<span class="crawler-desc"><?php echo esc_html( $info[2] ); ?></span>
								</td>
								<td><?php echo esc_html( $info[1] ); ?></td>
								<td>
									<select name="<?php echo esc_attr( self::OPTION_KEY . "[{$key}]" ); ?>">
										<option value="allow" <?php selected( $val, 'allow' ); ?>><?php esc_html_e( 'Allow', 'dennis-geo' ); ?></option>
										<option value="block" <?php selected( $val, 'block' ); ?>><?php esc_html_e( 'Block', 'dennis-geo' ); ?></option>
									</select>
								</td>
							</tr>
							<?php endforeach; ?>
						</tbody>
					</table>

					<div style="margin-top:24px;">
						<table class="form-table">
							<?php $this->render_textarea( $opts, 'custom_robots_rules', __( 'Custom robots.txt Rules', 'dennis-geo' ), __( 'Appended as-is to robots.txt. Advanced users only.', 'dennis-geo' ) ); ?>
						</table>
					</div>
				</div>

				<div style="margin-top:12px;padding:12px 16px;background:#f6f7f7;border:1px solid #e0e0e0;border-radius:4px;">
					<label style="display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;">
						<input type="checkbox" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[keep_data_on_uninstall]" value="1" <?php checked( ! empty( $opts['keep_data_on_uninstall'] ) ); ?>>
						<strong><?php esc_html_e( 'Keep all data when the plugin is deleted', 'dennis-geo' ); ?></strong>
						<span style="color:#757575;font-size:12px;">— <?php esc_html_e( 'Uncheck to remove all settings and per-page fields on uninstall.', 'dennis-geo' ); ?></span>
					</label>
				</div>

				<?php submit_button( __( 'Save All Settings', 'dennis-geo' ) ); ?>
			</form>

			<!-- Import / Export (standalone section below settings) -->
			<div style="margin-top:24px; padding:24px; background:#f7f5ee; border:1px solid #e0e0e0; border-radius:6px;">
				<h2 style="margin:0 0 6px; font-size:16px; font-weight:600;"><?php esc_html_e( 'Import / Export SEO Data', 'dennis-geo' ); ?></h2>
				<p style="margin:0 0 18px; font-size:13px; color:#50575e;"><?php esc_html_e( 'Transfer all SEO settings and per-page data between sites. URLs are automatically converted during import.', 'dennis-geo' ); ?></p>

				<div style="display:flex; gap:16px; flex-wrap:wrap;">
					<!-- Export -->
					<div style="flex:1; min-width:280px; padding:18px; background:#fff; border:1px solid #e0e0e0; border-radius:4px;">
						<h3 style="margin:0 0 8px; font-size:13px; font-weight:600;"><?php esc_html_e( 'Export', 'dennis-geo' ); ?></h3>
						<p style="margin:0 0 12px; font-size:12px; color:#50575e;"><?php esc_html_e( 'Download all settings and page SEO fields as JSON.', 'dennis-geo' ); ?></p>
						<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=dennis_seo_export' ), 'dennis_seo_export' ) ); ?>" class="button button-primary">
							<span class="dashicons dashicons-download" style="margin-top:4px;"></span> <?php esc_html_e( 'Export', 'dennis-geo' ); ?>
						</a>
					</div>

					<!-- Import -->
					<div style="flex:1; min-width:280px; padding:18px; background:#fff; border:1px solid #e0e0e0; border-radius:4px;">
						<h3 style="margin:0 0 8px; font-size:13px; font-weight:600;"><?php esc_html_e( 'Import', 'dennis-geo' ); ?></h3>
						<p style="margin:0 0 12px; font-size:12px; color:#50575e;"><?php esc_html_e( 'Upload a JSON export. Posts matched by slug.', 'dennis-geo' ); ?> <span style="color:#d63638;"><?php esc_html_e( 'This will overwrite all existing Dennis GEO fields.', 'dennis-geo' ); ?></span></p>
						<form method="post" enctype="multipart/form-data" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" id="dseo-import-form" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
							<?php wp_nonce_field( 'dennis_seo_import', 'dennis_seo_import_nonce' ); ?>
							<input type="hidden" name="action" value="dennis_seo_import">
							<input type="file" accept=".json,application/json" id="dseo-import-file" name="dseo_import_file" style="font-size:12px;" />
							<button type="submit" class="button" onclick="return document.getElementById('dseo-import-file').value ? confirm('<?php echo esc_js( __( 'This will overwrite existing SEO data. Continue?', 'dennis-geo' ) ); ?>') : (alert('<?php echo esc_js( __( 'Please select a file first.', 'dennis-geo' ) ); ?>'), false);">
								<span class="dashicons dashicons-upload" style="margin-top:4px;"></span> <?php esc_html_e( 'Import', 'dennis-geo' ); ?>
							</button>
						</form>
					</div>
				</div>

				<?php if ( Dennis_SEO_Yoast_Compat::is_yoast_active() ) : ?>
				<!-- Yoast Migration -->
				<div style="margin-top:16px; padding:18px; background:#fff; border:1px solid #e0e0e0; border-radius:4px;">
					<h3 style="margin:0 0 8px; font-size:13px; font-weight:600;">
						<span class="dashicons dashicons-migrate" style="font-size:16px; width:16px; height:16px; margin-right:4px; color:#2271b1;"></span>
						<?php esc_html_e( 'Migrate from Yoast SEO', 'dennis-geo' ); ?>
					</h3>
					<p style="margin:0 0 14px; font-size:12px; color:#50575e;"><?php esc_html_e( 'Copy selected Yoast fields into Dennis GEO for all pages. Choose which fields to migrate.', 'dennis-geo' ); ?></p>

					<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
						<?php wp_nonce_field( 'dennis_seo_yoast_migrate', 'dennis_seo_yoast_migrate_nonce' ); ?>
						<input type="hidden" name="action" value="dennis_seo_yoast_migrate">

						<div style="display:grid; grid-template-columns:1fr 1fr; gap:6px 24px; margin-bottom:14px;">
							<?php
							$yoast_fields = array(
								'_yoast_wpseo_title'              => __( 'SEO Titles', 'dennis-geo' ),
								'_yoast_wpseo_metadesc'           => __( 'Meta Descriptions', 'dennis-geo' ),
								'_yoast_wpseo_focuskw'            => __( 'Focus Keyphrases', 'dennis-geo' ),
								'_yoast_wpseo_canonical'          => __( 'Canonical URLs', 'dennis-geo' ),
								'_yoast_wpseo_opengraph-title'    => __( 'OG Titles', 'dennis-geo' ),
								'_yoast_wpseo_opengraph-description' => __( 'OG Descriptions', 'dennis-geo' ),
								'_yoast_wpseo_opengraph-image'    => __( 'OG Images', 'dennis-geo' ),
							);
							foreach ( $yoast_fields as $key => $label ) :
								// Count how many posts have this Yoast field.
								global $wpdb;
								$count = (int) $wpdb->get_var( $wpdb->prepare(
									"SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value != ''",
									$key
								) );
							?>
								<label style="display:flex; align-items:center; gap:6px; font-size:12px; color:#1d2327; cursor:pointer;">
									<input type="checkbox" name="dseo_yoast_fields[]" value="<?php echo esc_attr( $key ); ?>" <?php echo $count > 0 ? 'checked' : 'disabled'; ?>>
									<?php echo esc_html( $label ); ?>
									<span style="color:#757575;">(<?php echo esc_html( $count ); ?>)</span>
								</label>
							<?php endforeach; ?>
						</div>

						<button type="submit" class="button" onclick="return confirm('<?php echo esc_js( __( 'This will overwrite all Dennis GEO fields with Yoast data for the selected fields across all pages. This cannot be undone. Continue?', 'dennis-geo' ) ); ?>');">
							<span class="dashicons dashicons-migrate" style="margin-top:4px;"></span> <?php esc_html_e( 'Migrate Selected Fields', 'dennis-geo' ); ?>
						</button>
					</form>
				</div>
				<?php endif; ?>

				<!-- Clear Dennis GEO fields -->
				<div id="dseo-clear-fields" style="margin-top:16px; padding:18px; background:#fff; border:1px solid #e0e0e0; border-radius:4px;">
					<h3 style="margin:0 0 8px; font-size:13px; font-weight:600;">
						<span class="dashicons dashicons-trash" style="font-size:16px; width:16px; height:16px; margin-right:4px; color:#d63638;"></span>
						<?php esc_html_e( 'Clear Dennis GEO Fields', 'dennis-geo' ); ?>
					</h3>
					<p style="margin:0 0 14px; font-size:12px; color:#50575e;"><?php esc_html_e( 'Select what to clear.', 'dennis-geo' ); ?> <span style="color:#d63638;"><?php esc_html_e( 'This cannot be undone.', 'dennis-geo' ); ?></span></p>

					<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" id="dseo-clear-form">
						<?php wp_nonce_field( 'dennis_seo_clear_fields', 'dennis_seo_clear_nonce' ); ?>
						<input type="hidden" name="action" value="dennis_seo_clear_fields">
						<div style="display:flex; flex-direction:column; gap:8px; margin-bottom:14px;">
							<label style="display:flex; align-items:flex-start; gap:6px; font-size:13px; cursor:pointer;">
								<input type="checkbox" name="dseo_clear_pages" value="1" style="margin-top:2px;">
								<span><strong><?php esc_html_e( 'All posts & pages', 'dennis-geo' ); ?></strong> <span style="color:#757575; font-size:12px;">— <?php esc_html_e( 'titles, descriptions, keyphrases, OG, schema, llms.txt entries', 'dennis-geo' ); ?></span></span>
							</label>
							<label style="display:flex; align-items:flex-start; gap:6px; font-size:13px; cursor:pointer;">
								<input type="checkbox" name="dseo_clear_global" value="1" style="margin-top:2px;">
								<span><strong><?php esc_html_e( 'Global settings', 'dennis-geo' ); ?></strong> <span style="color:#757575; font-size:12px;">— <?php esc_html_e( 'business info, schema, social, llms.txt config', 'dennis-geo' ); ?></span></span>
							</label>
						</div>
						<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
							<input type="text" name="dseo_clear_confirm" placeholder="<?php esc_attr_e( 'Type CLEAR to confirm', 'dennis-geo' ); ?>" style="width:200px; font-size:13px;" autocomplete="off">
							<button type="submit" class="button" style="color:#d63638; border-color:#d63638;" onclick="var f=document.getElementById('dseo-clear-form'); var p=f.querySelector('input[name=dseo_clear_pages]').checked; var g=f.querySelector('input[name=dseo_clear_global]').checked; if(!p && !g){alert('<?php echo esc_js( __( 'Select at least one option to clear.', 'dennis-geo' ) ); ?>'); return false;} if(f.querySelector('input[name=dseo_clear_confirm]').value !== 'CLEAR'){alert('<?php echo esc_js( __( 'Please type CLEAR to confirm.', 'dennis-geo' ) ); ?>'); return false;} return confirm('<?php echo esc_js( __( 'Are you sure? This cannot be undone.', 'dennis-geo' ) ); ?>');">
								<span class="dashicons dashicons-trash" style="margin-top:4px;"></span> <?php esc_html_e( 'Clear Selected', 'dennis-geo' ); ?>
							</button>
						</div>
					</form>
				</div>

			</div>
		</div>

		<input type="hidden" name="dseo_active_tab" id="dseo-active-tab" value="<?php
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- read-only tab state, no action taken.
			$active_tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : ( isset( $_POST['dseo_active_tab'] ) ? sanitize_text_field( wp_unslash( $_POST['dseo_active_tab'] ) ) : 'identity' );
			echo esc_attr( $active_tab );
		?>">

		<?php
	}

	// --- Field renderers ---

	private function render_text( array $opts, string $key, string $label, string $desc = '' ): void {
		$val = esc_attr( $opts[ $key ] ?? '' );
		$name = self::OPTION_KEY . "[{$key}]";
		echo '<tr><th scope="row"><label for="dseo-' . esc_attr( $key ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td><input type="text" id="dseo-' . esc_attr( $key ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $val ) . '" class="regular-text">';
		if ( $desc ) {
			echo '<p class="description">' . esc_html( $desc ) . '</p>';
		}
		echo '</td></tr>';
	}

	private function render_image( array $opts, string $key, string $label, string $desc = '' ): void {
		$val  = esc_attr( $opts[ $key ] ?? '' );
		$name = self::OPTION_KEY . "[{$key}]";
		echo '<tr><th scope="row"><label for="dseo-' . esc_attr( $key ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td>';
		echo '<div style="display:flex; gap:8px; align-items:flex-start;">';
		echo '<input type="text" id="dseo-' . esc_attr( $key ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $val ) . '" class="regular-text dseo-image-url">';
		echo '<button type="button" class="button dseo-upload-btn" data-target="dseo-' . esc_attr( $key ) . '">' . esc_html__( 'Select Image', 'dennis-geo' ) . '</button>';
		echo '</div>';
		if ( $val ) {
			echo '<div style="margin-top:8px;"><img src="' . esc_url( $val ) . '" style="max-width:200px; max-height:80px; border:1px solid #ddd; border-radius:4px; padding:2px;" /></div>';
		}
		if ( $desc ) {
			echo '<p class="description">' . esc_html( $desc ) . '</p>';
		}
		echo '</td></tr>';
	}

	private function render_textarea( array $opts, string $key, string $label, string $desc = '' ): void {
		$val  = esc_textarea( $opts[ $key ] ?? '' );
		$name = self::OPTION_KEY . "[{$key}]";
		echo '<tr><th scope="row"><label for="dseo-' . esc_attr( $key ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td><textarea id="dseo-' . esc_attr( $key ) . '" name="' . esc_attr( $name ) . '" rows="3" class="large-text">' . esc_textarea( $val ) . '</textarea>';
		if ( $desc ) {
			echo '<p class="description">' . esc_html( $desc ) . '</p>';
		}
		echo '</td></tr>';
	}

	private function render_textarea_large( array $opts, string $key, string $label, string $desc = '' ): void {
		$val  = esc_textarea( $opts[ $key ] ?? '' );
		$name = self::OPTION_KEY . "[{$key}]";
		echo '<tr><th scope="row"><label for="dseo-' . esc_attr( $key ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td><textarea id="dseo-' . esc_attr( $key ) . '" name="' . esc_attr( $name ) . '" rows="25" style="width:100%; font-family:monospace; font-size:13px; line-height:1.6; padding:16px; border:1px solid #c3c4c7; border-radius:4px; resize:vertical;">' . esc_textarea( $val ) . '</textarea>';
		if ( $desc ) {
			echo '<p class="description">' . esc_html( $desc ) . '</p>';
		}
		echo '</td></tr>';
	}

	private function render_select( array $opts, string $key, string $label, array $options ): void {
		$val  = $opts[ $key ] ?? '';
		$name = self::OPTION_KEY . "[{$key}]";
		echo '<tr><th scope="row"><label for="dseo-' . esc_attr( $key ) . '">' . esc_html( $label ) . '</label></th>';
		echo '<td><select id="dseo-' . esc_attr( $key ) . '" name="' . esc_attr( $name ) . '">';
		foreach ( $options as $opt_val => $opt_label ) {
			echo '<option value="' . esc_attr( $opt_val ) . '"' . selected( $val, $opt_val, false ) . '>' . esc_html( $opt_label ) . '</option>';
		}
		echo '</select></td></tr>';
	}

	private function render_checkbox( array $opts, string $key, string $label ): void {
		$checked = ! empty( $opts[ $key ] );
		$name    = self::OPTION_KEY . "[{$key}]";
		echo '<tr><th scope="row">' . esc_html( $label ) . '</th>';
		echo '<td><label><input type="checkbox" name="' . esc_attr( $name ) . '" value="1"' . checked( $checked, true, false ) . '> ' . esc_html__( 'Enabled', 'dennis-geo' ) . '</label></td></tr>';
	}
}
