<?php
/**
 * Plugin Name: CloudScale Cyber & DevTools
 * Description: Free AI penetration testing, brute-force protection, 2FA, passkeys, AI site audit, AI debugging, performance monitor, SMTP, SQL tool, server logs, vulnerability scanner, and Cloudflare uptime monitor. Free with your own API key, or an optional managed-AI subscription if you have none.
 * Version: 1.10.150
 * Author: CloudScale
 * Author URI: https://cloudscale.consulting
 * License: GPL-2.0-or-later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Requires at least: 6.2
 * Requires PHP: 7.4
 * Text Domain: cloudscale-cyber-devtools
 *
 * @package CloudScale_DevTools
 * @since   1.0.0
 */

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

// Pricing — update these two constants to reprice the Managed AI subscription everywhere.
define( 'CSDT_PRICE_USD', '10.00' );  // exact charge amount sent to PayPal / Paystack
define( 'CSDT_PRICE_TAG', '$10' );    // display label used in UI strings

require_once plugin_dir_path( __FILE__ ) . 'includes/class-cs-passkey.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-ai-dispatcher.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-csp.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-sec-headers.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-site-audit.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-vuln-scan.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-geo.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-login.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-smtp.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-perf-monitor.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-custom-404.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-thumbnails.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-optimizer.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-threat-monitor.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-monitor.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-test-accounts.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-uptime.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-cloudscale-telegram.php';
CloudScale_Telegram::register_ajax();
require_once plugin_dir_path( __FILE__ ) . 'includes/class-cloudscale-licence.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-code-migrator.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-git-provider.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-github-sync.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-managed-ai.php';

/**
 * Returns the singleton CloudScale_Licence instance for the Cyber DevTools plugin.
 */
function csdt_licence(): CloudScale_Licence {
    static $inst = null;
    if ( null !== $inst ) {
		return $inst; }

    $opt_key = CSDT_Managed_AI::OPTION;
    $map_get = static function () use ( $opt_key ): array {
        $o = get_option( $opt_key, array() );
        return array(
            'license_key'       => (string) ( $o['proxy_license_key'] ?? '' ),
            'status'            => (string) ( $o['proxy_status'] ?? '' ),
            'session_id'        => (string) ( $o['proxy_session_id'] ?? '' ),
            'email'             => (string) ( $o['proxy_email'] ?? '' ),
            'subscription_code' => (string) ( $o['proxy_subscription_code'] ?? '' ),
            'usage'             => (int) ( $o['proxy_usage'] ?? 0 ),
            'limit'             => (int) ( $o['proxy_limit'] ?? CSDT_Managed_AI::DEFAULT_LIMIT ),
            'boost'             => (int) ( $o['proxy_boost'] ?? 0 ),
            'reset_date'        => (string) ( $o['proxy_reset_date'] ?? '' ),
            'status_ts'         => (int) ( $o['proxy_status_ts'] ?? 0 ),
            'subscribed_at'     => (string) ( $o['proxy_subscribed_at'] ?? '' ),
        );
    };
    $map_set = static function ( array $s ) use ( $opt_key ): void {
        $o      = get_option( $opt_key, array() );
        $fields = array(
            'license_key'       => 'proxy_license_key',
			'status'            => 'proxy_status',
            'session_id'        => 'proxy_session_id',
			'email'             => 'proxy_email',
            'subscription_code' => 'proxy_subscription_code',
            'usage'             => 'proxy_usage',
			'limit'             => 'proxy_limit',
			'boost'             => 'proxy_boost',
            'reset_date'        => 'proxy_reset_date',
			'status_ts'         => 'proxy_status_ts',
            'subscribed_at'     => 'proxy_subscribed_at',
        );
        foreach ( $fields as $norm => $stored ) {
            if ( array_key_exists( $norm, $s ) ) {
				$o[ $stored ] = $s[ $norm ]; }
        }
        update_option( $opt_key, $o );
    };

    $inst = new CloudScale_Licence(
        array(
			'product'           => 'cyber',
			'nonce_name'        => CSDT_Managed_AI::NONCE,
			'action_prefix'     => 'csdt_managed_ai',
			'plugin_name'       => 'Cyber and Dev Tools',
			'paypal_mode'       => ( defined( 'PAYPAL_MODE' ) ? PAYPAL_MODE : ( getenv( 'PAYPAL_MODE' ) ?: 'live' ) ),
			'paypal_client_id'  => ( 'live' === ( defined( 'PAYPAL_MODE' ) ? PAYPAL_MODE : ( getenv( 'PAYPAL_MODE' ) ?: 'live' ) ) ) ? ( defined( 'PAYPAL_LIVE_CLIENT_ID' ) ? PAYPAL_LIVE_CLIENT_ID : getenv( 'PAYPAL_LIVE_CLIENT_ID' ) ) : ( defined( 'PAYPAL_SANDBOX_CLIENT_ID' ) ? PAYPAL_SANDBOX_CLIENT_ID : getenv( 'PAYPAL_SANDBOX_CLIENT_ID' ) ),
			'paypal_secret'     => ( 'live' === ( defined( 'PAYPAL_MODE' ) ? PAYPAL_MODE : ( getenv( 'PAYPAL_MODE' ) ?: 'live' ) ) ) ? ( defined( 'PAYPAL_LIVE_SECRET' ) ? PAYPAL_LIVE_SECRET : getenv( 'PAYPAL_LIVE_SECRET' ) ) : ( defined( 'PAYPAL_SANDBOX_SECRET' ) ? PAYPAL_SANDBOX_SECRET : getenv( 'PAYPAL_SANDBOX_SECRET' ) ),
			'paypal_plan_id'    => ( 'live' === ( defined( 'PAYPAL_MODE' ) ? PAYPAL_MODE : ( getenv( 'PAYPAL_MODE' ) ?: 'live' ) ) ) ? ( defined( 'PAYPAL_DEVTOOLS_LIVE_PLAN_ID' ) ? PAYPAL_DEVTOOLS_LIVE_PLAN_ID : getenv( 'PAYPAL_DEVTOOLS_LIVE_PLAN_ID' ) ) : ( defined( 'PAYPAL_DEVTOOLS_SANDBOX_PLAN_ID' ) ? PAYPAL_DEVTOOLS_SANDBOX_PLAN_ID : getenv( 'PAYPAL_DEVTOOLS_SANDBOX_PLAN_ID' ) ),
			'paypal_amount'       => CSDT_PRICE_USD,
			'paypal_billing_text' => 'Billed ' . CSDT_PRICE_TAG . ' USD/month · Cancel anytime',
			'paypal_currency'   => 'USD',
			'paypal_return_url' => admin_url( 'tools.php?page=cloudscale-cyber-devtools&cs_paypal_return=1' ),
			'paypal_cancel_url' => admin_url( 'tools.php?page=cloudscale-cyber-devtools' ),
			'get_state'         => $map_get,
			'set_state'         => $map_set,
			'action_aliases'    => array(
				'csdt_managed_ai_poll_session'   => 'poll',
				'csdt_managed_ai_refresh_status' => 'sync_status',
			),
        )
    );
    return $inst;
}

// Enable DB query saving for admin and REST requests only when CS Monitor is active.
// Scoped to non-frontend contexts to avoid memory overhead on public page loads.
if ( ! defined( 'SAVEQUERIES' ) && get_option( 'csdt_devtools_perf_monitor_enabled', '1' ) !== '0' ) {
    $csdt_is_admin_ctx = ( defined( 'WP_ADMIN' ) && WP_ADMIN )
        || ( defined( 'REST_REQUEST' ) && REST_REQUEST )
        || ( defined( 'WP_CLI' ) && WP_CLI )
        || ( isset( $_SERVER['REQUEST_URI'] ) && strpos( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), '/wp-admin' ) !== false ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
    if ( $csdt_is_admin_ctx ) {
        define( 'SAVEQUERIES', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- defining WP core constant
    }
    unset( $csdt_is_admin_ctx );
}

/**
 * In-process REST save diagnostic state.
 * Uses static class properties (PHP process memory) instead of transients so
 * it works regardless of whether Redis, Memcached, or the DB stores transients.
 * Populated by hooks registered in CloudScale_DevTools::init().
 */
class CSDT_REST_Diag {
    public static $post_id      = 0;
    public static $dispatched   = false;
    public static $pre_inserted = false;
    public static $inserted     = false;
    public static $served       = false;
    public static $intercepted  = false;
    public static $ob_stray     = '';
    public static $json_err     = '';
    public static $uri          = '';
}

/**
 * CloudScale Code Block, main plugin class.
 *
 * Handles block registration, shortcode, admin tools, settings,
 * the code block migration tool, and the SQL command tool.
 *
 * @package CloudScale_DevTools
 * @since   1.0.0
 */
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- CloudScale_ is this plugin's prefix
class CloudScale_DevTools {

    const VERSION      = '1.10.150';
    const HLJS_VERSION = '11.11.1';
    const HLJS_CDN     = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/';
    const TOOLS_SLUG   = 'cloudscale-cyber-devtools';

    // Nonce action strings, single source of truth used across all extracted classes.
    const MIGRATE_NONCE    = 'csdt_devtools_code_migrate_action';
    const SECURITY_NONCE   = 'csdt_devtools_security_nonce';
    const OPTIMIZER_NONCE  = 'csdt_optimizer_nonce';
    const LOGS_NONCE       = 'csdt_devtools_server_logs';
    const SQL_NONCE        = 'csdt_devtools_sql_nonce';
    const DEBUG_NONCE      = 'csdt_debug_nonce';
    const FPM_NONCE        = 'csdt_fpm_nonce';
    const SITE_AUDIT_NONCE = 'csdt_site_audit_nonce';
    const PERF_NONCE       = 'csdt_devtools_perf_monitor_nonce';
    const LOGIN_NONCE      = 'csdt_devtools_login_nonce';
    const SMTP_NONCE       = 'csdt_devtools_smtp_nonce';

    // Auth transient key prefixes (suffixed at call-site).
    const LOGIN_2FA_TRANSIENT    = 'csdt_devtools_2fa_pending_';
    const LOGIN_OTP_TRANSIENT    = 'csdt_devtools_2fa_otp_';
    const EMAIL_VERIFY_TRANSIENT = 'csdt_devtools_email_verify_';
    const TOTP_CHARS             = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

    // Option / REST constants.
    const CUSTOM_404_OPTION  = 'csdt_devtools_custom_404';
    const SCHEME_404_OPTION  = 'csdt_devtools_404_scheme';
    const HISCORE_NS         = 'csdt-devtools/v1';
    const SCORE_NONCE_ACTION = 'csdt_devtools_score_post';

    /**
     * Returns the theme registry mapping slugs to CDN filenames and colour values.
     *
     * Each entry maps a slug to its dark and light CDN filenames,
     * display label, and background colours for the wrapper/toolbar.
     *
     * @since  1.7.0
     * @return array<string, array<string, string>>
     */
    public static function get_theme_registry(): array {
        return array(
            'atom-one'         => array(
                'label'         => 'Atom One',
                'dark_css'      => 'atom-one-dark',
                'light_css'     => 'atom-one-light',
                'dark_bg'       => '#282c34',
                'dark_toolbar'  => '#21252b',
                'light_bg'      => '#fafafa',
                'light_toolbar' => '#e8eaed',
            ),
            'github'           => array(
                'label'         => 'GitHub',
                'dark_css'      => 'github-dark',
                'light_css'     => 'github',
                'dark_bg'       => '#24292e',
                'dark_toolbar'  => '#1f2428',
                'light_bg'      => '#fff',
                'light_toolbar' => '#f6f8fa',
            ),
            'monokai'          => array(
                'label'         => 'Monokai',
                'dark_css'      => 'monokai',
                'light_css'     => 'atom-one-light',
                'dark_bg'       => '#272822',
                'dark_toolbar'  => '#1e1f1c',
                'light_bg'      => '#fafafa',
                'light_toolbar' => '#e8eaed',
            ),
            'nord'             => array(
                'label'         => 'Nord',
                'dark_css'      => 'nord',
                'light_css'     => 'atom-one-light',
                'dark_bg'       => '#2e3440',
                'dark_toolbar'  => '#272c36',
                'light_bg'      => '#fafafa',
                'light_toolbar' => '#e8eaed',
            ),
            'dracula'          => array(
                'label'         => 'Dracula',
                'dark_css'      => 'dracula',
                'light_css'     => 'atom-one-light',
                'dark_bg'       => '#282a36',
                'dark_toolbar'  => '#21222c',
                'light_bg'      => '#fafafa',
                'light_toolbar' => '#e8eaed',
            ),
            'tokyo-night'      => array(
                'label'         => 'Tokyo Night',
                'dark_css'      => 'tokyo-night-dark',
                'light_css'     => 'tokyo-night-light',
                'dark_bg'       => '#1a1b26',
                'dark_toolbar'  => '#16161e',
                'light_bg'      => '#d5d6db',
                'light_toolbar' => '#c8c9ce',
            ),
            'vs2015'           => array(
                'label'         => 'VS 2015 / VS Code',
                'dark_css'      => 'vs2015',
                'light_css'     => 'vs',
                'dark_bg'       => '#1e1e1e',
                'dark_toolbar'  => '#181818',
                'light_bg'      => '#fff',
                'light_toolbar' => '#f3f3f3',
            ),
            'stackoverflow'    => array(
                'label'         => 'Stack Overflow',
                'dark_css'      => 'stackoverflow-dark',
                'light_css'     => 'stackoverflow-light',
                'dark_bg'       => '#1c1b1b',
                'dark_toolbar'  => '#151414',
                'light_bg'      => '#f6f6f6',
                'light_toolbar' => '#e8e8e8',
            ),
            'night-owl'        => array(
                'label'         => 'Night Owl',
                'dark_css'      => 'night-owl',
                'light_css'     => 'atom-one-light',
                'dark_bg'       => '#011627',
                'dark_toolbar'  => '#001122',
                'light_bg'      => '#fafafa',
                'light_toolbar' => '#e8eaed',
            ),
            'gruvbox'          => array(
                'label'         => 'Gruvbox',
                'dark_css'      => 'base16/gruvbox-dark-hard',
                'light_css'     => 'base16/gruvbox-light-hard',
                'dark_bg'       => '#1d2021',
                'dark_toolbar'  => '#171819',
                'light_bg'      => '#f9f5d7',
                'light_toolbar' => '#ece8c8',
            ),
            'solarized'        => array(
                'label'         => 'Solarized',
                'dark_css'      => 'base16/solarized-dark',
                'light_css'     => 'base16/solarized-light',
                'dark_bg'       => '#002b36',
                'dark_toolbar'  => '#002530',
                'light_bg'      => '#fdf6e3',
                'light_toolbar' => '#eee8d5',
            ),
            'panda'            => array(
                'label'         => 'Panda',
                'dark_css'      => 'panda-syntax-dark',
                'light_css'     => 'panda-syntax-light',
                'dark_bg'       => '#292a2b',
                'dark_toolbar'  => '#222324',
                'light_bg'      => '#e6e6e6',
                'light_toolbar' => '#d9d9d9',
            ),
            'tomorrow'         => array(
                'label'         => 'Tomorrow Night',
                'dark_css'      => 'tomorrow-night-bright',
                'light_css'     => 'atom-one-light',
                'dark_bg'       => '#000',
                'dark_toolbar'  => '#0a0a0a',
                'light_bg'      => '#fafafa',
                'light_toolbar' => '#e8eaed',
            ),
            'shades-of-purple' => array(
                'label'         => 'Shades of Purple',
                'dark_css'      => 'shades-of-purple',
                'light_css'     => 'atom-one-light',
                'dark_bg'       => '#2d2b55',
                'dark_toolbar'  => '#252347',
                'light_bg'      => '#fafafa',
                'light_toolbar' => '#e8eaed',
            ),
        );
    }

    private static $instance_count  = 0;
    private static $assets_enqueued = false;

    /**
     * Returns the real client IP.
     *
     * Only honours HTTP_CF_CONNECTING_IP when the immediate peer (REMOTE_ADDR)
     * is itself a Cloudflare edge IP. Without that gate, any attacker can
     * forge an arbitrary value in the header and bypass every IP-based
     * defence in the plugin (blocklist, brute-force lockout, rate limits,
     * alert attribution). HTTP_X_FORWARDED_FOR is never trusted, there's no
     * way to know which proxy in the chain actually set it.
     *
     * Sites that sit behind a non-Cloudflare trusted proxy can register their
     * proxy IPs / CIDRs via the 'csdt_trusted_proxy_cidrs' filter.
     *
     * @return string IPv4 or IPv6 address, or empty string when undetermined.
     */
    public static function get_client_ip(): string {
        $remote = isset( $_SERVER['REMOTE_ADDR'] )
            ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) )
            : '';
        if ( ! filter_var( $remote, FILTER_VALIDATE_IP ) ) {
            return '';
        }

        // If the peer is a Cloudflare edge (or admin-configured trusted proxy),
        // we can trust the HTTP_CF_CONNECTING_IP header it set.
        if ( self::ip_in_trusted_proxy_ranges( $remote ) && ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
            $cf = sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) );
            // Strip any list (some proxies append; CF doesn't).
            $cf = explode( ',', $cf )[0];
            $cf = trim( $cf );
            if ( filter_var( $cf, FILTER_VALIDATE_IP ) ) {
                return $cf;
            }
        }
        return $remote;
    }

    /**
     * Tests whether $ip is within Cloudflare's published IPv4/IPv6 ranges
     * (or any admin-supplied trusted-proxy CIDR added via the
     * 'csdt_trusted_proxy_cidrs' filter).
     */
    private static function ip_in_trusted_proxy_ranges( string $ip ): bool {
        // Cloudflare published IP ranges. Stable but not eternal, refresh
        // when CF announces a change. See the Cloudflare IP range documentation
        // (ips-v4 and ips-v6 pages on the Cloudflare website).
        $cidrs = array(
            // IPv4
            '173.245.48.0/20',
            '103.21.244.0/22',
            '103.22.200.0/22',
            '103.31.4.0/22',
            '141.101.64.0/18',
            '108.162.192.0/18',
            '190.93.240.0/20',
            '188.114.96.0/20',
            '197.234.240.0/22',
            '198.41.128.0/17',
            '162.158.0.0/15',
            '104.16.0.0/13',
            '104.24.0.0/14',
            '172.64.0.0/13',
            '131.0.72.0/22',
            // IPv6
            '2400:cb00::/32',
            '2606:4700::/32',
            '2803:f800::/32',
            '2405:b500::/32',
            '2405:8100::/32',
            '2a06:98c0::/29',
            '2c0f:f248::/32',
        );
        /** @var string[] $cidrs */
        $cidrs = apply_filters( 'csdt_trusted_proxy_cidrs', $cidrs );

        foreach ( $cidrs as $cidr ) {
            if ( self::ip_in_cidr( $ip, $cidr ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns true when $ip is contained in $cidr. Supports IPv4 and IPv6.
     */
    private static function ip_in_cidr( string $ip, string $cidr ): bool {
        if ( strpos( $cidr, '/' ) === false ) {
            return false;
        }
        list( $subnet, $bits ) = explode( '/', $cidr, 2 );
        $bits                  = (int) $bits;
        $ip_bin                = @inet_pton( $ip );
        $subnet_bin            = @inet_pton( $subnet );
        if ( $ip_bin === false || $subnet_bin === false || strlen( $ip_bin ) !== strlen( $subnet_bin ) ) {
            return false;
        }
        $total_bits = strlen( $ip_bin ) * 8;
        if ( $bits < 0 || $bits > $total_bits ) {
            return false;
        }
        $full_bytes = intdiv( $bits, 8 );
        $rem_bits   = $bits % 8;
        if ( $full_bytes > 0 && substr( $ip_bin, 0, $full_bytes ) !== substr( $subnet_bin, 0, $full_bytes ) ) {
            return false;
        }
        if ( $rem_bits === 0 ) {
            return true;
        }
        $mask = chr( 0xFF << ( 8 - $rem_bits ) & 0xFF );
        return ( $ip_bin[ $full_bytes ] & $mask ) === ( $subnet_bin[ $full_bytes ] & $mask );
    }

    /**
     * Registers a WP-Cron action with a Throwable safety wrapper.
     *
     * An uncaught exception inside a cron callback causes PHP-FPM to SIGSEGV,
     * taking the entire worker pool down. This wrapper catches any Throwable,
     * logs it, and returns cleanly so the worker survives.
     */
    private static function cron_action( string $hook, callable $callback ): void {
        add_action(
            $hook,
            static function () use ( $hook, $callback ): void {
				try {
					$callback();
				} catch ( \Throwable $e ) {
					// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- cron exception handler; only path to surface crashes
					error_log(
                        sprintf(
                            '[CSDT] cron "%s" exception (%s): %s in %s line %d',
                            $hook,
                            get_class( $e ),
                            $e->getMessage(),
                            $e->getFile(),
                            $e->getLine()
                        )
					);
				}
			}
        );
    }

    /**
     * Registers all plugin hooks.
     *
     * @since  1.0.0
     * @return void
     */
    /**
     * Adopt fixes that older versions installed as must-use plugin files into the
     * option flags used now, and delete the orphaned files. Runs once per site.
     *
     * @return void
     */
    private static function migrate_legacy_mu_plugin_fixes() {
        $mu_dir = WPMU_PLUGIN_DIR;
        $map    = array(
            'csdt-disable-xmlrpc.php'  => 'csdt_disable_xmlrpc',
            'csdt-no-file-edit.php'    => 'csdt_disable_file_edit',
            'csdt-limit-revisions.php' => 'csdt_limit_revisions',
            'csdt-no-user-enum.php'    => 'csdt_block_user_enum',
        );
        foreach ( $map as $file => $option ) {
            $path = $mu_dir . '/' . $file;
            if ( file_exists( $path ) ) {
                update_option( $option, '1' );
                wp_delete_file( $path );
            }
        }
        // These two only relocated the error log via an option that is already set
        // when the fix was applied, so just remove the orphaned files.
        foreach ( array( 'csdt-secure-logs.php', 'csdt-php-error-log.php' ) as $file ) {
            $path = $mu_dir . '/' . $file;
            if ( file_exists( $path ) ) {
                wp_delete_file( $path );
            }
        }
        update_option( 'csdt_muplugin_fixes_migrated', '1' );
    }

    public static function init() {
        self::maybe_migrate_autoload();
        CSDT_SMTP::maybe_migrate_prefix();
        CSDT_SMTP::maybe_migrate_smtp_prefix();
        CSDT_SMTP::maybe_migrate_usermeta_prefix();
        add_filter( 'xmlrpc_enabled', '__return_false' );
        remove_action( 'init', '_register_theme_block_patterns' );

        // One-click security hardening, option-driven filters applied at every boot
        if ( get_option( 'csdt_block_basic_auth', '0' ) === '1' ) {
            // Hard block, no app passwords / Basic Auth for anyone, including test accounts
            add_filter( 'wp_is_application_passwords_available', '__return_false' );
        } elseif ( get_option( 'csdt_devtools_disable_app_passwords', '0' ) === '1' ) {
            if ( get_option( 'csdt_test_accounts_enabled', '0' ) === '1' ) {
                // Test-account mode: block per-user (not site-wide) so test accounts still authenticate
                add_filter( 'wp_is_application_passwords_available_for_user', array( 'CSDT_Test_Accounts', 'filter_app_pw_for_user' ), 10, 2 );
            } else {
                add_filter( 'wp_is_application_passwords_available', '__return_false' );
            }
        }
        // Test-account cleanup cron + single-use hook (always when feature is enabled)
        if ( get_option( 'csdt_test_accounts_enabled', '0' ) === '1' ) {
            add_action( 'application_password_did_authenticate', array( 'CSDT_Test_Accounts', 'test_account_after_auth' ), 10, 2 );
        }
        if ( get_option( 'csdt_devtools_hide_wp_version', '0' ) === '1' ) {
            remove_action( 'wp_head', 'wp_generator' );
            add_filter( 'the_generator', '__return_empty_string' );
            // Strip ?ver= query strings from enqueued scripts/styles to prevent version fingerprinting
            add_filter( 'style_loader_src', array( 'CSDT_Thumbnails', 'strip_asset_ver' ), 9999 );
            add_filter( 'script_loader_src', array( 'CSDT_Thumbnails', 'strip_asset_ver' ), 9999 );
        }

        // One-click "Quick Fix" hardening, applied in-plugin from option flags.
        // These replace must-use plugin files: a WordPress.org-hosted plugin may
        // not write executable code into wp-content/mu-plugins, so each fix is
        // applied here at boot instead. See CSDT_Site_Audit::ajax_apply_quick_fix().
        if ( get_option( 'csdt_disable_xmlrpc', '0' ) === '1' ) {
            add_filter( 'xmlrpc_enabled', '__return_false' );
        }
        if ( get_option( 'csdt_disable_file_edit', '0' ) === '1' && ! defined( 'DISALLOW_FILE_EDIT' ) ) {
            define( 'DISALLOW_FILE_EDIT', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- defining WP core constant
        }
        if ( get_option( 'csdt_limit_revisions', '0' ) === '1' ) {
            add_filter(
                'wp_revisions_to_keep',
                static function () {
					return 5;
				},
                99
            );
        }
        if ( get_option( 'csdt_block_user_enum', '0' ) === '1' ) {
            add_action(
                'template_redirect',
                static function () {
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only redirect, no data mutation
					if ( ! is_admin() && isset( $_GET['author'] ) ) {
						wp_safe_redirect( home_url( '/' ), 301 );
						exit;
					}
				}
            );
        }
        // Relocate the PHP error log off the web root when an admin has configured a
        // safe path via the debug-log / php-error-log Quick Fix (replaces the former
        // csdt-secure-logs / csdt-php-error-log mu-plugins).
        $csdt_err_log = (string) ( get_option( 'csdt_php_error_log_path', '' ) ?: get_option( 'csdt_debug_log_path', '' ) );
        if ( '' !== $csdt_err_log ) {
            @ini_set( 'error_log', $csdt_err_log ); // phpcs:ignore WordPress.PHP.IniSet.Risky, WordPress.PHP.NoSilencedErrors.Discouraged, Squiz.PHP.DiscouragedFunctions.Discouraged -- ini_set required for SMTP/memory adjustment in this context
        }

        // One-time migration for sites upgraded from a version that installed the
        // above fixes as must-use plugin files: adopt each into its option flag and
        // remove the now-orphaned file.
        if ( is_admin() && '1' !== get_option( 'csdt_muplugin_fixes_migrated', '0' ) ) {
            self::migrate_legacy_mu_plugin_fixes();
        }

        add_action( 'init', array( __CLASS__, 'register_block' ) );
        add_action( 'init', array( __CLASS__, 'register_shortcode' ) );
        add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_convert_script' ) );
        add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_replace_post_script' ) );
        add_action( 'admin_menu', array( __CLASS__, 'add_tools_page' ) );
        add_action( 'wp_dashboard_setup', array( __CLASS__, 'register_dashboard_widget' ) );
        add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
        add_action( 'admin_init', array( __CLASS__, 'redirect_legacy_slug' ) );
        add_action( 'init', array( __CLASS__, 'redirect_legacy_help_url' ), 1 );
        add_action( 'init', array( __CLASS__, 'maybe_lscache_purge' ), 1 );
        add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin_assets' ) );

        // Migration AJAX
        add_action( 'wp_ajax_csdt_devtools_migrate_scan', array( 'CSDT_Code_Migrator', 'ajax_scan' ) );
        add_action( 'wp_ajax_csdt_devtools_migrate_preview', array( 'CSDT_Code_Migrator', 'ajax_preview' ) );
        add_action( 'wp_ajax_csdt_devtools_migrate_single', array( 'CSDT_Code_Migrator', 'ajax_migrate_single' ) );
        add_action( 'wp_ajax_csdt_devtools_migrate_all', array( 'CSDT_Code_Migrator', 'ajax_migrate_all' ) );
        add_action( 'wp_ajax_csdt_devtools_reset_theme_all', array( 'CSDT_Code_Migrator', 'ajax_reset_theme_all' ) );

        // Managed AI (commercial pay-monthly tier), ajax + asset enqueue.
        CSDT_Managed_AI::register();

        // SQL AJAX
        add_action( 'wp_ajax_csdt_devtools_sql_run', array( __CLASS__, 'ajax_sql_run' ) );

        // Settings AJAX
        add_action( 'wp_ajax_csdt_devtools_save_theme_setting', array( 'CSDT_Code_Migrator', 'ajax_save_theme_setting' ) );
        add_action( 'wp_ajax_csdt_devtools_save_perf_monitor', array( 'CSDT_Code_Migrator', 'ajax_save_perf_monitor' ) );
        add_action( 'wp_ajax_csdt_object_cache_toggle', array( 'CSDT_Code_Migrator', 'ajax_object_cache_toggle' ) );
        add_action( 'wp_ajax_csdt_install_apcu', array( 'CSDT_Code_Migrator', 'ajax_install_apcu' ) );

        // Login security AJAX
        add_action( 'wp_ajax_csdt_devtools_login_save', array( 'CSDT_Login', 'ajax_login_save' ) );
        add_action( 'wp_ajax_csdt_devtools_bf_log_fetch', array( 'CSDT_Login', 'ajax_bf_log_fetch' ) );
        add_action( 'wp_ajax_csdt_ip_block', array( 'CSDT_Login', 'ajax_ip_block' ) );
        add_action( 'wp_ajax_csdt_ip_unblock', array( 'CSDT_Login', 'ajax_ip_unblock' ) );
        add_action( 'wp_ajax_csdt_download_dbip', array( 'CSDT_Geo', 'ajax_download_dbip' ) );
        add_action( 'wp_ajax_csdt_save_dbip_settings', array( 'CSDT_Geo', 'ajax_save_dbip_settings' ) );
        self::cron_action( 'csdt_dbip_auto_update', array( 'CSDT_Geo', 'auto_update_run' ) );
        if ( ! wp_next_scheduled( 'csdt_dbip_auto_update' ) ) {
            wp_schedule_event( time(), 'daily', 'csdt_dbip_auto_update' );
        }
        add_action( 'parse_request', array( 'CSDT_Login', 'enforce_ip_blocklist' ), 1 );
        add_action( 'wp_ajax_csdt_ssh_monitor_save', array( 'CSDT_Monitor', 'ajax_ssh_monitor_save' ) );
        add_action( 'wp_ajax_csdt_ssh_log_clear', array( 'CSDT_Monitor', 'ajax_ssh_log_clear' ) );
        add_action( 'wp_ajax_csdt_ssh_fix_permissions', array( 'CSDT_Monitor', 'ajax_ssh_fix_permissions' ) );
        add_action( 'wp_ajax_csdt_csp_domain_hunt', array( __CLASS__, 'ajax_csp_domain_hunt' ) );
        add_action( 'wp_ajax_csdt_bf_self_test', array( 'CSDT_Threat_Monitor', 'ajax_bf_self_test' ) );
        add_action( 'wp_ajax_csdt_devtools_totp_setup_start', array( 'CSDT_Login', 'ajax_totp_setup_start' ) );
        add_action( 'wp_ajax_csdt_devtools_totp_setup_verify', array( 'CSDT_Login', 'ajax_totp_setup_verify' ) );
        add_action( 'wp_ajax_csdt_devtools_2fa_disable', array( 'CSDT_Login', 'ajax_2fa_disable' ) );
        add_action( 'wp_ajax_csdt_devtools_email_2fa_enable', array( 'CSDT_Login', 'ajax_email_2fa_enable' ) );
        add_action( 'admin_init', array( 'CSDT_Login', 'email_2fa_confirm_check' ) );
        add_action( 'after_password_reset', array( 'CSDT_Login', 'on_password_reset' ), 10, 1 );
        add_action( 'profile_update', array( 'CSDT_Login', 'on_profile_update' ), 10, 2 );
        CSDT_DevTools_Passkey::register_hooks();

        // Default Featured Image
        add_action( 'wp_ajax_csdt_save_default_image', array( 'CSDT_Thumbnails', 'ajax_save_default_image' ) );
        add_filter( 'post_thumbnail_html', array( 'CSDT_Thumbnails', 'default_image_html' ), 10, 5 );
        add_filter( 'has_post_thumbnail', array( 'CSDT_Thumbnails', 'default_image_has_thumbnail' ), 10, 3 );
        // Hero image: swap to 1200×630 social format on single post pages.
        add_filter( 'post_thumbnail_html', array( 'CSDT_Thumbnails', 'hero_image_html' ), 11, 5 );
        add_action( 'wp_enqueue_scripts', array( 'CSDT_Thumbnails', 'enqueue_hero_styles' ) );
        // Admin-only Generate Featured Image button (injected via footer, positioned by JS).
        add_action( 'wp_footer', array( 'CSDT_Thumbnails', 'inject_gen_image_button' ), 5 );
        add_action( 'wp_enqueue_scripts', array( 'CSDT_Thumbnails', 'enqueue_frontend_admin_scripts' ) );

        // Thumbnails / Social Preview AJAX
        add_action( 'wp_ajax_csdt_devtools_social_check_url', array( 'CSDT_Thumbnails', 'ajax_social_check_url' ) );
        add_action( 'wp_ajax_csdt_devtools_social_scan_posts', array( 'CSDT_Thumbnails', 'ajax_social_scan_posts' ) );
        add_action( 'wp_ajax_csdt_devtools_social_scan_media', array( 'CSDT_Thumbnails', 'ajax_social_scan_media' ) );
        add_action( 'wp_ajax_csdt_devtools_social_fix_image', array( 'CSDT_Thumbnails', 'ajax_social_fix_image' ) );
        add_action( 'wp_ajax_csdt_devtools_social_generate_formats', array( 'CSDT_Thumbnails', 'ajax_social_generate_formats' ) );
        add_action( 'wp_ajax_csdt_devtools_social_platform_save', array( 'CSDT_Thumbnails', 'ajax_social_platform_save' ) );
        add_action( 'wp_ajax_csdt_devtools_social_fix_all_batch', array( 'CSDT_Thumbnails', 'ajax_social_fix_all_batch' ) );
        add_action( 'wp_ajax_csdt_devtools_social_refresh_stale_batch', array( 'CSDT_Thumbnails', 'ajax_social_refresh_stale_batch' ) );
        add_action( 'wp_ajax_csdt_devtools_social_diagnose_formats', array( 'CSDT_Thumbnails', 'ajax_social_diagnose_formats' ) );
        add_action( 'wp_ajax_csdt_devtools_regen_thumb_scan', array( 'CSDT_Thumbnails', 'ajax_regen_thumb_scan' ) );
        add_action( 'wp_ajax_csdt_devtools_regen_thumb_batch', array( 'CSDT_Thumbnails', 'ajax_regen_thumb_batch' ) );
        add_action( 'save_post_post', array( 'CSDT_Thumbnails', 'on_post_saved' ), 100, 3 );
        // Catches the "publish first, add image later" workflow where set_post_thumbnail()
        // fires after save_post and on_post_saved() misses the new thumbnail ID.
        add_action( 'added_post_meta', array( 'CSDT_Thumbnails', 'on_thumbnail_meta_updated' ), 10, 4 );
        add_action( 'updated_post_meta', array( 'CSDT_Thumbnails', 'on_thumbnail_meta_updated' ), 10, 4 );
        add_action( 'transition_post_status', array( 'CSDT_Thumbnails', 'on_post_status_change' ), 10, 3 );
        add_action( 'admin_notices', array( 'CSDT_Thumbnails', 'social_format_admin_notice' ) );
        // Serve platform-specific og:image based on crawler User-Agent.
        add_action( 'wp_head', array( 'CSDT_Thumbnails', 'output_crawler_og_image' ), 1 );
        add_action( 'wp_head', array( __CLASS__, 'output_ads_dedup_script' ), 0 );
        add_action( 'template_redirect', array( __CLASS__, 'ads_dedup_ob_start' ), 1 );
        if ( get_option( 'csdt_crossorigin_scripts', '0' ) === '1' ) {
            add_filter( 'script_loader_tag', array( __CLASS__, 'add_crossorigin_to_external_scripts' ), 10, 3 );
        }
        add_action( 'wp_ajax_csdt_devtools_social_cf_test', array( 'CSDT_Thumbnails', 'ajax_social_cf_test' ) );
        add_action( 'wp_ajax_csdt_devtools_cf_purge', array( 'CSDT_Thumbnails', 'ajax_cf_purge' ) );
        add_action( 'wp_ajax_csdt_devtools_cf_save', array( 'CSDT_Thumbnails', 'ajax_cf_save' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_save_key', array( 'CSDT_Thumbnails', 'ajax_ai_image_save_key' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_test_key', array( 'CSDT_Thumbnails', 'ajax_ai_image_test_key' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_scan', array( 'CSDT_Thumbnails', 'ajax_ai_image_scan' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_write_prompt', array( 'CSDT_Thumbnails', 'ajax_ai_image_write_prompt' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_generate', array( 'CSDT_Thumbnails', 'ajax_ai_image_generate' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_process', array( 'CSDT_Thumbnails', 'ajax_ai_image_process' ) );
        add_action( 'wp_ajax_nopriv_csdt_devtools_ai_image_process', array( 'CSDT_Thumbnails', 'ajax_ai_image_process' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_poll', array( 'CSDT_Thumbnails', 'ajax_ai_image_poll' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_pick', array( 'CSDT_Thumbnails', 'ajax_ai_image_pick' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_discard', array( 'CSDT_Thumbnails', 'ajax_ai_image_discard' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_save_sysprompt', array( 'CSDT_Thumbnails', 'ajax_ai_image_save_sysprompt' ) );
        add_action( 'wp_ajax_csdt_devtools_ai_image_save_settings', array( 'CSDT_Thumbnails', 'ajax_ai_image_save_settings' ) );

        // SMTP AJAX
        add_action( 'wp_ajax_csdt_devtools_smtp_save', array( 'CSDT_SMTP', 'ajax_smtp_save' ) );
        add_action( 'wp_ajax_csdt_devtools_smtp_test', array( 'CSDT_SMTP', 'ajax_smtp_test' ) );
        add_action( 'wp_ajax_csdt_devtools_smtp_log_clear', array( 'CSDT_SMTP', 'ajax_smtp_log_clear' ) );
        add_action( 'wp_ajax_csdt_devtools_smtp_log_fetch', array( 'CSDT_SMTP', 'ajax_smtp_log_fetch' ) );
        add_action( 'wp_ajax_csdt_devtools_smtp_log_view', array( 'CSDT_SMTP', 'ajax_smtp_log_view' ) );
        add_action( 'wp_ajax_csdt_notify_channels_save', array( 'CSDT_SMTP', 'ajax_notify_channels_save' ) );

        add_action( 'wp_ajax_csdt_devtools_vuln_scan', array( 'CSDT_Vuln_Scan', 'ajax_vuln_scan' ) );
        add_action( 'wp_ajax_csdt_devtools_deep_scan', array( 'CSDT_Site_Audit', 'ajax_deep_scan' ) );
        add_action( 'wp_ajax_csdt_devtools_adhoc_scan', array( 'CSDT_Site_Audit', 'ajax_adhoc_scan' ) );
        add_action( 'wp_ajax_csdt_devtools_adhoc_delete', array( 'CSDT_Site_Audit', 'ajax_adhoc_delete' ) );
        add_action( 'wp_ajax_csdt_devtools_scan_status', array( 'CSDT_Site_Audit', 'ajax_scan_status' ) );
        add_action( 'wp_ajax_csdt_devtools_cancel_scan', array( 'CSDT_Site_Audit', 'ajax_cancel_scan' ) );
        add_action( 'wp_ajax_csdt_devtools_vuln_save_key', array( 'CSDT_Vuln_Scan', 'ajax_vuln_save_key' ) );
        add_action( 'wp_ajax_csdt_devtools_security_test_key', array( 'CSDT_Vuln_Scan', 'ajax_security_test_key' ) );
        add_action( 'wp_ajax_csdt_devtools_server_logs_status', array( __CLASS__, 'ajax_server_logs_status' ) );
        add_action( 'wp_ajax_csdt_devtools_server_logs_fetch', array( __CLASS__, 'ajax_server_logs_fetch' ) );
        add_action( 'wp_ajax_csdt_devtools_logs_setup_php', array( __CLASS__, 'ajax_logs_setup_php' ) );
        add_action( 'wp_ajax_csdt_devtools_logs_fix_mu_perms', array( __CLASS__, 'ajax_logs_fix_mu_perms' ) );
        add_action( 'wp_ajax_csdt_devtools_logs_custom_save', array( __CLASS__, 'ajax_logs_custom_save' ) );
        add_action( 'wp_ajax_csdt_devtools_scan_history', array( 'CSDT_Site_Audit', 'ajax_scan_history' ) );
        add_action( 'wp_ajax_csdt_devtools_save_schedule', array( 'CSDT_Site_Audit', 'ajax_save_schedule' ) );
        add_action( 'wp_ajax_csdt_devtools_save_notify', array( 'CSDT_Site_Audit', 'ajax_save_notify' ) );
        add_action( 'wp_ajax_csdt_devtools_save_alerts', array( __CLASS__, 'ajax_save_alerts' ) );
        add_action( 'wp_ajax_csdt_sec_headers_restore', array( 'CSDT_Security_Headers', 'ajax_sec_headers_restore' ) );
        add_action( 'wp_ajax_csdt_devtools_quick_fix', array( 'CSDT_Site_Audit', 'ajax_apply_quick_fix' ) );
        add_action( 'wp_ajax_csdt_db_prefix_preflight', array( 'CSDT_Site_Audit', 'ajax_db_prefix_preflight' ) );
        add_action( 'wp_ajax_csdt_db_prefix_migrate', array( 'CSDT_Site_Audit', 'ajax_db_prefix_migrate' ) );
        add_action( 'wp_ajax_csdt_db_prefix_rollback', array( 'CSDT_Site_Audit', 'ajax_db_prefix_rollback' ) );
        add_action( 'wp_ajax_csdt_db_orphaned_scan', array( 'CSDT_Optimizer', 'ajax_db_orphaned_scan' ) );
        add_action( 'wp_ajax_csdt_db_identify_table', array( 'CSDT_Optimizer', 'ajax_db_identify_table' ) );
        add_action( 'wp_ajax_csdt_db_archive_tables', array( 'CSDT_Optimizer', 'ajax_db_archive_tables' ) );
        add_action( 'wp_ajax_csdt_db_trash_scan', array( 'CSDT_Optimizer', 'ajax_db_trash_scan' ) );
        add_action( 'wp_ajax_csdt_db_restore_tables', array( 'CSDT_Optimizer', 'ajax_db_restore_tables' ) );
        add_action( 'wp_ajax_csdt_db_drop_tables', array( 'CSDT_Optimizer', 'ajax_db_drop_tables' ) );
        add_action( 'wp_ajax_csdt_sec_headers_save', array( 'CSDT_Security_Headers', 'ajax_sec_headers_save' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_save', array( 'CSDT_CSP', 'ajax_csp_save' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_rollback', array( 'CSDT_CSP', 'ajax_csp_rollback' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_restore', array( 'CSDT_CSP', 'ajax_csp_restore' ) );
        add_action( 'wp_ajax_csdt_scan_headers', array( 'CSDT_Security_Headers', 'ajax_scan_headers' ) );
        add_action( 'wp_ajax_csdt_scan_history_item', array( 'CSDT_Security_Headers', 'ajax_scan_history_item' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_violations_get', array( 'CSDT_CSP', 'ajax_csp_violations_get' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_violations_clear', array( 'CSDT_CSP', 'ajax_csp_violations_clear' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_fixes_get', array( 'CSDT_CSP', 'ajax_csp_fixes_get' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_fixes_clear', array( 'CSDT_CSP', 'ajax_csp_fixes_clear' ) );
        add_action( 'wp_ajax_csdt_devtools_csp_apply_fix', array( 'CSDT_CSP', 'ajax_csp_apply_fix' ) );
        add_action( 'send_headers', array( 'CSDT_CSP', 'output_security_headers' ) );
        add_action( 'wp_ajax_csdt_test_account_create', array( 'CSDT_Test_Accounts', 'ajax_create_test_account' ) );
        add_action( 'wp_ajax_csdt_test_account_revoke', array( 'CSDT_Test_Accounts', 'ajax_revoke_test_account' ) );
        add_action( 'wp_ajax_csdt_test_account_settings_save', array( 'CSDT_Test_Accounts', 'ajax_save_test_account_settings' ) );
        self::cron_action( 'csdt_cleanup_test_accounts', array( 'CSDT_Test_Accounts', 'cleanup_expired_test_accounts' ) );
        add_action( 'wp_ajax_csdt_playwright_role_create', array( 'CSDT_Test_Accounts', 'ajax_create_playwright_role' ) );
        add_action( 'wp_ajax_csdt_playwright_role_delete', array( 'CSDT_Test_Accounts', 'ajax_delete_playwright_role' ) );
        add_filter( 'authenticate', array( 'CSDT_Test_Accounts', 'block_playwright_password_login' ), 30, 2 );
        add_action( 'wp_ajax_csdt_kill_test_sessions', array( 'CSDT_Test_Accounts', 'ajax_kill_test_sessions' ) );
        add_action( 'wp_ajax_csdt_regen_test_secret', array( 'CSDT_Test_Accounts', 'ajax_regen_test_secret' ) );
        add_action( 'wp_ajax_csdt_regen_path_token', array( 'CSDT_Test_Accounts', 'ajax_regen_path_token' ) );
        add_action( 'wp_ajax_csdt_rename_test_user', array( 'CSDT_Test_Accounts', 'ajax_rename_test_user' ) );
        add_action( 'wp_ajax_csdt_rename_wp_user', array( __CLASS__, 'ajax_rename_wp_user' ) );
        add_action( 'wp_ajax_csdt_toggle_block_basic_auth', array( 'CSDT_Test_Accounts', 'ajax_toggle_block_basic_auth' ) );
        add_action( 'wp_ajax_csdt_delete_app_password', array( 'CSDT_Test_Accounts', 'ajax_delete_app_password' ) );
        add_action( 'wp_ajax_csdt_bulk_delete_app_passwords', array( 'CSDT_Test_Accounts', 'ajax_bulk_delete_app_passwords' ) );
        add_action( 'rest_api_init', array( 'CSDT_Test_Accounts', 'register_rest_routes' ) );
        self::cron_action( 'csdt_scheduled_scan', array( 'CSDT_Site_Audit', 'run_scheduled_scan' ) );
        self::cron_action( 'csdt_refresh_home_html', array( 'CSDT_Site_Audit', 'refresh_home_html_cache' ) );
        self::cron_action( 'csdt_ssh_monitor', array( 'CSDT_Monitor', 'monitor_ssh_failures' ) );
        self::cron_action( 'csdt_php_error_monitor', array( 'CSDT_Monitor', 'monitor_php_errors' ) );
        add_action( 'wp_ajax_csdt_php_error_monitor_save', array( 'CSDT_Monitor', 'ajax_php_error_monitor_save' ) );
        add_action( 'wp_ajax_csdt_fpm_monitor_save', array( 'CSDT_Monitor', 'ajax_fpm_monitor_save' ) );
        add_action( 'wp_ajax_csdt_fpm_worker_status', array( 'CSDT_Monitor', 'ajax_fpm_worker_status' ) );
        add_action( 'wp_ajax_csdt_fpm_setup_detect', array( 'CSDT_Monitor', 'ajax_fpm_setup_detect' ) );
        add_action( 'wp_ajax_csdt_fpm_setup_patch', array( 'CSDT_Monitor', 'ajax_fpm_setup_patch' ) );
        add_action( 'wp_ajax_csdt_fpm_worker_detail', array( 'CSDT_Monitor', 'ajax_fpm_worker_detail' ) );
        add_action( 'wp_ajax_csdt_opcache_stats', array( 'CSDT_Monitor', 'ajax_opcache_stats' ) );
        add_action( 'wp_ajax_csdt_opcache_flush', array( 'CSDT_Monitor', 'ajax_opcache_flush' ) );
        add_action( 'wp_ajax_nopriv_csdt_opcache_flush', array( 'CSDT_Monitor', 'ajax_opcache_flush' ) );
        add_action( 'wp_ajax_csdt_lscache_purge', array( 'CSDT_Monitor', 'ajax_lscache_purge' ) );
        add_action( 'wp_ajax_nopriv_csdt_lscache_purge', array( 'CSDT_Monitor', 'ajax_lscache_purge' ) );
        add_action( 'wp_ajax_csdt_sql_http_fix', array( __CLASS__, 'ajax_sql_http_fix' ) );
        // FPM report uses the REST endpoint csdt/v1/fpm-report (CSDT_Monitor::rest_fpm_report).
        self::cron_action( 'csdt_threat_monitor', array( 'CSDT_Threat_Monitor', 'monitor_threats' ) );
        add_action( 'wp_ajax_csdt_threat_monitor_save', array( 'CSDT_Threat_Monitor', 'ajax_threat_monitor_save' ) );
        add_action( 'wp_ajax_csdt_threat_integrity_reset', array( 'CSDT_Threat_Monitor', 'ajax_threat_integrity_reset' ) );
        add_action( 'user_register', array( 'CSDT_Threat_Monitor', 'on_user_registered' ) );
        add_action( 'set_user_role', array( 'CSDT_Threat_Monitor', 'on_set_user_role' ), 10, 3 );
        add_filter( 'cron_schedules', array( 'CSDT_Site_Audit', 'add_cron_schedules' ) );
        add_action( 'wp_ajax_csdt_plugin_stack_scan', array( 'CSDT_Optimizer', 'ajax_plugin_stack_scan' ) );
        add_action( 'wp_ajax_csdt_ai_debug_log', array( 'CSDT_Optimizer', 'ajax_ai_debug_log' ) );
        add_action( 'wp_ajax_csdt_site_audit', array( 'CSDT_Site_Audit', 'ajax_site_audit' ) );
        add_action( 'wp_ajax_csdt_update_risk_scan', array( 'CSDT_Optimizer', 'ajax_update_risk_scan' ) );
        add_action( 'wp_ajax_csdt_update_risk_assess', array( 'CSDT_Optimizer', 'ajax_update_risk_assess' ) );
        add_action( 'wp_ajax_csdt_db_intelligence_scan', array( 'CSDT_Optimizer', 'ajax_db_intelligence_scan' ) );
        add_action( 'wp_ajax_csdt_db_intelligence_fix', array( 'CSDT_Optimizer', 'ajax_db_intelligence_fix' ) );
        add_action( 'wp_ajax_nopriv_csdt_uptime_ping', array( 'CSDT_Uptime', 'ajax_uptime_ping' ) );
        add_action( 'wp_ajax_csdt_uptime_ping', array( 'CSDT_Uptime', 'ajax_uptime_ping' ) );
        add_action( 'wp_ajax_csdt_uptime_setup', array( 'CSDT_Uptime', 'ajax_uptime_setup' ) );
        add_action( 'wp_ajax_csdt_uptime_history', array( 'CSDT_Uptime', 'ajax_uptime_history' ) );
        add_action( 'wp_ajax_csdt_uptime_deploy_worker', array( 'CSDT_Uptime', 'ajax_uptime_deploy_worker' ) );
        add_action( 'wp_ajax_csdt_uptime_save_settings', array( 'CSDT_Uptime', 'ajax_uptime_save_settings' ) );
        add_action( 'wp_ajax_csdt_uptime_test_endpoint', array( 'CSDT_Uptime', 'ajax_uptime_test_endpoint' ) );
        add_action( 'wp_ajax_csdt_uptime_pause_heartbeat', array( 'CSDT_Uptime', 'ajax_uptime_pause_heartbeat' ) );
        self::cron_action( 'csdt_uptime_heartbeat', array( 'CSDT_Uptime', 'push_heartbeat' ) );
        add_filter( 'cron_schedules', array( 'CSDT_Uptime', 'add_cron_schedules' ) );
        // Ensure heartbeat cron is scheduled whenever uptime is enabled
        if ( get_option( 'csdt_uptime_enabled', '0' ) === '1'
            && get_option( 'csdt_uptime_worker_url', '' ) !== ''
            && ! wp_next_scheduled( 'csdt_uptime_heartbeat' ) ) {
            wp_schedule_event( time() + 5, 'csdt_minutely', 'csdt_uptime_heartbeat' );
        }
        add_action( 'admin_bar_menu', array( 'CSDT_Uptime', 'render_admin_bar_badge' ), 100 );
        add_action( 'admin_enqueue_scripts', array( 'CSDT_Uptime', 'admin_bar_badge_styles' ) );
        add_action( 'wp_enqueue_scripts', array( 'CSDT_Uptime', 'admin_bar_badge_styles' ) );

        // CSP nonce injection, only active when nonce mode is enabled.
        // Note: nonces are added to <script> tags only. <link rel="stylesheet">
        // tags are deliberately NOT nonced because style-src uses 'self' +
        // 'unsafe-inline' (no nonce-source listed), so a nonce attribute on a
        // stylesheet is purely decorative, and iOS WKWebView (LinkedIn's
        // in-app browser) mis-handles it, blocking the theme CSS entirely.
        if ( ! is_admin() && get_option( 'csdt_csp_nonces_enabled', '0' ) === '1' ) {
            add_filter( 'script_loader_tag', array( 'CSDT_CSP', 'csp_nonce_script_tag' ), 10, 1 );
            // WP 6.3+ inline script attributes filter
            add_filter( 'wp_inline_script_attributes', array( 'CSDT_CSP', 'csp_nonce_inline_attrs' ), 10, 1 );
            // Output buffer to catch scripts that bypass wp_enqueue (AdSense, theme inline scripts, etc.)
            add_action( 'template_redirect', array( 'CSDT_CSP', 'csp_ob_start' ), 0 );
        }

        // Schedule SSH monitor (default on), ensure cron is running if enabled
        if ( get_option( 'csdt_ssh_monitor_enabled', '1' ) === '1' ) {
            if ( ! wp_next_scheduled( 'csdt_ssh_monitor' ) ) {
                wp_schedule_event( time() + 60, 'csdt_every_1min', 'csdt_ssh_monitor' );
            }
        } else {
            wp_clear_scheduled_hook( 'csdt_ssh_monitor' );
        }

        // Schedule PHP error monitor (default on)
        if ( get_option( 'csdt_php_error_monitor_enabled', '1' ) === '1' ) {
            if ( ! wp_next_scheduled( 'csdt_php_error_monitor' ) ) {
                wp_schedule_event( time() + 300, 'csdt_every_5min', 'csdt_php_error_monitor' );
            }
        } else {
            wp_clear_scheduled_hook( 'csdt_php_error_monitor' );
        }

        // Schedule threat monitor (default on)
        if ( get_option( 'csdt_threat_monitor_enabled', '1' ) === '1' ) {
            if ( ! wp_next_scheduled( 'csdt_threat_monitor' ) ) {
                wp_schedule_event( time() + 300, 'csdt_every_5min', 'csdt_threat_monitor' );
            }
        } else {
            wp_clear_scheduled_hook( 'csdt_threat_monitor' );
        }

        self::cron_action( 'csdt_devtools_run_vuln_scan', array( 'CSDT_Vuln_Scan', 'cron_vuln_scan' ) );
        self::cron_action( 'csdt_devtools_run_deep_scan', array( 'CSDT_Site_Audit', 'cron_deep_scan' ) );

        // Email log, always active so every wp_mail() call is tracked site-wide,
        // regardless of whether our SMTP is enabled.
        add_filter( 'wp_mail', array( 'CSDT_SMTP', 'smtp_log_capture' ) );
        add_action( 'wp_mail_failed', array( 'CSDT_SMTP', 'smtp_log_on_failure' ) );
        // Priority 5 so it runs before phpmailer_configure (priority 10) and sets action_function first.
        add_action( 'phpmailer_init', array( 'CSDT_SMTP', 'smtp_log_set_callback' ), 5 );

        // SMTP, configure phpmailer and override from address only when fully configured.
        // Guard: if host is empty we skip configuration entirely so other plugins' emails
        // continue to work via PHP mail() rather than silently failing.
        if ( get_option( 'csdt_devtools_smtp_enabled', '0' ) === '1'
            && '' !== trim( (string) get_option( 'csdt_devtools_smtp_host', '' ) )
        ) {
            add_action( 'phpmailer_init', array( 'CSDT_SMTP', 'phpmailer_configure' ) );
            if ( get_option( 'csdt_devtools_smtp_from_email', '' ) ) {
                add_filter( 'wp_mail_from', array( 'CSDT_SMTP', 'smtp_from_email' ) );
            }
            if ( get_option( 'csdt_devtools_smtp_from_name', '' ) ) {
                add_filter( 'wp_mail_from_name', array( 'CSDT_SMTP', 'smtp_from_name' ) );
            }
        }

        // Login security, URL intercept / 2FA flow (early, priority 1 on init).
        add_action( 'init', array( 'CSDT_Login', 'login_admin_intercept' ), 0 );
        add_action( 'init', array( 'CSDT_Login', 'login_serve_custom_slug' ), 1 );
        add_action( 'login_init', array( 'CSDT_Login', 'login_redirect_authenticated' ), 0 );
        add_action( 'login_init', array( 'CSDT_Login', 'login_block_direct_access' ), 1 );
        add_filter( 'auth_cookie_expiration', array( 'CSDT_Login', 'login_session_expiration' ), 10, 3 );
        add_action( 'login_init', array( 'CSDT_Login', 'login_2fa_handle' ) );
        add_filter( 'authenticate', array( 'CSDT_Login', 'login_2fa_intercept' ), 100, 3 );
        add_filter( 'login_url', array( 'CSDT_Login', 'login_custom_url' ), 10, 3 );
        add_filter( 'logout_url', array( 'CSDT_Login', 'login_custom_logout_url' ), 10, 2 );
        add_filter( 'lostpassword_url', array( 'CSDT_Login', 'login_custom_lostpassword_url' ), 10, 2 );
        add_filter( 'network_site_url', array( 'CSDT_Login', 'login_custom_network_url' ), 10, 3 );
        add_filter( 'site_url', array( 'CSDT_Login', 'login_custom_site_url' ), 10, 4 );

        // Brute-force protection, check before authentication (priority 1, before password check).
        add_filter( 'authenticate', array( 'CSDT_Login', 'login_brute_force_check' ), 1, 3 );
        // Force persistent cookie when a custom session duration is configured.
        // Must be login_init (fires before the POST is processed) not login_form_login
        // (which is a display hook that never fires on a successful login POST).
        add_action( 'login_init', array( 'CSDT_Login', 'login_force_remember' ), 5 );
        // Honeypot 2FA, redirect failed logins to fake PIN screen, then watched screen.
        add_action( 'wp_login_failed', array( 'CSDT_Login', 'honeypot_redirect' ), 5 );
        add_action( 'login_init', array( 'CSDT_Login', 'honeypot_handle' ), 2 );
        // Security monitor, always track failed logins regardless of monitor toggle.
        add_action( 'wp_login_failed', array( 'CSDT_Perf_Monitor', 'perf_track_failed_login' ) );
        // Telegram alerts for failed logins and REST API auth failures.
        add_action( 'wp_login_failed', array( 'CSDT_Login', 'on_login_failed' ) );
        add_action( 'application_password_failed_authentication', array( 'CSDT_Login', 'on_rest_auth_failed' ) );
        // Reject malformed Basic Auth (empty username, oversized, invalid chars) with 400.
        add_filter( 'rest_authentication_errors', array( 'CSDT_Login', 'rest_reject_empty_basic_auth' ), 1 );
        // Return generic 401 so attackers can't enumerate usernames via REST auth errors.
        add_filter( 'rest_authentication_errors', array( 'CSDT_Login', 'rest_generic_auth_error' ), 99 );
        // Alert on ANY write to security-critical options, catches WP-CLI, direct DB edits, etc.
        add_action( 'updated_option', array( 'CSDT_Login', 'on_option_updated' ), 10, 3 );
        // Style the login error panel.
        add_action( 'login_enqueue_scripts', array( 'CSDT_Login', 'login_error_styles' ) );
        // Username enumeration protection, only register if option is enabled (default on).
        if ( get_option( 'csdt_devtools_enum_protect', '1' ) === '1' ) {
            add_filter( 'wp_login_errors', array( 'CSDT_Login', 'generic_login_errors' ) );
        }

        // Custom 404 page + hiscore leaderboard.
        add_action( 'template_redirect', array( 'CSDT_Custom_404', 'serve_scheme_css' ), 0 );
        add_action( 'template_redirect', array( 'CSDT_Custom_404', 'maybe_custom_404' ), 1 );
        add_action( 'rest_api_init', array( 'CSDT_Custom_404', 'register_hiscore_routes' ) );
        add_action( 'rest_api_init', array( 'CSDT_CSP', 'register_csp_report_route' ) );
        add_action( 'rest_api_init', array( 'CSDT_Monitor', 'register_fpm_report_route' ) );
        add_action( 'rest_api_init', array( 'CSDT_Uptime', 'register_rest_routes' ) );
        add_action( 'rest_api_init', array( 'CSDT_GitHub_Sync', 'register_rest_routes' ) );
        add_action( 'init', array( 'CSDT_GitHub_Sync', 'maybe_install_table' ) );
        // REST save chain diagnostics — Redis-safe, uses PHP static class (in-process memory).
        // Previous code was broken: it checked REST_REQUEST inside plugins_loaded, but
        // REST_REQUEST is not defined until WP_REST_Server::serve_request() fires, so the
        // shutdown handler was never registered. Also used $wpdb->options transient queries
        // that miss Redis-cached transients entirely.
        //
        // Layers tracked:
        // rest_pre_dispatch       → request reached the REST router
        // rest_pre_insert_post    → DB write about to start
        // rest_after_insert_post  → DB write succeeded
        // rest_pre_serve_request  → output buffer check (stray PHP output corrupts JSON)
        // rest_pre_serve_request  → another plugin's filter intercepted the response
        // shutdown                → PHP exited without serving the response
        // Layer 1: REST router started (rest_pre_dispatch fires before controller dispatch).
        add_filter(
            'rest_pre_dispatch',
            static function ( $result, $server, $request ) {
				if ( 'POST' !== $request->get_method() ) {
					return $result;
				}
				if ( ! preg_match( '#^/wp/v2/posts/(\d+)$#', $request->get_route(), $m ) ) {
					return $result;
				}
				CSDT_REST_Diag::$post_id    = (int) $m[1];
				CSDT_REST_Diag::$dispatched = true;
				CSDT_REST_Diag::$uri        = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : $request->get_route();
				return $result;
			},
            1,
            3
        );
        // Layer 2: DB write starting.
        add_filter(
            'rest_pre_insert_post',
            static function ( $prepared, $req ) {
				CSDT_REST_Diag::$pre_inserted = true;
				if ( ! CSDT_REST_Diag::$post_id && isset( $prepared->ID ) ) {
					CSDT_REST_Diag::$post_id = (int) $prepared->ID;
				}
				return $prepared;
			},
            1,
            2
        );
        // Layer 3: DB write succeeded.
        add_action(
            'rest_after_insert_post',
            static function ( \WP_Post $post ) {
				CSDT_REST_Diag::$inserted = true;
			},
            PHP_INT_MAX
        );
        // Layer 4: Output buffer check at priority 1 (before WP encodes the response).
        // Any content already in the buffer here is stray PHP output (warnings, notices)
        // that would be prepended to the JSON body and break the response.
        add_filter(
            'rest_pre_serve_request',
            static function ( $served, $result, $request ) {
				if ( ! CSDT_REST_Diag::$dispatched ) {
					return $served;
				}
				$ob = ob_get_contents();
				if ( false !== $ob && '' !== trim( $ob ) ) {
					CSDT_REST_Diag::$ob_stray = substr( $ob, 0, 600 );
				}
				// If $served is already true here, a lower-priority filter already intercepted
				// and served a custom response — that other plugin is the culprit.
				if ( $served ) {
					CSDT_REST_Diag::$intercepted = true;
				}
				return $served;
			},
            1,
            3
        );
        // Layer 5: Final check just before WP actually sends the JSON.
        // Validate the encoded response and mark served = true so the shutdown function
        // knows the request completed normally.
        add_filter(
            'rest_pre_serve_request',
            static function ( $served, $result, $request ) {
				if ( ! CSDT_REST_Diag::$dispatched ) {
					return $served;
				}
				CSDT_REST_Diag::$served = true;
				if ( ! $served && $result instanceof \WP_REST_Response ) {
					$encoded = wp_json_encode( $result->get_data() );
					if ( false === $encoded ) {
						CSDT_REST_Diag::$json_err = 'wp_json_encode returned false (non-serialisable value in response data)';
					}
				}
				return $served;
			},
            PHP_INT_MAX,
            3
        );
        // Shutdown handler registered at rest_api_init — this action fires inside
        // WP_REST_Server::serve_request() so REST_REQUEST is already defined and true.
        // Previous code registered this at plugins_loaded where REST_REQUEST does not
        // yet exist, meaning it never ran for any REST request.
        add_action(
            'rest_api_init',
            static function () {
				register_shutdown_function(
                    static function () {
						if ( ! class_exists( 'CloudScale_Telegram' ) || ! CloudScale_Telegram::is_configured() ) {
							return;
						}
						$err        = error_get_last();
						$err_detail = $err
						? sprintf( "\nLast PHP error: [%d] %s\n  in %s:%d", $err['type'], $err['message'], $err['file'], $err['line'] )
						: '';
						// Stray PHP output buffered before the response (warnings/notices with display_errors=on).
						if ( CSDT_REST_Diag::$dispatched && '' !== CSDT_REST_Diag::$ob_stray ) {
							CloudScale_Telegram::send(
                                sprintf(
                                    "REST post save: stray PHP output detected before response\nPost: #%d\nURI: %s\nBuffer (first 600 chars):\n%s%s",
                                    CSDT_REST_Diag::$post_id,
                                    CSDT_REST_Diag::$uri,
                                    CSDT_REST_Diag::$ob_stray,
                                    $err_detail
                                ),
                                'CloudScale Cyber Devtools',
                                'critical'
							);
							return;
						}
						// Another plugin's rest_pre_serve_request filter intercepted the response.
						if ( CSDT_REST_Diag::$dispatched && CSDT_REST_Diag::$intercepted && ! CSDT_REST_Diag::$served ) {
							CloudScale_Telegram::send(
                                sprintf(
                                    "REST post save: another plugin intercepted rest_pre_serve_request\nPost: #%d\nURI: %s\nThe response was hijacked before our PHP_INT_MAX hook — a plugin served a custom (possibly corrupt) response.%s",
                                    CSDT_REST_Diag::$post_id,
                                    CSDT_REST_Diag::$uri,
                                    $err_detail
                                ),
                                'CloudScale Cyber Devtools',
                                'critical'
							);
							return;
						}
						// wp_json_encode failed — response data contained non-serialisable values.
						if ( CSDT_REST_Diag::$dispatched && '' !== CSDT_REST_Diag::$json_err ) {
							CloudScale_Telegram::send(
                                sprintf(
                                    "REST post save: JSON encoding failed\nPost: #%d\nURI: %s\nReason: %s%s",
                                    CSDT_REST_Diag::$post_id,
                                    CSDT_REST_Diag::$uri,
                                    CSDT_REST_Diag::$json_err,
                                    $err_detail
                                ),
                                'CloudScale Cyber Devtools',
                                'critical'
							);
							return;
						}
						// PHP exited before the response was served — map to the last completed stage.
						if ( CSDT_REST_Diag::$dispatched && ! CSDT_REST_Diag::$served ) {
							if ( CSDT_REST_Diag::$inserted ) {
								$stage = 'between rest_after_insert_post and rest_pre_serve_request';
							} elseif ( CSDT_REST_Diag::$pre_inserted ) {
								$stage = 'between rest_pre_insert_post and rest_after_insert_post (DB write in progress)';
							} else {
								$stage = 'between rest_pre_dispatch and rest_pre_insert_post';
							}
							CloudScale_Telegram::send(
                                sprintf(
                                    "REST post save INCOMPLETE: PHP exited without serving response\nPost: #%d\nURI: %s\nFailed at stage: %s%s",
                                    CSDT_REST_Diag::$post_id,
                                    CSDT_REST_Diag::$uri,
                                    $stage,
                                    $err_detail
                                ),
                                'CloudScale Cyber Devtools',
                                'critical'
							);
						}
					}
				);
			},
            1
        );
        // Reconcile the index after every post save so that deleting a block or
        // clearing a block's GitHub URL removes its row from the linked-blocks table.
        add_action( 'save_post', array( 'CSDT_GitHub_Sync', 'on_save_post' ), 20, 3 );
        add_action( 'before_delete_post', array( 'CSDT_GitHub_Sync', 'on_post_deleted' ) );
        add_action( 'wp_trash_post', array( 'CSDT_GitHub_Sync', 'on_post_deleted' ) );
        self::cron_action( CSDT_GitHub_Sync::CRON_HOOK, array( 'CSDT_GitHub_Sync', 'cron_sweep' ) );
        if ( ! wp_next_scheduled( CSDT_GitHub_Sync::CRON_HOOK ) ) {
            wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', CSDT_GitHub_Sync::CRON_HOOK );
        }
        add_action( 'wp_ajax_csdt_devtools_save_404_settings', array( 'CSDT_Custom_404', 'ajax_save_404_settings' ) );

        // Performance monitor, EXPLAIN endpoint.
        add_action( 'wp_ajax_csdt_devtools_perf_explain', array( 'CSDT_Perf_Monitor', 'ajax_perf_explain' ) );
        add_action( 'wp_ajax_csdt_devtools_perf_debug_toggle', array( 'CSDT_Perf_Monitor', 'ajax_perf_debug_toggle' ) );
        add_action( 'wp_ajax_csdt_perf_critical_notify', array( 'CSDT_Perf_Monitor', 'ajax_perf_critical_notify' ) );

        // Performance monitor, only register data-collection hooks when the monitor is enabled.
        // This prevents SAVEQUERIES-scale memory accumulation on every request when disabled.
        if ( get_option( 'csdt_devtools_perf_monitor_enabled', '1' ) !== '0' ) {
            add_filter( 'pre_http_request', array( 'CSDT_Perf_Monitor', 'perf_http_before' ), 10, 3 );
            add_action( 'http_api_debug', array( 'CSDT_Perf_Monitor', 'perf_http_after' ), 10, 5 );

            // If the user enabled debug logging via the panel, activate PHP error logging
            // using ini_set, this works regardless of WP_DEBUG in wp-config.php and
            // survives Docker container rebuilds because the setting lives in the DB.
            if ( get_option( 'csdt_devtools_perf_debug_logging', false ) ) {
                @ini_set( 'log_errors', '1' ); // phpcs:ignore WordPress.PHP.IniSet.Risky, Squiz.PHP.DiscouragedFunctions.Discouraged -- ini_set required for SMTP/memory adjustment in this context
                @ini_set( 'error_log', WP_CONTENT_DIR . '/debug.log' ); // phpcs:ignore WordPress.PHP.IniSet.Risky, Squiz.PHP.DiscouragedFunctions.Discouraged -- ini_set required for SMTP/memory adjustment in this context
                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting
                error_reporting( E_ALL );
            }

            // Register error handler late (priority 9999 on plugins_loaded) so we sit
            // on top of any handler registered by other plugins (e.g. Query Monitor).
            add_action(
                'plugins_loaded',
                function () {
					CSDT_Perf_Monitor::$perf_prev_error_handler = set_error_handler( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler -- custom error capture for diagnostic purposes
                        array( 'CSDT_Perf_Monitor', 'perf_error_handler' ),
                        E_WARNING | E_NOTICE | E_DEPRECATED | E_USER_WARNING | E_USER_NOTICE | E_USER_DEPRECATED
					);
					CSDT_Monitor::register_shutdown_handler();
				},
                9999
            );

            // Performance monitor, panel rendering (admin pages).
            add_action( 'admin_enqueue_scripts', array( 'CSDT_Perf_Monitor', 'perf_enqueue' ) );
            // Inject JSON data at priority 15, before wp_print_footer_scripts (priority 20) so
            // cs-perf-monitor.js reads window.csdtDevtoolsPerfData when its IIFE runs.
            add_action( 'admin_footer', array( 'CSDT_Perf_Monitor', 'perf_inject_data' ), 15 );
            add_action( 'admin_footer', array( 'CSDT_Perf_Monitor', 'perf_output_panel' ), 9999 );

            // Performance monitor, panel rendering (frontend, admin users only).
            add_action( 'wp_enqueue_scripts', array( 'CSDT_Perf_Monitor', 'perf_frontend_enqueue' ) );
            add_action( 'wp_footer', array( 'CSDT_Perf_Monitor', 'perf_inject_data' ), 15 );
            add_action( 'wp_footer', array( 'CSDT_Perf_Monitor', 'perf_output_panel' ), 9999 );

            // Capture the active template filename for the page-context strip.
            add_filter( 'template_include', array( 'CSDT_Perf_Monitor', 'perf_capture_template' ), 9999 );

            // Hook timing tracker, fires on every action/filter.
            add_action( 'all', array( 'CSDT_Perf_Monitor', 'perf_hook_tracker' ) );

            // Transient + template hierarchy observer (single all-hook for both).
            add_action( 'all', array( 'CSDT_Perf_Monitor', 'perf_misc_tracker' ) );
            add_action( 'setted_transient', array( 'CSDT_Perf_Monitor', 'perf_transient_set' ) );
            add_action( 'setted_site_transient', array( 'CSDT_Perf_Monitor', 'perf_transient_set' ) );
            add_action( 'deleted_transient', array( 'CSDT_Perf_Monitor', 'perf_transient_delete' ) );
            add_action( 'deleted_site_transient', array( 'CSDT_Perf_Monitor', 'perf_transient_delete' ) );

            // Scripts & styles, collect at footer time (after everything is enqueued).
            add_action( 'admin_footer', array( 'CSDT_Perf_Monitor', 'perf_capture_assets' ), 1 );
            add_action( 'wp_footer', array( 'CSDT_Perf_Monitor', 'perf_capture_assets' ), 1 );

            // Request lifecycle milestones for the waterfall timeline.
            // Registered at PHP_INT_MAX so we capture the time after all other
            // callbacks on that hook have finished running.
            foreach ( array(
                'plugins_loaded'    => 'Plugins loaded',
                'init'              => 'WP init',
                'admin_init'        => 'Admin init',
                'wp_loaded'         => 'WP loaded',
                'wp'                => 'Query setup',
                'template_redirect' => 'Template',
            ) as $_ms_hook => $_ms_label ) {
                add_action(
                    $_ms_hook,
                    static function () use ( $_ms_label ) {
						CSDT_Perf_Monitor::perf_record_milestone( $_ms_label );
					},
                    PHP_INT_MAX
                );
            }
        }
    }

    /*
    ==================================================================
        0a. ONE-TIME MIGRATIONS
        ================================================================== */

    private static function maybe_migrate_autoload(): void {
        global $wpdb;

        // ── v1: ensure hot-path config options ARE autoloaded.
        // (Credentials removed from this list, see v2 below.)
        $done_key_v1 = 'csdt_autoload_migrated_v1';
        if ( ! get_option( $done_key_v1 ) ) {
            $names_yes    = array(
                'csdt_uptime_enabled',
				'csdt_uptime_worker_url',
                'csdt_ssh_monitor_enabled',
				'csdt_ssh_monitor_threshold',
                'csdt_php_error_monitor_enabled',
				'csdt_php_error_monitor_threshold',
                'csdt_threat_monitor_enabled',
				'csdt_threat_file_integrity_enabled',
                'csdt_threat_new_admin_enabled',
				'csdt_threat_probe_enabled',
				'csdt_threat_probe_threshold',
                'csdt_devtools_ai_provider',
                'csdt_devtools_security_model',
				'csdt_devtools_deep_scan_model',
				'csdt_devtools_security_prompt',
            );
            $placeholders = implode( ',', array_fill( 0, count( $names_yes ), '%s' ) );
            $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- custom table direct query
                $wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
                    "UPDATE {$wpdb->options} SET autoload = 'yes' WHERE option_name IN ({$placeholders}) AND autoload != 'yes'", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- variadic spread counted incorrectly by PHPCS
                    ...$names_yes
                )
            );
            add_option( $done_key_v1, '1', '', 'yes' );
        }

        // ── v2: force credentials OFF autoload.
        // Cyber review: these values are long-lived secrets. Keeping them out of
        // wp_load_alloptions() means a future arbitrary-read primitive can't
        // dump them in a single query.
        $done_key_v2 = 'csdt_autoload_migrated_v2';
        if ( ! get_option( $done_key_v2 ) ) {
            $names_no     = array(
                'csdt_devtools_anthropic_key',
                'csdt_devtools_gemini_key',
                'csdt_devtools_cf_api_token',
                'csdt_devtools_smtp_pass',
                'csdt_db_prefix_rollback',
                'csdt_devtools_openai_key',
                'csdt_uptime_token',
                'csdt_opcache_token',
                'csdt_fpm_token',
            );
            $placeholders = implode( ',', array_fill( 0, count( $names_no ), '%s' ) );
            $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- custom table direct query
                $wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
                    "UPDATE {$wpdb->options} SET autoload = 'no' WHERE option_name IN ({$placeholders}) AND autoload != 'no'", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- variadic spread counted incorrectly by PHPCS
                    ...$names_no
                )
            );
            add_option( $done_key_v2, '1', '', 'no' );
        }

        // ── v3: catch credential options the v2 list missed.
        // Cyber review identified three credentials that pre-existing installs
        // may still have stored with autoload=yes from earlier plugin code.
        $done_key_v3 = 'csdt_autoload_migrated_v3';
        if ( ! get_option( $done_key_v3 ) ) {
            $names_no     = array(
                CloudScale_Telegram::OPTION_TOKEN,
                'csdt_test_session_secret',      // test-session API HMAC secret
                'csdt_test_session_path_token',  // path-segment secret hiding the endpoint
            );
            $placeholders = implode( ',', array_fill( 0, count( $names_no ), '%s' ) );
            $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- custom table direct query
                $wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
                    "UPDATE {$wpdb->options} SET autoload = 'no' WHERE option_name IN ({$placeholders}) AND autoload != 'no'", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- variadic spread counted incorrectly by PHPCS
                    ...$names_no
                )
            );
            add_option( $done_key_v3, '1', '', 'no' );
        }
    }

    /*
    ==================================================================
        1. BLOCK REGISTRATION
        ================================================================== */

    /**
     * Registers the block type and all its scripts and stylesheets.
     *
     * @since  1.0.0
     * @return void
     */
    public static function register_block() {
        // Serve hljs and its theme CSS from the plugin's own /assets/ directory so
        // they are not blocked by a strict CSP that would otherwise require an external CDN allowlist.
        $assets = plugins_url( 'assets/', __FILE__ );

        wp_register_script(
            'hljs-core',
            $assets . 'highlight.min.js',
            array(),
            self::HLJS_VERSION,
            true
        );

        // Register both theme stylesheets from the selected pair
        $pair_slug = get_option( 'csdt_devtools_code_theme_pair', 'atom-one' );
        $registry  = self::get_theme_registry();
        $pair      = isset( $registry[ $pair_slug ] ) ? $registry[ $pair_slug ] : $registry['atom-one'];

        wp_register_style(
            'hljs-theme-dark',
            $assets . 'hljs-' . $pair['dark_css'] . '.min.css',
            array(),
            self::HLJS_VERSION
        );
        wp_register_style(
            'hljs-theme-light',
            $assets . 'hljs-' . $pair['light_css'] . '.min.css',
            array(),
            self::HLJS_VERSION
        );

        wp_register_style(
            'csdt-code-block-frontend',
            plugins_url( 'assets/cs-code-block.css', __FILE__ ),
            array( 'hljs-theme-dark', 'hljs-theme-light' ),
            self::VERSION
        );

        wp_register_script(
            'csdt-code-block-frontend',
            plugins_url( 'assets/cs-code-block.js', __FILE__ ),
            array( 'hljs-core' ),
            self::VERSION,
            true
        );

        wp_register_style(
            'csdt-code-block-editor',
            plugins_url( 'assets/cs-code-block-editor.css', __FILE__ ),
            array(),
            self::VERSION
        );

        wp_register_script(
            'cloudscale-code-block-editor-script',
            plugins_url( 'blocks/code/editor.js', __FILE__ ),
            array( 'wp-blocks', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-i18n', 'wp-data', 'wp-hooks' ),
            self::VERSION,
            true
        );

        register_block_type(
            __DIR__ . '/blocks/code',
            array(
                'render_callback' => array( __CLASS__, 'render_block' ),
                'editor_script'   => 'cloudscale-code-block-editor-script',
            )
        );
    }

    /*
    ==================================================================
        1b. CONVERT SCRIPT
        ================================================================== */

    /**
     * Enqueues the block editor auto-convert script and attaches the toast inline style.
     *
     * @since  1.5.0
     * @return void
     */
    public static function enqueue_convert_script() {
        wp_enqueue_script(
            'csdt-code-block-convert',
            plugins_url( 'assets/cs-convert.js', __FILE__ ),
            array( 'wp-blocks', 'wp-data' ),
            self::VERSION,
            true
        );
        wp_add_inline_style( 'csdt-code-block-editor', self::get_convert_toast_css() );
    }

    /**
     * Returns the CSS string for the block editor convert-all toast notification.
     *
     * @since  1.7.17
     * @return string
     */
    private static function get_log_viewer_css(): string {
        return '.cs-log-src-btn.status-ok{border-color:#22c55e;color:#22c55e}' .
                '.cs-log-src-btn.status-not-found{border-color:#555;color:#555}' .
                '.cs-log-src-btn.status-permission-denied{border-color:#f59e0b;color:#f59e0b}' .
                '.cs-log-src-btn.status-empty{border-color:#6366f1;color:#6366f1}' .
                '.cs-log-src-btn.active{background:rgba(74,158,255,.15);border-color:#4a9eff;color:#4a9eff}' .
                '.cs-log-line{padding:1px 0;white-space:pre-wrap;word-break:break-all;border-bottom:1px solid rgba(255,255,255,.03)}' .
                '.cs-log-line.level-emerg,.cs-log-line.level-alert,.cs-log-line.level-crit{color:#ff6b6b}' .
                '.cs-log-line.level-error{color:#f87171}' .
                '.cs-log-line.level-warn{color:#fbbf24}' .
                '.cs-log-line.level-notice{color:#a78bfa}' .
                '.cs-log-line.level-info{color:#60a5fa}' .
                '.cs-log-line.level-debug{color:#6b7280}' .
                '.cs-log-line.level-default{color:#c9d1d9}';
    }

    private static function get_dashboard_widget_css(): string {
        return /* Dark header band, rendered as HTML inside the widget body */
            '#csdt_security_summary .cs-dw-header{background:linear-gradient(135deg,#1a0a02 0%,#5c2500 60%,#1a0a02 100%);margin:-1px -12px 14px;padding:12px 14px 10px;border-bottom:1px solid rgba(200,110,30,0.35);display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap}' .
            '#csdt_security_summary .cs-dw-header-left{display:flex;align-items:center;gap:8px}' .
            '#csdt_security_summary .cs-dw-header-title{color:#e2e8f0;font-size:13px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;line-height:1.2}' .
            '#csdt_security_summary .cs-dw-header-sub{color:#94a3b8;font-size:10px;margin-top:2px;line-height:1.3}' .
            '#csdt_security_summary .cs-dw-header-right{display:flex;gap:6px;align-items:center;flex-shrink:0}' .
            '#csdt_security_summary .cs-dw-hpill{display:inline-flex;flex-direction:column;align-items:center;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.12);border-radius:8px;padding:4px 10px;min-width:44px;text-align:center}' .
            '#csdt_security_summary .cs-dw-hpill-num{font-size:16px;font-weight:800;line-height:1.1}' .
            '#csdt_security_summary .cs-dw-hpill-lbl{font-size:8px;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;margin-top:1px;opacity:.75}' .
            '#csdt_security_summary .cs-dw-bar{display:none}' .

            /* Hero score block */
            '#csdt_security_summary .cs-dw-hero{display:flex;align-items:center;gap:14px;padding:4px 0 16px;border-bottom:1px solid #e2e8f0}' .
            '#csdt_security_summary .cs-dw-score-ring{width:62px;height:62px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:21px;font-weight:800;flex-shrink:0;border:3px solid currentColor}' .
            '#csdt_security_summary .cs-dw-hero-meta{flex:1;min-width:0}' .
            '#csdt_security_summary .cs-dw-hero-title{font-size:13px;font-weight:700;color:#1e293b;line-height:1.2}' .
            '#csdt_security_summary .cs-dw-hero-sub{font-size:11px;color:#94a3b8;margin-top:2px}' .
            '#csdt_security_summary .cs-dw-hero-pills{display:flex;gap:6px;margin-top:8px}' .
            '#csdt_security_summary .cs-dw-pill{display:flex;flex-direction:column;align-items:center;justify-content:center;border-radius:8px;padding:5px 10px;font-weight:700;line-height:1}' .
            '#csdt_security_summary .cs-dw-pill-num{font-size:18px}' .
            '#csdt_security_summary .cs-dw-pill-lbl{font-size:9px;text-transform:uppercase;letter-spacing:.06em;margin-top:2px;opacity:.8}' .

            /* Section header */
            '#csdt_security_summary .cs-dw-section{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:#64748b;padding:12px 0 6px;display:flex;align-items:center;gap:6px}' .
            '#csdt_security_summary .cs-dw-section::after{content:"";flex:1;height:1px;background:#e2e8f0}' .

            /* Status grid */
            '#csdt_security_summary .cs-dw-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:4px}' .
            '#csdt_security_summary .cs-dw-chip{display:flex;align-items:center;gap:7px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:7px 9px;font-size:12px;font-weight:600;color:#334155}' .
            '#csdt_security_summary .cs-dw-chip .cs-dw-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}' .
            '#csdt_security_summary .cs-dw-chip-label{font-size:10px;color:#94a3b8;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' .
            '#csdt_security_summary .cs-dw-chip details summary{font-size:11px;color:#6366f1;cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:3px;}' .
            '#csdt_security_summary .cs-dw-chip details summary::-webkit-details-marker{display:none}' .
            '#csdt_security_summary .cs-dw-chip details[open] .csdt-2fa-chevron{transform:rotate(90deg)}' .
            '#csdt_security_summary .cs-dw-chip .csdt-2fa-chevron{display:inline-block;transition:transform .15s}' .

            /* Timestamps row */
            '#csdt_security_summary .cs-dw-times{display:flex;gap:10px;margin-top:4px}' .
            '#csdt_security_summary .cs-dw-time{flex:1;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:5px 8px;font-size:10px;color:#94a3b8;text-align:center}' .
            '#csdt_security_summary .cs-dw-time strong{display:block;font-size:11px;color:#475569;font-weight:600}' .

            /* CTA button */
            '#csdt_security_summary .cs-dw-cta{margin-top:14px}' .
            '#csdt_security_summary .cs-dw-cta a{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;box-sizing:border-box;' .
            'background:linear-gradient(135deg,#0ea5e9 0%,#0369a1 100%);color:#fff;font-weight:700;font-size:13px;' .
            'padding:11px 16px;border-radius:8px;text-decoration:none;' .
            'box-shadow:0 2px 8px rgba(14,165,233,.35);letter-spacing:.03em;transition:filter .15s,box-shadow .15s}' .
            '#csdt_security_summary .cs-dw-cta a:hover{filter:brightness(1.1);box-shadow:0 6px 20px rgba(3,105,161,.45)}';
    }

    private static function get_convert_toast_css(): string {
        return '#cs-convert-all-toast{'
            . 'position:fixed;bottom:24px;right:24px;z-index:999999;'
            . 'background:linear-gradient(135deg,#1e3a5f 0%,#0d9488 100%);'
            . 'color:#fff;padding:16px 20px;border-radius:10px;'
            . 'box-shadow:0 8px 32px rgba(0,0,0,0.3);'
            . 'display:flex;align-items:center;gap:16px;'
            . 'font-size:14px;font-weight:500;'
            . 'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;'
            . 'animation:cs-toast-in 0.3s ease-out;'
            . '}'
            . '#cs-convert-all-toast button{'
            . 'background:#fff;color:#1e3a5f;font-weight:700;border-radius:6px;'
            . 'padding:10px 24px;font-size:14px;border:none;white-space:nowrap;'
            . 'cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.15);font-family:inherit;'
            . '}'
            . '#cs-convert-all-toast button:hover{background:#f0fdf4;}'
            . '@keyframes cs-toast-in{'
            . 'from{opacity:0;transform:translateY(20px);}'
            . 'to{opacity:1;transform:translateY(0);}'
            . '}';
    }

    /**
     * Enqueues the Replace Post with Clipboard button script in the block editor.
     *
     * @since  1.10.150
     * @return void
     */
    public static function enqueue_replace_post_script() {
        wp_enqueue_script(
            'csdt-replace-post',
            plugins_url( 'assets/cs-replace-post.js', __FILE__ ),
            array( 'wp-blocks', 'wp-data', 'wp-dom-ready' ),
            self::VERSION,
            true
        );
        wp_add_inline_style( 'wp-edit-post', self::get_replace_post_css() );
    }

    /**
     * Returns the CSS for the Replace Post floating button, confirm banner, and paste modal.
     *
     * @since  1.10.150
     * @return string
     */
    private static function get_replace_post_css(): string {
        $font = '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif';
        return '#cs-replace-post-btn{'
            . 'background:linear-gradient(135deg,#1e3a5f 0%,#7c3aed 100%);'
            . 'color:#fff;padding:8px 14px;border-radius:6px;'
            . 'box-shadow:0 2px 8px rgba(0,0,0,0.2);'
            . 'font-size:13px;font-weight:600;font-family:' . $font . ';'
            . 'border:none;cursor:pointer;min-height:36px;white-space:nowrap;'
            . 'display:inline-flex;align-items:center;gap:6px;'
            . 'transition:background 0.2s;'
            . '}'
            // When successfully injected into the Gutenberg toolbar
            . '#cs-replace-post-btn.cs-in-toolbar{'
            . 'position:relative;margin-left:12px;align-self:center;'
            . '}'
            // Fallback: fixed position (toolbar not found after retries)
            . '#cs-replace-post-btn:not(.cs-in-toolbar){'
            . 'position:fixed;top:60px;left:16px;z-index:999998;'
            . '}'
            . '#cs-replace-post-btn:hover{background:linear-gradient(135deg,#0d9488 0%,#7c3aed 100%);}'
            . '#cs-replace-post-confirm{'
            . 'position:fixed;z-index:999999;'
            . 'background:#fff;border:1px solid #e2e8f0;border-radius:8px;'
            . 'box-shadow:0 4px 20px rgba(0,0,0,0.15);'
            . 'padding:12px 14px;display:flex;align-items:center;gap:8px;'
            . 'font-family:' . $font . ';font-size:13px;color:#374151;white-space:nowrap;'
            . 'animation:cs-rp-in 0.2s ease-out;'
            . '}'
            . '#cs-replace-post-confirm button{'
            . 'padding:7px 14px;border-radius:5px;font-size:13px;font-weight:600;'
            . 'border:none;cursor:pointer;font-family:inherit;'
            . '}'
            . '#cs-rp-yes{background:#1e3a5f;color:#fff;}'
            . '#cs-rp-yes:hover{background:#7c3aed;}'
            . '#cs-rp-no{background:#f1f5f9;color:#374151;}'
            . '#cs-rp-no:hover{background:#e2e8f0;}'
            . '#cs-replace-post-modal{'
            . 'position:fixed;inset:0;z-index:1000000;'
            . 'background:rgba(0,0,0,0.55);'
            . 'display:flex;align-items:center;justify-content:center;padding:16px;'
            . '}'
            . '#cs-rp-modal-inner{'
            . 'background:#fff;border-radius:10px;padding:22px;'
            . 'width:100%;max-width:540px;'
            . 'font-family:' . $font . ';'
            . '}'
            . '#cs-rp-modal-title{'
            . 'font-size:15px;font-weight:600;color:#1e3a5f;margin:0 0 12px;'
            . '}'
            . '#cs-rp-modal-ta{'
            . 'width:100%;box-sizing:border-box;'
            . 'padding:10px;border:1px solid #cbd5e1;border-radius:6px;'
            . 'font-size:13px;font-family:monospace;resize:vertical;'
            . '}'
            . '#cs-rp-modal-btns{'
            . 'display:flex;gap:8px;justify-content:flex-end;margin-top:14px;'
            . '}'
            . '#cs-rp-modal-btns button{'
            . 'padding:9px 18px;border-radius:6px;font-size:14px;font-weight:600;'
            . 'border:none;cursor:pointer;font-family:inherit;'
            . '}'
            . '#cs-rp-modal-apply{background:#1e3a5f;color:#fff;}'
            . '#cs-rp-modal-apply:hover{background:#7c3aed;}'
            . '#cs-rp-modal-cancel{background:#f1f5f9;color:#374151;}'
            . '#cs-rp-modal-cancel:hover{background:#e2e8f0;}'
            . '@keyframes cs-rp-in{'
            . 'from{opacity:0;transform:translateY(8px);}'
            . 'to{opacity:1;transform:translateY(0);}'
            . '}';
    }

    /*
    ==================================================================
        2. RENDER (shared by block + shortcode)
        ================================================================== */

    /**
     * Renders a code block on the frontend.
     *
     * @since  1.0.0
     * @param  array  $attributes    Block attributes.
     * @param  string $block_content Existing block content (unused).
     * @return string HTML output.
     */
    public static function render_block( $attributes, $block_content = '' ) {
        self::maybe_enqueue_frontend();
        ++self::$instance_count;

        $id    = 'cs-code-' . self::$instance_count;
        $code  = isset( $attributes['content'] ) ? $attributes['content'] : '';
        $lang  = isset( $attributes['language'] ) ? $attributes['language'] : '';
        $title = isset( $attributes['title'] ) ? $attributes['title'] : '';
        $theme = isset( $attributes['theme'] ) ? $attributes['theme'] : '';

        $gh_view_url = '';
        $gh_label    = 'GitHub';
        if ( ! empty( $attributes['githubUrl'] ) && ! empty( $attributes['githubShowLink'] ) ) {
            $provider = CSDT_Git_Providers::detect( (string) $attributes['githubUrl'] );
            $parts    = $provider ? $provider->parse( (string) $attributes['githubUrl'] ) : null;
            if ( $provider && $parts ) {
                // Whole-file link (no line anchor), the stored block doesn't carry line numbers.
                $gh_view_url = $provider->view_url( $parts, 0, 0 );
                $gh_label    = $provider->label();
            }
        }

        return self::build_html( $id, $code, $lang, $title, $theme, $gh_view_url, $gh_label );
    }

    /**
     * Returns the inline SVG mark for a source provider. GitHub keeps its octocat;
     * other providers (GitLab, future) use a neutral git-branch glyph so we never
     * render a wrong-brand logo. Markup is a fixed literal, safe to echo directly.
     *
     * @since  1.10.150
     * @param  string $label Provider label ("GitHub", "GitLab", …).
     * @return string Inline SVG markup.
     */
    private static function source_icon_svg( $label ) {
        if ( 'GitHub' === $label ) {
            return '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-2.2c-3.2.7-3.88-1.36-3.88-1.36-.52-1.32-1.27-1.67-1.27-1.67-1.04-.71.08-.69.08-.69 1.15.08 1.76 1.18 1.76 1.18 1.02 1.75 2.68 1.24 3.34.95.1-.74.4-1.24.73-1.53-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.04 0 0 .97-.31 3.17 1.18a11 11 0 0 1 2.88-.39c.98 0 1.97.13 2.88.39 2.2-1.49 3.17-1.18 3.17-1.18.62 1.58.23 2.75.11 3.04.74.81 1.18 1.84 1.18 3.1 0 4.43-2.69 5.41-5.25 5.69.41.36.78 1.06.78 2.14v3.17c0 .31.21.67.8.56C20.21 21.39 23.5 17.08 23.5 12 23.5 5.65 18.35.5 12 .5z"/></svg>';
        }
        return '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>';
    }

    /**
     * Builds the full HTML markup for a code block.
     *
     * @since  1.0.0
     * @param  string $id    Unique HTML element ID.
     * @param  string $code  Code content to display.
     * @param  string $lang  Language identifier for highlight.js, or empty for auto-detect.
     * @param  string $title Optional filename or title label.
     * @param  string $theme Per-block colour-theme override slug, or empty for site default.
     * @param  string $gh_view_url Source "view file" URL, or empty when not linked.
     * @param  string $gh_label    Provider label for link text ("GitHub", "GitLab").
     * @return string HTML markup.
     */
    private static function build_html( $id, $code, $lang, $title, $theme, $gh_view_url = '', $gh_label = 'GitHub' ) {
        $lang_class = $lang ? 'language-' . esc_attr( $lang ) : '';

        $cloudscale_link = get_option( 'csdt_show_powered_by', '0' ) === '1'
            ? '<a class="cs-code-brand" href="https://help.cloudscale.consulting/plugin-help/cyber-devtools/code-block/" target="_blank" rel="noopener noreferrer"><span class="cs-brand-bolt">&#9889;</span> Powered by CloudScale</a>'
            : '';

        $title_html = '';
        if ( $title ) {
            $title_html = '<div class="cs-code-title">' . esc_html( $title ) . '</div>';
        }

        ob_start();
        ?>
        <div class="cs-code-wrapper" id="<?php echo esc_attr( $id ); ?>"
        <?php
        if ( $theme ) {
			echo ' data-theme="' . esc_attr( $theme ) . '"'; }
		?>
        >
            <div class="cs-code-toolbar">
                <?php echo wp_kses_post( $cloudscale_link ); ?>
                <?php echo wp_kses_post( $title_html ); ?>
                <div class="cs-code-actions">
                    <span class="cs-code-lang-badge"></span>
                    <?php if ( $gh_view_url ) : ?>
                        <?php /* translators: %s: source provider name, e.g. GitHub or GitLab. */ ?>
                        <a class="cs-code-gh-btn"
                            href="<?php echo esc_url( $gh_view_url ); ?>"
                            target="_blank"
                            rel="noopener noreferrer"
                            title="<?php /* translators: %s is the name of the code hosting service (e.g. GitHub, GitLab) */ echo esc_attr( sprintf( __( 'View source on %s', 'cloudscale-cyber-devtools' ), $gh_label ) ); ?>"
                            aria-label="<?php /* translators: %s is the name of the code hosting service (e.g. GitHub, GitLab) */ echo esc_attr( sprintf( __( 'View source on %s', 'cloudscale-cyber-devtools' ), $gh_label ) ); ?>">
                            <?php echo self::source_icon_svg( $gh_label ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed literal SVG ?>
                            <span class="cs-gh-label"><?php esc_html_e( 'Linked', 'cloudscale-cyber-devtools' ); ?></span>
                        </a>
                    <?php endif; ?>
                    <button class="cs-code-lines-toggle" title="<?php esc_attr_e( 'Toggle line numbers', 'cloudscale-cyber-devtools' ); ?>" aria-label="<?php esc_attr_e( 'Toggle line numbers', 'cloudscale-cyber-devtools' ); ?>">
                        <svg class="cs-icon-lines" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><text x="4" y="7" font-size="7" fill="currentColor" stroke="none" font-family="monospace">1</text><text x="4" y="13" font-size="7" fill="currentColor" stroke="none" font-family="monospace">2</text><text x="4" y="19" font-size="7" fill="currentColor" stroke="none" font-family="monospace">3</text></svg>
                    </button>
                    <button class="cs-code-theme-toggle" title="<?php esc_attr_e( 'Toggle light/dark mode', 'cloudscale-cyber-devtools' ); ?>" aria-label="<?php esc_attr_e( 'Toggle theme', 'cloudscale-cyber-devtools' ); ?>">
                        <svg class="cs-icon-sun" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
                        <svg class="cs-icon-moon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
                    </button>
                    <button class="cs-code-copy" title="<?php esc_attr_e( 'Copy to clipboard', 'cloudscale-cyber-devtools' ); ?>" aria-label="<?php esc_attr_e( 'Copy code', 'cloudscale-cyber-devtools' ); ?>">
                        <svg class="cs-icon-copy" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
                        <svg class="cs-icon-check" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                        <span class="cs-copy-label"><?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></span>
                    </button>
                </div>
            </div>
            <div class="cs-code-body">
                <pre><code class="<?php echo esc_attr( $lang_class ); ?>"><?php echo str_replace( array( '[', ']' ), array( '&#91;', '&#93;' ), esc_html( $code ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- esc_html() applied to $code; str_replace only converts bracket chars to HTML entities ?></code></pre>
            </div>
            <?php if ( $gh_view_url ) : ?>
                <div class="cs-code-gh-footer">
                    <a class="cs-code-gh-link" href="<?php echo esc_url( $gh_view_url ); ?>" target="_blank" rel="noopener noreferrer">
                        <?php echo self::source_icon_svg( $gh_label ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed literal SVG ?>
                        <?php /* translators: %s: source provider name, e.g. GitHub or GitLab. */ ?>
                        <span><?php echo esc_html( sprintf( __( 'View on %s', 'cloudscale-cyber-devtools' ), $gh_label ) ); ?></span>
                    </a>
                </div>
            <?php endif; ?>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * Enqueues frontend scripts and styles on first block render, then localises config.
     *
     * @since  1.0.0
     * @return void
     */
    private static function maybe_enqueue_frontend() {
        if ( self::$assets_enqueued ) {
            return;
        }
        self::$assets_enqueued = true;

        wp_enqueue_style( 'hljs-theme-dark' );
        wp_enqueue_style( 'hljs-theme-light' );
        wp_enqueue_style( 'csdt-code-block-frontend' );
        wp_enqueue_script( 'hljs-core' );
        wp_enqueue_script( 'csdt-code-block-frontend' );

        $default_theme = get_option( 'csdt_devtools_code_default_theme', 'dark' );
        $pair_slug     = get_option( 'csdt_devtools_code_theme_pair', 'atom-one' );
        $registry      = self::get_theme_registry();
        $pair          = isset( $registry[ $pair_slug ] ) ? $registry[ $pair_slug ] : $registry['atom-one'];

        wp_localize_script(
            'csdt-code-block-frontend',
            'csdtDevtoolsCodeConfig',
            array(
				'defaultTheme' => $default_theme,
				'themePair'    => $pair_slug,
				'darkBg'       => $pair['dark_bg'],
				'darkToolbar'  => $pair['dark_toolbar'],
				'lightBg'      => $pair['light_bg'],
				'lightToolbar' => $pair['light_toolbar'],
			)
        );
    }

    /*
    ==================================================================
        3. SHORTCODE [csdt_devtools_code]
        ================================================================== */

    /**
     * Registers the [csdt_devtools_code] shortcode.
     *
     * @since  1.0.0
     * @return void
     */
    public static function register_shortcode() {
        add_shortcode( 'csdt_devtools_code', array( __CLASS__, 'render_shortcode' ) );
    }

    /**
     * Renders the [csdt_devtools_code] shortcode.
     *
     * @since  1.0.0
     * @param  array       $atts    Shortcode attributes.
     * @param  string|null $content Shortcode content.
     * @return string HTML output.
     */
    public static function render_shortcode( $atts, $content = null ) {
        $atts = shortcode_atts(
            array(
				'lang'  => '',
				'theme' => '',
				'title' => '',
            ),
            $atts,
            'csdt_devtools_code'
        );

        $code = self::decode_shortcode_content( $content );

        return self::render_block(
            array(
				'content'  => $code,
				'language' => $atts['lang'],
				'title'    => $atts['title'],
				'theme'    => $atts['theme'],
            )
        );
    }

    /**
     * Decodes WordPress-mangled HTML entities and line breaks from shortcode content.
     *
     * @since  1.0.0
     * @param  string|null $content Raw shortcode content.
     * @return string Plain-text code with entities decoded.
     */
    private static function decode_shortcode_content( $content ) {
        $content = preg_replace( '#^<p>|</p>$#i', '', trim( $content ) );
        $content = str_replace(
            array( '<br />', '<br/>', '<br>', '&#8220;', '&#8221;', '&#8216;', '&#8217;', '&nbsp;', '&#038;' ),
            array( "\n", "\n", "\n", '"', '"', "'", "'", ' ', '&' ),
            $content
        );
        $content = html_entity_decode( $content, ENT_QUOTES, 'UTF-8' );
        return trim( $content );
    }

    /*
    ==================================================================
        4. SETTINGS
        ================================================================== */

    /**
     * Registers plugin settings with sanitise callbacks.
     *
     * @since  1.0.0
     * @return void
     */
    public static function register_settings() {
        register_setting(
            'csdt_devtools_code_settings',
            'csdt_devtools_code_default_theme',
            array(
				'type'              => 'string',
				'sanitize_callback' => function ( $val ) {
					return in_array( $val, array( 'dark', 'light' ) ) ? $val : 'dark';
				},
				'default'           => 'dark',
			)
        );

        $valid_themes = array_keys( self::get_theme_registry() );
        register_setting(
            'csdt_devtools_code_settings',
            'csdt_devtools_code_theme_pair',
            array(
				'type'              => 'string',
				'sanitize_callback' => function ( $val ) use ( $valid_themes ) {
					return in_array( $val, $valid_themes, true ) ? $val : 'atom-one';
				},
				'default'           => 'atom-one',
			)
        );

        register_setting(
            'csdt_devtools_code_settings',
            'csdt_devtools_perf_monitor_enabled',
            array(
				'type'              => 'string',
				'sanitize_callback' => function ( $val ) {
					return '0' === $val ? '0' : '1';
				},
				'default'           => '1',
			)
        );

        register_setting(
            'csdt_devtools_code_settings',
            'csdt_show_powered_by',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					return '1' === $v ? '1' : '0'; },
				'default'           => '0',
			)
        );

        // Login security settings
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_login_hide_enabled',
            array(
				'type'              => 'string',
				'sanitize_callback' => function ( $v ) {
					return '1' === $v ? '1' : '0'; },
				'default'           => '0',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_login_slug',
            array(
				'type'              => 'string',
				'sanitize_callback' => function ( $v ) {
					$slug = sanitize_title( $v );
					// Disallow WP reserved slugs
					$reserved = array( 'wp-login', 'wp-admin', 'login', 'admin', 'dashboard' );
					return in_array( $slug, $reserved, true ) ? '' : $slug;
				},
				'default'           => '',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_2fa_method',
            array(
				'type'              => 'string',
				'sanitize_callback' => function ( $v ) {
					return in_array( $v, array( 'off', 'email', 'totp' ), true ) ? $v : 'off';
				},
				'default'           => 'off',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_2fa_force_admins',
            array(
				'type'              => 'string',
				'sanitize_callback' => function ( $v ) {
					return '1' === $v ? '1' : '0'; },
				'default'           => '0',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_2fa_grace_logins',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					$n = (int) $v;
					return ( $n >= 0 && $n <= 10 ) ? (string) $n : '0';
				},
				'default'           => '0',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_session_duration',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					$valid = array( 'default', '1', '7', '14', '30', '90', '365' );
					return in_array( $v, $valid, true ) ? $v : 'default';
				},
				'default'           => 'default',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_brute_force_enabled',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					return $v === '1' ? '1' : '0'; },
				'default'           => '1',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_brute_force_attempts',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					$n = (int) $v;
					return ( $n >= 1 && $n <= 100 ) ? (string) $n : '5';
				},
				'default'           => '5',
			)
        );
        register_setting(
            'csdt_devtools_login_settings',
            'csdt_devtools_brute_force_lockout',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					$n = (int) $v;
					return ( $n >= 1 && $n <= 1440 ) ? (string) $n : '5';
				},
				'default'           => '5',
			)
        );

        // SMTP settings
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_enabled',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					return $v === '1' ? '1' : '0'; },
				'default'           => '0',
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_host',
            array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => '',
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_port',
            array(
				'type'              => 'integer',
				'sanitize_callback' => static function ( $v ) {
					$v = absint( $v );
					return ( $v >= 1 && $v <= 65535 ) ? $v : 587;
				},
				'default'           => 587,
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_encryption',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					return in_array( $v, array( 'tls', 'ssl', 'none' ), true ) ? $v : 'tls';
				},
				'default'           => 'tls',
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_auth',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					return $v === '1' ? '1' : '0'; },
				'default'           => '1',
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_user',
            array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => '',
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_pass',
            array(
				'type'              => 'string',
				'sanitize_callback' => static function ( $v ) {
					return $v; },
				'default'           => '',
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_from_email',
            array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_email',
				'default'           => '',
			)
        );
        register_setting(
            'csdt_devtools_smtp_settings',
            'csdt_devtools_smtp_from_name',
            array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => '',
			)
        );

        register_setting(
            'csdt_devtools_thumbs_settings',
            'csdt_devtools_openai_key',
            array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => '',
			)
        );
    }

    /*
    ==================================================================
        5. COMBINED TOOLS PAGE (Code Block Migrator + SQL Command)
        ================================================================== */

    /**
     * Adds the combined Tools page to the WordPress admin menu.
     *
     * @since  1.6.0
     * @return void
     */
    /**
     * Redirects legacy ?page=cloudscale-code-sql URLs to the new slug.
     *
     * @since  1.8.56
     * @return void
     */
    /**
     * Redirects the old help page URL to the current one.
     *
     * @since  1.8.56
     * @return void
     */
    public static function maybe_lscache_purge(): void {
        // Front-end endpoint: GET /?csdt_lscache_purge=TOKEN
        // Fires before WordPress loads the page, so LSWS processes the LiteSpeed
        // purge header in this normal front-end response (unlike admin-ajax.php which
        // LSWS excludes from cache header processing).
        if ( ! isset( $_GET['csdt_lscache_purge'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return;
        }
        $token  = sanitize_text_field( wp_unslash( (string) $_GET['csdt_lscache_purge'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $stored = get_option( 'csdt_opcache_token', '' );
        if ( ! $stored || ! $token || ! hash_equals( $stored, $token ) ) {
            wp_die( esc_html__( 'Unauthorized', 'cloudscale-cyber-devtools' ), '', 403 );
        }
        // Send the purge header directly so LSWS clears its full-page cache even
        // when exit fires before wp_headers (which is where LiteSpeed plugin normally injects it).
        header( 'X-LiteSpeed-Purge: *' );
        do_action( 'litespeed_purge_all' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- third-party LiteSpeed hook
        header( 'Content-Type: application/json; charset=utf-8' );
        echo wp_json_encode( array( 'purged' => true ) );
        exit;
    }

    public static function redirect_legacy_help_url() {
        $uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
        if ( strpos( $uri, 'code-block-help' ) !== false ) {
            wp_safe_redirect( home_url( '/wordpress-plugin-help/cloudscale-cyber-devtools-help/' ), 301 );
            exit;
        }
    }

    public static function redirect_legacy_slug() {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( in_array( $page, array( 'cloudscale-code-sql', 'cyber-devtools' ), true ) ) {
            $args         = array_map( 'sanitize_text_field', wp_unslash( $_GET ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Recommended -- read-only slug redirect
            $args['page'] = self::TOOLS_SLUG;
            wp_safe_redirect( add_query_arg( array_map( 'rawurlencode', $args ), admin_url( 'tools.php' ) ) );
            exit;
        }
    }

    public static function add_tools_page() {
        add_management_page(
            'CloudScale Cyber and Devtools',
            '🔐 Cyber and Devtools',
            'manage_options',
            self::TOOLS_SLUG,
            array( __CLASS__, 'render_tools_page' )
        );
    }

    /**
     * Conditionally enqueues admin assets on the plugin tools page only.
     *
     * @since  1.6.0
     * @param  string $hook Current admin page hook suffix.
     * @return void
     */
    public static function enqueue_admin_assets( $hook ) {
        // Dashboard widget inline CSS, needed on the dashboard only.
        if ( $hook === 'index.php' ) {
            wp_add_inline_style( 'dashboard', self::get_dashboard_widget_css() );
        }

        // All remaining assets are only needed on the plugin's own tools page.
        if ( $hook !== 'tools_page_' . self::TOOLS_SLUG ) {
            return;
        }

        // Shared CloudScale admin design system (single source of truth), load first.
        wp_enqueue_style(
            'cs-design-system',
            plugins_url( 'assets/cloudscale-admin.css', __FILE__ ),
            array(),
            self::VERSION
        );
        // Tabs CSS
        wp_enqueue_style(
            'csdt-admin-tabs',
            plugins_url( 'assets/cs-admin-tabs.css', __FILE__ ),
            array( 'cs-design-system' ),
            self::VERSION
        );
        // Explain modal description styling, scoped to .cs-explain-desc.
        wp_add_inline_style( 'csdt-admin-tabs', CSDT_Perf_Monitor::get_explain_modal_css() );

        // Migrate CSS + JS
        wp_enqueue_style(
            'csdt-code-migrate',
            plugins_url( 'assets/cs-code-migrate.css', __FILE__ ),
            array(),
            self::VERSION
        );
        wp_enqueue_script(
            'csdt-code-migrate',
            plugins_url( 'assets/cs-code-migrate.js', __FILE__ ),
            array(),
            self::VERSION,
            true
        );
        wp_localize_script(
            'csdt-code-migrate',
            'csdtDevtoolsMigrate',
            array(
				'ajaxUrl'       => admin_url( 'admin-ajax.php' ),
				'nonce'         => wp_create_nonce( self::MIGRATE_NONCE ),
				// Settings-save uses a different nonce action because it shares a
				// handler with the Diagnostics → Code Block Settings panel.
				'settingsNonce' => wp_create_nonce( 'csdt_devtools_code_settings_inline' ),
			)
        );

        // Settings save JS
        wp_enqueue_script(
            'csdt-admin-settings',
            plugins_url( 'assets/cs-admin-settings.js', __FILE__ ),
            array(),
            self::VERSION,
            true
        );
        wp_localize_script(
            'csdt-admin-settings',
            'csdtDevtoolsAdminSettings',
            array(
				'nonce' => wp_create_nonce( 'csdt_devtools_code_settings_inline' ),
			)
        );

        // SQL editor JS
        wp_enqueue_script(
            'csdt-sql-editor',
            plugins_url( 'assets/cs-sql-editor.js', __FILE__ ),
            array(),
            self::VERSION,
            true
        );
        wp_localize_script(
            'csdt-sql-editor',
            'csdtDevtoolsSqlEditor',
            array(
				'nonce' => wp_create_nonce( self::SQL_NONCE ),
			)
        );

        // Tab router, client-side switching without full page reloads
        wp_enqueue_script(
            'csdt-tab-router',
            plugins_url( 'assets/cs-tab-router.js', __FILE__ ),
            array(),
            self::VERSION,
            true
        );

        // Login security JS (only loaded on the login tab)
        $active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'home'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( $active_tab === 'login' ) {
            wp_enqueue_style(
                'csdt-leaflet-css',
                plugins_url( 'assets/leaflet.min.css', __FILE__ ),
                array(),
                self::VERSION
            );
            wp_enqueue_script(
                'csdt-leaflet-js',
                plugins_url( 'assets/leaflet.min.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_enqueue_script(
                'csdt-qrcode',
                plugins_url( 'assets/qrcode.min.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_enqueue_script(
                'csdt-login',
                plugins_url( 'assets/cs-login.js', __FILE__ ),
                array( 'csdt-qrcode', 'csdt-leaflet-js' ),
                self::VERSION,
                true
            );
            // Build per-country blocked counts (7-day sum) for the map.
            $wplogin_map_stats = get_option( 'csdt_wplogin_blocked_stats', array() );
            $countries_blocked = array();
            if ( isset( $wplogin_map_stats['country_stats'] ) && is_array( $wplogin_map_stats['country_stats'] ) ) {
                foreach ( $wplogin_map_stats['country_stats'] as $cc => $days ) {
                    if ( is_array( $days ) ) {
                        $countries_blocked[ $cc ] = array_sum( $days );
                    }
                }
            }
            wp_localize_script(
                'csdt-login',
                'csdtDevtoolsLogin',
                array(
					'ajaxUrl'          => admin_url( 'admin-ajax.php' ),
					'nonce'            => wp_create_nonce( 'csdt_devtools_login_nonce' ),
					'secNonce'         => wp_create_nonce( self::SECURITY_NONCE ),
					'currentUser'      => get_current_user_id(),
					'mailTabUrl'       => admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=mail' ),
					'countriesBlocked' => $countries_blocked,
					'hideLoginEnabled' => get_option( 'csdt_devtools_login_hide_enabled', '0' ),
				)
            );
            wp_enqueue_script(
                'csdt-passkey',
                plugins_url( 'assets/cs-passkey.js', __FILE__ ),
                array( 'csdt-login' ),
                self::VERSION,
                true
            );
            wp_enqueue_script(
                'csdt-test-accounts',
                plugins_url( 'assets/cs-test-accounts.js', __FILE__ ),
                array( 'csdt-login' ),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-test-accounts',
                'csdtTestAccounts',
                array(
					'ajaxUrl'    => admin_url( 'admin-ajax.php' ),
					'nonce'      => wp_create_nonce( 'csdt_devtools_login_nonce' ),
					'testUsers'  => CSDT_Test_Accounts::get_test_users_with_sessions(),
					'secret'     => CSDT_Test_Accounts::get_or_create_secret(),
					'sessionUrl' => rest_url( 'csdt/v1/test-session-' . CSDT_Test_Accounts::get_or_create_path_token() ),
					'logoutUrl'  => rest_url( 'csdt/v1/test-logout-' . CSDT_Test_Accounts::get_or_create_path_token() ),
					'siteUrl'    => home_url(),
				)
            );
        }

        if ( $active_tab === 'mail' ) {
            wp_enqueue_script(
                'csdt-smtp',
                plugins_url( 'assets/cs-smtp.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-smtp',
                'csdtDevtoolsSmtp',
                array(
					'ajaxUrl' => admin_url( 'admin-ajax.php' ),
					'nonce'   => wp_create_nonce( self::SMTP_NONCE ),
					'testTo'  => wp_get_current_user()->user_email,
				)
            );
        }

        if ( $active_tab === 'debug' || $active_tab === '404' ) {
            wp_enqueue_script(
                'csdt-404-admin',
                plugins_url( 'assets/cs-404-admin.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-404-admin',
                'csdtDevtools404',
                array(
					'ajaxUrl'    => admin_url( 'admin-ajax.php' ),
					'nonce'      => wp_create_nonce( 'csdt_devtools_404_settings' ),
					'custom_404' => get_option( self::CUSTOM_404_OPTION, 1 ) ? 1 : 0,
					'scheme'     => get_option( self::SCHEME_404_OPTION, 'ocean' ),
					'previewUrl' => home_url( '/this-page-does-not-exist' ),
				)
            );
        }

        if ( in_array( $active_tab, array( 'security', 'headers', 'home' ), true ) ) {
            wp_enqueue_script(
                'csdt-vuln-scan',
                plugins_url( 'assets/cs-vuln-scan.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            $saved_model      = get_option( 'csdt_devtools_security_model', '_auto' );
            $saved_deep_model = get_option( 'csdt_devtools_deep_scan_model', '_auto_deep' );
            $saved_prompt     = get_option( 'csdt_devtools_security_prompt', '' );
            $saved_provider   = get_option( 'csdt_devtools_ai_provider', 'anthropic' );
            $api_key          = get_option( 'csdt_devtools_anthropic_key', '' );
            $gemini_key       = get_option( 'csdt_devtools_gemini_key', '' );
            $masked_key       = $api_key ? '••••••••' . substr( $api_key, -4 ) : '';
            $masked_gemini    = $gemini_key ? '••••••••' . substr( $gemini_key, -4 ) : '';
            $has_key          = $saved_provider === 'gemini' ? ! empty( $gemini_key ) : ! empty( $api_key );
            wp_localize_script(
                'csdt-vuln-scan',
                'csdtVulnScan',
                array(
					'ajaxUrl'        => admin_url( 'admin-ajax.php' ),
					'nonce'          => wp_create_nonce( self::SECURITY_NONCE ),
					'hasKey'         => $has_key,
					'savedProvider'  => $saved_provider,
					'maskedKey'      => $masked_key,
					'maskedGemini'   => $masked_gemini,
					'apiKey'         => $api_key,
					'geminiKey'      => $gemini_key,
					'savedModel'     => $saved_model,
					'savedDeepModel' => $saved_deep_model,
					'savedPrompt'    => $saved_prompt,
					'defaultPrompt'  => CSDT_Site_Audit::default_security_prompt(),
					'scanHistory'    => get_option( 'csdt_scan_history', array() ),
					'homeUrl'        => home_url( '/' ),
					'adhocHistory'   => get_option( 'csdt_adhoc_scans', array() ),
				)
            );
            wp_enqueue_script(
                'csdt-sec-headers',
                plugins_url( 'assets/cs-sec-headers.js', __FILE__ ),
                array( 'csdt-vuln-scan' ),
                self::VERSION,
                true
            );
            wp_enqueue_script(
                'csdt-csp',
                plugins_url( 'assets/cs-csp.js', __FILE__ ),
                array( 'csdt-vuln-scan' ),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-csp',
                'csdtCspI18n',
                array(
					'rollbackLabel' => esc_js( __( 'Rollback to previous settings', 'cloudscale-cyber-devtools' ) ),
					'siteUrl'       => home_url( '/' ),
				)
            );
            wp_enqueue_script(
                'csdt-prefix-rollback',
                plugins_url( 'assets/cs-prefix-rollback.js', __FILE__ ),
                array( 'csdt-vuln-scan' ),
                self::VERSION,
                true
            );
        }

        if ( $active_tab === 'thumbnails' || $active_tab === 'ai-images' ) {
            wp_enqueue_media();
            $thumb_js = plugin_dir_path( __FILE__ ) . 'assets/cs-thumbnails.js';
            wp_enqueue_script(
                'csdt-thumbnails',
                plugins_url( 'assets/cs-thumbnails.js', __FILE__ ),
                array( 'jquery' ),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-thumbnails',
                'csdtDevtoolsThumbs',
                array(
					'ajaxUrl'      => admin_url( 'admin-ajax.php' ),
					'nonce'        => wp_create_nonce( 'csdt_devtools_thumbnails' ),
					'siteUrl'      => home_url( '/' ),
					'defimgNonce'  => wp_create_nonce( 'csdt_defimg' ),
					'styleOptions' => CSDT_Thumbnails::get_image_style_options(),
				)
            );
            // Thumbnails-tab-specific CSS, injected as inline style to avoid an
            // extra HTTP request and keep the render method free of <style> tags.
            wp_add_inline_style( 'csdt-admin-tabs', CSDT_Perf_Monitor::get_thumbnails_admin_css() );
        }

        if ( $active_tab === 'credentials' ) {
            // Credentials tab needs cs-plugin-stack.js for Show/Copy/Rotate/Save-alerts buttons.
            wp_enqueue_script(
                'csdt-optimizer',
                plugins_url( 'assets/cs-plugin-stack.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-optimizer',
                'csdtOptimizer',
                array(
					'ajaxUrl' => admin_url( 'admin-ajax.php' ),
					'nonce'   => wp_create_nonce( self::OPTIMIZER_NONCE ),
					'baseUrl' => admin_url( 'tools.php?page=' . self::TOOLS_SLUG ),
					'hasAi'   => CSDT_AI_Dispatcher::has_key(),
				)
            );
            // Also provide login nonce (used by regen handlers) and thumbnails nonce (alerts save).
            wp_add_inline_script(
                'csdt-optimizer',
                'window.csdtDevtoolsLogin = window.csdtDevtoolsLogin || { nonce: ' . wp_json_encode( wp_create_nonce( self::LOGIN_NONCE ) ) . ', ajaxUrl: ' . wp_json_encode( admin_url( 'admin-ajax.php' ) ) . ' };'
                . 'window.csdtDevtoolsThumbs = window.csdtDevtoolsThumbs || { nonce: ' . wp_json_encode( wp_create_nonce( 'csdt_devtools_thumbnails' ) ) . ', ajaxUrl: ' . wp_json_encode( admin_url( 'admin-ajax.php' ) ) . ' };'
                . 'window.csdtTestAccounts = window.csdtTestAccounts || { nonce: ' . wp_json_encode( wp_create_nonce( 'csdt_devtools_login_nonce' ) ) . ', ajaxUrl: ' . wp_json_encode( admin_url( 'admin-ajax.php' ) ) . ' };',
                'before'
            );
        }

        if ( $active_tab === 'optimizer' ) {
            wp_enqueue_script(
                'csdt-optimizer',
                plugins_url( 'assets/cs-plugin-stack.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-optimizer',
                'csdtOptimizer',
                array(
					'ajaxUrl' => admin_url( 'admin-ajax.php' ),
					'nonce'   => wp_create_nonce( self::OPTIMIZER_NONCE ),
					'baseUrl' => admin_url( 'tools.php?page=' . self::TOOLS_SLUG ),
					'hasAi'   => CSDT_AI_Dispatcher::has_key(),
				)
            );
            wp_enqueue_script(
                'csdt-optimizer-panel',
                plugins_url( 'assets/cs-optimizer-panel.js', __FILE__ ),
                array( 'csdt-optimizer' ),
                self::VERSION,
                true
            );
        }

        if ( $active_tab === 'site-audit' ) {
            wp_enqueue_script(
                'csdt-site-audit',
                plugins_url( 'assets/cs-site-audit.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            $audit_cache = get_option( 'csdt_site_audit_cache', null );
            $_cc         = method_exists( 'CSDT_Site_Audit', 'get_combined_counts' )
                ? CSDT_Site_Audit::get_combined_counts()
                : array(
					'scan_critical' => 0,
					'scan_high'     => 0,
					'audit_run_at'  => 0,
				);
            wp_localize_script(
                'csdt-site-audit',
                'csdtSiteAudit',
                array(
					'ajaxUrl'      => admin_url( 'admin-ajax.php' ),
					'nonce'        => wp_create_nonce( self::SITE_AUDIT_NONCE ),
					'secNonce'     => wp_create_nonce( self::SECURITY_NONCE ),
					'seoAiUrl'     => admin_url( 'tools.php?page=cs-seo-optimizer' ),
					'cached'       => $audit_cache ? $audit_cache['data'] : null,
					'cachedAt'     => $audit_cache ? $audit_cache['run_at'] : null,
					'scanCritical' => $_cc['scan_critical'],
					'scanHigh'     => $_cc['scan_high'],
					'scanUrl'      => admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=security' ),
				)
            );
        }

        if ( $active_tab === 'debug' || $active_tab === 'logs' ) {
            wp_add_inline_style( 'csdt-admin-tabs', self::get_log_viewer_css() );
            $logs_js = plugin_dir_path( __FILE__ ) . 'assets/cs-server-logs.js';
            wp_enqueue_script(
                'csdt-server-logs',
                plugins_url( 'assets/cs-server-logs.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-server-logs',
                'csdtServerLogs',
                array(
					'ajaxUrl' => admin_url( 'admin-ajax.php' ),
					'nonce'   => wp_create_nonce( self::LOGS_NONCE ),
					'sources' => self::get_log_sources(),
				)
            );
        }

        if ( $active_tab === 'debug' ) {
            wp_enqueue_script(
                'csdt-debug',
                plugins_url( 'assets/cs-debug.js', __FILE__ ),
                array(),
                self::VERSION,
                true
            );
            wp_localize_script(
                'csdt-debug',
                'csdtDebug',
                array(
					'ajaxUrl'        => admin_url( 'admin-ajax.php' ),
					'logsNonce'      => wp_create_nonce( self::LOGS_NONCE ),
					'aiNonce'        => wp_create_nonce( self::OPTIMIZER_NONCE ),
					'debugNonce'     => wp_create_nonce( self::DEBUG_NONCE ),
					'fpmNonce'       => wp_create_nonce( self::FPM_NONCE ),
					'perfNonce'      => wp_create_nonce( self::PERF_NONCE ),
					'perfEnabled'    => get_option( 'csdt_devtools_perf_monitor_enabled', '1' ),
					'objCacheNonce'  => wp_create_nonce( 'csdt_object_cache_nonce' ),
					'objCacheStatus' => CSDT_Code_Migrator::get_object_cache_status(),
					'sources'        => self::get_log_sources(),
				)
            );
        }

        // Email-verified modal countdown, only needed when the verification
        // redirect lands back on the login tab with ?email_2fa_activated=1.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( $active_tab === 'login' && isset( $_GET['email_2fa_activated'] ) && '1' === $_GET['email_2fa_activated'] ) {
            wp_add_inline_script(
                'csdt-admin-settings',
                '(function(){' .
                'var modal=document.getElementById("cs-email-verified-modal");' .
                'var cd=document.getElementById("cs-modal-countdown");' .
                'var closeBtn=document.getElementById("cs-email-modal-close");' .
                'var n=6;' .
                'var t=setInterval(function(){n--;if(cd)cd.textContent=n;if(n<=0){clearInterval(t);if(modal)modal.style.display="none";}},1000);' .
                'function dismiss(){clearInterval(t);if(modal)modal.style.display="none";}' .
                'if(closeBtn)closeBtn.addEventListener("click",dismiss);' .
                'if(modal)modal.addEventListener("click",function(e){if(e.target===modal)dismiss();});' .
                '(function(){var u=new URL(location.href);u.searchParams.delete("email_2fa_activated");history.replaceState(null,"",u.toString());})();' .
                '})()'
            );
        }
    }

    /**
     * Renders the combined Code Migrator and SQL Command tools page.
     *
     * @since  1.6.0
     * @return void
     */
    public static function render_tools_page() {
        $active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'home'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only tab routing; no state change
        $base_url   = admin_url( 'tools.php?page=' . self::TOOLS_SLUG );
        ?>
        <div class="wrap">
        <div id="cs-app">

            <!-- Banner -->
            <div id="cs-banner">
                <div id="cs-banner-top">
                    <div id="cs-banner-title"><span style="flex-shrink:0;font-size:18px;line-height:1">🔐</span><span><?php esc_html_e( 'Cyber Devtools', 'cloudscale-cyber-devtools' ); ?></span></div>
                    <div id="cs-banner-right">
                        <span class="cs-badge cs-badge-version">v<?php echo esc_html( self::VERSION ); ?></span>
                        <span class="cs-badge cs-badge-green">✅ <?php esc_html_e( 'Totally Free', 'cloudscale-cyber-devtools' ); ?></span>
                        <a href="https://cloudscale.consulting" target="_blank" rel="noopener noreferrer" class="cs-badge cs-badge-orange" style="text-decoration:none">cloudscale.consulting</a>
                        <a href="https://help.cloudscale.consulting/plugin-help/cyber-devtools/" target="_blank" rel="noopener noreferrer" class="cs-badge cs-badge-help" style="text-decoration:none">❓ <?php esc_html_e( 'Help', 'cloudscale-cyber-devtools' ); ?></a>
                    </div>
                </div>
                <div id="cs-banner-sub"><?php esc_html_e( 'AI security scanner · 2FA · SMTP mailer · SQL tools · developer toolkit', 'cloudscale-cyber-devtools' ); ?></div>
            </div>

            <!-- Tab bar -->
            <div id="cs-tab-bar">
                <a href="<?php echo esc_url( $base_url . '&tab=home' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'home' ? 'active' : ''; ?>">
                    🏠 <?php esc_html_e( 'Home', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=site-audit' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'site-audit' ? 'active' : ''; ?>">
                    🔍 <?php esc_html_e( 'Site Audit', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=login' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'login' ? 'active' : ''; ?>">
                    🔐 <?php esc_html_e( 'Login Security', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=security' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'security' ? 'active' : ''; ?>">
                    🛡️ <?php esc_html_e( 'AI Security Scan', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=headers' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'headers' ? 'active' : ''; ?>">
                    🔒 <?php esc_html_e( 'Headers', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=ai-images' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'ai-images' ? 'active' : ''; ?>">
                    🎨 <?php esc_html_e( 'Featured Images', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=optimizer' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'optimizer' ? 'active' : ''; ?>">
                    ⚡ <?php esc_html_e( 'Performance', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=migrate' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'migrate' ? 'active' : ''; ?>">
                    🔄 <?php esc_html_e( 'Code Block Migrator', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=debug' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'debug' ? 'active' : ''; ?>">
                    🩺 <?php esc_html_e( 'Diagnostics', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=mail' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'mail' ? 'active' : ''; ?>">
                    🔔 <?php esc_html_e( 'Alerts', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=thumbnails' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'thumbnails' ? 'active' : ''; ?>">
                    🖼️ <?php esc_html_e( 'Thumbnails', 'cloudscale-cyber-devtools' ); ?>
                </a>
                <a href="<?php echo esc_url( $base_url . '&tab=credentials' ); ?>"
                    class="cs-tab <?php echo $active_tab === 'credentials' ? 'active' : ''; ?>" style="<?php echo $active_tab === 'credentials' ? '' : 'color:#f59e0b;'; ?>">
                    🔑 <?php esc_html_e( 'Credentials', 'cloudscale-cyber-devtools' ); ?>
                </a>
            </div>
            <!-- Copy All action bar -->
            <div id="cs-tab-actions">
                <button id="cs-copy-all-btn" class="cs-copy-all-btn" title="<?php esc_attr_e( 'Copy all content from this tab to clipboard', 'cloudscale-cyber-devtools' ); ?>">
                    &#128203; <?php esc_html_e( 'Copy All', 'cloudscale-cyber-devtools' ); ?>
                </button>
            </div>

            <?php if ( $active_tab === 'home' ) : ?>
                <div class="cs-tab-content active">
                    <?php self::render_home_panel(); ?>
                </div>

            <?php elseif ( $active_tab === 'login' ) : ?>
                <div class="cs-tab-content active">
                    <?php self::render_login_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'mail' ) : ?>
                <div class="cs-tab-content active">
                    <?php CSDT_SMTP::render_notification_channels_panel(); ?>
                    <?php CSDT_SMTP::render_smtp_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'thumbnails' ) : ?>
                <div class="cs-tab-content active">
                    <?php CSDT_Thumbnails::render_thumbnails_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'credentials' ) : ?>
                <div class="cs-tab-content active">
                    <?php self::render_credentials_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'ai-images' ) : ?>
                <div class="cs-tab-content active">
                    <?php CSDT_Thumbnails::render_ai_images_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'security' ) : ?>
                <div class="cs-tab-content active">
                    <?php self::render_security_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'headers' ) : ?>
                <div class="cs-tab-content active">
                    <?php CSDT_Security_Headers::render_header_scan_panel(); ?>
                    <?php CSDT_Security_Headers::render_security_headers_panel( false ); ?>
                    <?php CSDT_CSP::render_csp_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'optimizer' ) : ?>
                <div class="cs-tab-content active">
                    <?php self::render_optimizer_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'site-audit' ) : ?>
                <div class="cs-tab-content active">
                    <?php CSDT_Site_Audit::render_site_audit_panel(); ?>
                </div>
            <?php elseif ( $active_tab === 'migrate' ) : ?>
                <div class="cs-tab-content active">
                    <?php self::render_migrate_panel(); ?>
                </div>
            <?php elseif ( in_array( $active_tab, array( 'debug', 'logs', 'sql', '404' ), true ) ) : ?>
                <div class="cs-tab-content active">
                    <?php self::render_debug_panel(); ?>
                </div>
            <?php endif; ?>

            <?php CSDT_Site_Audit::render_quick_fix_modals(); ?>

        </div>
        </div>
        <?php
    }

    /*
    ==================================================================
        5a. Settings panel (inline on Migrator tab)
        ================================================================== */

    /**
     * Renders the Code Block Settings panel (colour theme and default mode selectors).
     *
     * @since  1.6.0
     * @return void
     */
    /**
     * Renders an "Explain…" button and its associated modal for a panel header.
     *
     * @param string $id    Unique slug used to build element IDs.
     * @param string $title Modal title.
     * @param array  $items Array of ['name'=>'', 'rec'=>'', 'desc'=>''] entries.
     */
    /**
     * Allowed HTML tags/attrs for item descriptions that contain links.
     *
     * @var array<string,array<string,bool>>
     */
    private static array $explain_kses = array(
        'a'      => array(
			'href'   => true,
			'target' => true,
			'rel'    => true,
		),
        'strong' => array(),
        'em'     => array(),
        'code'   => array(),
        'br'     => array(),
        'ul'     => array(),
        'ol'     => array(),
        'li'     => array(),
        'p'      => array(),
        'h4'     => array(),
    );

    /**
     * Renders an "Explain…" button + inline modal.
     *
     * Each item in $items may have:
     *   'name' => string, section heading
     *   'rec'  => string, badge label (Recommended | Note | Optional)
     *   'desc' => string, plain-text description (escaped with esc_html)
     *   'html' => string, HTML description rendered via wp_kses (overrides 'desc')
     *
     * @param string $id    Unique slug used to build element IDs.
     * @param string $title Modal title.
     * @param array  $items Array of item arrays.
     */
    public static function render_explain_btn( string $id, string $title, array $items, string $intro = '', bool $light = false ): void {
        $btn_id    = 'cs-explain-btn-' . $id;
        $modal_id  = 'cs-explain-modal-' . $id;
        $btn_style = $light
            ? 'background:#eff6ff!important;border:1.5px solid #3b82f6!important;border-radius:5px!important;color:#1d4ed8!important;font-size:12px!important;font-weight:700!important;padding:5px 14px!important;cursor:pointer!important;margin-left:auto!important;flex-shrink:0!important;display:block!important;box-shadow:0 1px 3px rgba(59,130,246,0.15)!important;text-shadow:none!important;text-transform:none!important;letter-spacing:normal!important;line-height:1.4!important;'
            : 'background:rgba(255,255,255,0.15)!important;border:1px solid rgba(255,255,255,0.5)!important;border-radius:5px!important;color:#fff!important;font-size:12px!important;font-weight:600!important;padding:5px 14px!important;cursor:pointer!important;margin-left:auto!important;flex-shrink:0!important;display:block!important;box-shadow:none!important;text-shadow:none!important;text-transform:none!important;letter-spacing:normal!important;line-height:1.4!important;backdrop-filter:blur(4px)!important';
        ?>
        <button type="button" id="<?php echo esc_attr( $btn_id ); ?>"
            data-cs-modal-open="<?php echo esc_attr( $modal_id ); ?>"
            style="<?php echo esc_attr( $btn_style ); ?>">
            Explain&hellip;
        </button>
        <div id="<?php echo esc_attr( $modal_id ); ?>"
            style="display:none;position:fixed;inset:0;z-index:100002;background:rgba(0,0,0,0.55);align-items:center;justify-content:center;padding:16px;text-transform:none;letter-spacing:normal;font-weight:normal"
            data-cs-modal-backdrop="true">
            <div style="background:#fff;border-radius:10px;max-width:600px;width:100%;max-height:88vh;overflow-y:auto;box-shadow:0 8px 32px rgba(0,0,0,0.25)">
                <div style="padding:18px 22px 12px;border-bottom:1px solid #e5e7eb;display:flex;align-items:center;gap:10px">
                    <strong style="font-size:15px;color:#111"><?php echo esc_html( $title ); ?></strong>
                    <button type="button"
                        data-cs-modal-close="<?php echo esc_attr( $modal_id ); ?>"
                        style="margin-left:auto;background:none;border:none;font-size:20px;cursor:pointer;color:#888;line-height:1;padding:0">&times;</button>
                </div>
                <div style="padding:16px 22px 20px">
                    <?php if ( $intro ) : ?>
                    <div style="background:#f0f6ff;border-left:4px solid #2271b1;border-radius:0 6px 6px 0;padding:12px 16px;margin-bottom:14px;font-size:13px;color:#1a1a1a;line-height:1.6">
                        <?php echo wp_kses( $intro, self::$explain_kses ); ?>
                    </div>
                    <?php endif; ?>
                    <?php
                    foreach ( $items as $item ) :
                        $rec = $item['rec'];
                        $rl  = strtolower( $rec );
                        if ( str_contains( $rl, 'critical' ) || str_contains( $rl, 'not recommended' ) ) {
                            $bg  = '#fef2f2';
							$col = '#991b1b';
							$bdr = '#dc2626';
                        } elseif ( str_contains( $rl, 'high' ) ) {
                            $bg  = '#fff7ed';
							$col = '#9a3412';
							$bdr = '#f97316';
                        } elseif ( str_contains( $rl, 'recommended' ) ) {
                            $bg  = '#f0fdf4';
							$col = '#14532d';
							$bdr = '#16a34a';
                        } elseif ( str_contains( $rl, 'important' ) || str_contains( $rl, 'required' ) ) {
                            $bg  = '#fffbeb';
							$col = '#92400e';
							$bdr = '#d97706';
                        } elseif ( str_contains( $rl, 'optional' ) || str_contains( $rl, 'note' ) ) {
                            $bg  = '#f6f7f7';
							$col = '#50575e';
							$bdr = '#c3c4c7';
                        } elseif ( str_contains( $rl, 'info' ) || str_contains( $rl, 'overview' ) || str_contains( $rl, 'diagnostic' ) || str_contains( $rl, 'technical' ) || str_contains( $rl, 'automatic' ) ) {
                            $bg  = '#eff6ff';
							$col = '#1e40af';
							$bdr = '#3b82f6';
                        } else {
                            $bg  = '#faf5ff';
							$col = '#6b21a8';
							$bdr = '#a855f7';
                        }
						?>
                    <div style="border:1px solid #e0e0e0;border-radius:6px;padding:14px 16px;margin-bottom:10px">
                        <div style="display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px">
                            <strong style="font-size:13px;color:#111;line-height:1.3"><?php echo esc_html( $item['name'] ); ?></strong>
                            <span style="flex-shrink:0;display:inline-block;background:<?php echo esc_attr( $bg ); ?>;color:<?php echo esc_attr( $col ); ?>;border:1px solid <?php echo esc_attr( $bdr ); ?>;border-radius:4px;font-size:10px;font-weight:700;padding:2px 8px;white-space:nowrap;letter-spacing:0.02em;text-transform:uppercase"><?php echo esc_html( $rec ); ?></span>
                        </div>
                        <div class="cs-explain-desc">
                            <?php
                            if ( ! empty( $item['html'] ) ) {
                                echo wp_kses( $item['html'], self::$explain_kses ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- sanitised via wp_kses with restricted allowlist
                            } else {
                                echo esc_html( $item['desc'] ?? '' );
                            }
                            ?>
                        </div>
                    </div>
                    <?php endforeach; ?>
                </div>
                <div style="padding:10px 22px 16px;border-top:1px solid #e5e7eb;text-align:right">
                    <button type="button"
                        data-cs-modal-close="<?php echo esc_attr( $modal_id ); ?>"
                        style="background:#f3f4f6;border:1px solid #d1d5db;border-radius:5px;padding:6px 18px;font-size:12px;font-weight:600;cursor:pointer;color:#374151">
                        <?php esc_html_e( 'Got it', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                </div>
            </div>
        </div>
        <?php
    }

    private static function render_settings_panel() {
        $theme        = get_option( 'csdt_devtools_code_default_theme', 'dark' );
        $pair_slug    = get_option( 'csdt_devtools_code_theme_pair', 'atom-one' );
        $show_powered = get_option( 'csdt_show_powered_by', '0' ) === '1';
        $registry     = self::get_theme_registry();
        ?>
        <div class="cs-panel" id="cs-panel-code-settings">
            <div class="cs-section-header cs-section-header-teal">
                <span>🎨 CODE BLOCK SETTINGS</span>
                <?php
                self::render_explain_btn(
                    'code-settings',
                    'Code Block Settings',
                    array(
						array(
							'name' => 'Theme Pair',
							'rec'  => 'Recommended',
							'desc' => 'Choose a light/dark colour-scheme pair for syntax-highlighted code blocks. The pair is applied automatically based on the visitor\'s OS colour preference.',
						),
						array(
							'name' => 'Default Mode',
							'rec'  => 'Optional',
							'desc' => 'Force all code blocks to always use light or dark mode, ignoring the visitor\'s system preference. Leave unset to follow the OS setting.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 16px;"><?php esc_html_e( 'Customise the appearance of code blocks across your site, choose a syntax highlighting theme and set the default display mode for visitors.', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-settings-pair"><?php esc_html_e( 'Color Theme:', 'cloudscale-cyber-devtools' ); ?></label>
                        <select id="cs-settings-pair" name="csdt_devtools_code_theme_pair" class="cs-input">
                            <?php foreach ( $registry as $slug => $info ) : ?>
                                <option value="<?php echo esc_attr( $slug ); ?>" <?php selected( $pair_slug, $slug ); ?>>
                                    <?php echo esc_html( $info['label'] ); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                        <span class="cs-hint"><?php esc_html_e( 'Syntax highlighting color scheme loaded from CDN.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                    <div class="cs-field">
                        <label class="cs-label" for="cs-settings-theme"><?php esc_html_e( 'Default Mode:', 'cloudscale-cyber-devtools' ); ?></label>
                        <select id="cs-settings-theme" name="csdt_devtools_code_default_theme" class="cs-input">
                            <option value="dark" <?php selected( $theme, 'dark' ); ?>><?php esc_html_e( 'Dark', 'cloudscale-cyber-devtools' ); ?></option>
                            <option value="light" <?php selected( $theme, 'light' ); ?>><?php esc_html_e( 'Light', 'cloudscale-cyber-devtools' ); ?></option>
                        </select>
                        <span class="cs-hint"><?php esc_html_e( 'Visitors can still toggle per block.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
                <div class="cs-field-row" style="margin-top:14px;">
                    <label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
                        <input type="checkbox" id="cs-settings-powered-by" name="csdt_show_powered_by" value="1" <?php checked( $show_powered ); ?>>
                        <span style="font-size:13px;color:#1d2327;"><?php esc_html_e( 'Show "Powered by CloudScale" credit link on code blocks', 'cloudscale-cyber-devtools' ); ?></span>
                    </label>
                    <span class="cs-hint"><?php esc_html_e( 'Optional. When checked, a small attribution link appears on each code block.', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
<div style="margin-top:14px;display:flex;align-items:center;gap:10px">
                    <button type="button" class="cs-btn-primary" id="cs-settings-save">💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                    <span class="cs-settings-saved" id="cs-settings-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
            </div>
        </div>
        <?php
    }

    /*
    ==================================================================
        5b. Migrate panel
        ================================================================== */

    /**
     * Renders the Code Block Migrator panel.
     *
     * @since  1.5.0
     * @return void
     */
    /*
    ==================================================================
        Server Logs tab
    ================================================================== */

    public static function get_log_sources(): array {
        $sources = array();

        // PHP error log, prefer path set by our mu-plugin; fall back to php.ini if it's a real file
        $php_log = get_option( 'csdt_php_error_log_path', '' );
        if ( ! $php_log ) {
            $ini_log = ini_get( 'error_log' );
            if ( $ini_log && is_file( $ini_log ) ) {
                $php_log = $ini_log;
            }
        }
        if ( $php_log ) {
            $sources['php_error'] = array(
				'label' => 'PHP Error Log',
				'path'  => $php_log,
			);
        }

        // WordPress debug.log, prefer relocated path set by quick-fix mu-plugin
        $relocated           = get_option( 'csdt_debug_log_path', '' );
        $wp_debug_log        = $relocated ?: WP_CONTENT_DIR . '/debug.log';
        $sources['wp_debug'] = array(
			'label' => 'WordPress Debug Log',
			'path'  => $wp_debug_log,
		);

        // Web server error log, check common paths that may be readable by the web user
        $web_error_candidates = array(
            '/var/log/apache2/error.log',
            '/var/log/httpd/error_log',
            '/var/log/nginx/error.log',
            '/var/log/apache2/error_log',
        );
        foreach ( $web_error_candidates as $path ) {
            if ( is_readable( $path ) ) {
                $sources['web_error'] = array(
					'label' => 'Web Server Error Log',
					'path'  => $path,
				);
                break;
            }
        }

        // Web server access log
        $web_access_candidates = array(
            '/var/log/apache2/access.log',
            '/var/log/httpd/access_log',
            '/var/log/nginx/access.log',
        );
        foreach ( $web_access_candidates as $path ) {
            if ( is_readable( $path ) ) {
                $sources['web_access'] = array(
					'label' => 'Web Server Access Log',
					'path'  => $path,
				);
                break;
            }
        }

        // SSH auth log, readable if www-data is in the adm group
        $auth_log_candidates = array(
            '/var/log/auth.log',   // Debian/Ubuntu
            '/var/log/secure',     // RHEL/CentOS/Fedora
            '/var/log/messages',   // some RHEL variants
        );
        foreach ( $auth_log_candidates as $path ) {
            if ( is_readable( $path ) ) {
                $sources['auth_ssh'] = array(
					'label' => 'SSH Auth Log',
					'path'  => $path,
				);
                break;
            }
        }

        // WP Cron log
        $cron_log = WP_CONTENT_DIR . '/cron.log';
        if ( file_exists( $cron_log ) ) {
            $sources['wp_cron'] = array(
				'label' => 'WP Cron Log',
				'path'  => $cron_log,
			);
        }

        // Admin-configured custom paths
        $custom = get_option( 'csdt_custom_log_paths', array() );
        if ( is_array( $custom ) ) {
            foreach ( $custom as $i => $cp ) {
                if ( ! empty( $cp['label'] ) && ! empty( $cp['path'] ) ) {
                    $sources[ 'custom_' . $i ] = array(
                        'label'  => sanitize_text_field( $cp['label'] ),
                        'path'   => $cp['path'],
                        'custom' => true,
                    );
                }
            }
        }

        return $sources;
    }

    public static function ajax_logs_setup_php(): void {
        check_ajax_referer( self::LOGS_NONCE, 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        try {
            $log_path = WP_CONTENT_DIR . '/php-error.log';

            // Store the path; CloudScale_DevTools::init() relocates the PHP error log to it
            // at runtime via ini_set(). No must-use plugin is written (a WordPress.org
            // plugin may not deploy executable code into wp-content/mu-plugins).
            update_option( 'csdt_php_error_log_path', $log_path, false );
            wp_send_json_success(
                array(
					'path'    => $log_path,
					'sources' => self::get_log_sources(),
                )
            );
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }

    public static function ajax_logs_fix_mu_perms(): void {
        check_ajax_referer( self::LOGS_NONCE, 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        try {
            $mu_dir = WPMU_PLUGIN_DIR;
            if ( ! is_dir( $mu_dir ) ) {
                wp_mkdir_p( $mu_dir );
            }

            $uid = posix_getuid();
            if ( $uid !== 0 ) {
                wp_send_json_error( array( 'message' => __( 'PHP is not running as root, cannot chown automatically. Run manually: docker exec pi_wordpress chown www-data:www-data /var/www/html/wp-content/mu-plugins', 'cloudscale-cyber-devtools' ) ) );
                return;
            }

            // phpcs:ignore WordPress.WP.AlternativeFunctions
            $ok = @chown( $mu_dir, 'www-data' ) && @chgrp( $mu_dir, 'www-data' );
            if ( ! $ok ) {
                wp_send_json_error( array( 'message' => __( 'chown failed. Run manually: docker exec pi_wordpress chown www-data:www-data /var/www/html/wp-content/mu-plugins', 'cloudscale-cyber-devtools' ) ) );
                return;
            }

            wp_send_json_success( array( 'message' => __( 'Permissions fixed. mu-plugins is now writable.', 'cloudscale-cyber-devtools' ) ) );
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }

    public static function ajax_logs_custom_save(): void {
        check_ajax_referer( self::LOGS_NONCE, 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        try {
            $raw   = isset( $_POST['paths'] ) ? wp_unslash( $_POST['paths'] ) : '[]'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
            $paths = json_decode( $raw, true );
            if ( ! is_array( $paths ) ) {
                $paths = array();
            }

            $clean = array();
            foreach ( $paths as $p ) {
                $label = sanitize_text_field( $p['label'] ?? '' );
                $path  = sanitize_text_field( $p['path'] ?? '' );
                if ( $label !== '' && $path !== '' ) {
                    $clean[] = array(
						'label' => $label,
						'path'  => $path,
					);
                }
            }

            update_option( 'csdt_custom_log_paths', $clean );
            wp_send_json_success( array( 'sources' => self::get_log_sources() ) );
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }

    private static function render_debug_panel(): void {
        $has_key = CSDT_AI_Dispatcher::has_key();
        $key_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=security' );
        $perf_on = get_option( 'csdt_devtools_perf_monitor_enabled', '1' ) !== '0';
        ?>

        <!-- ── CS Monitor toggle ── -->
        <div class="cs-panel" id="cs-panel-cs-monitor" style="margin-bottom:12px;">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#0f4c75 0%,#1b6ca8 100%);border-left:3px solid #38bdf8;">
                <span>⚡ <?php esc_html_e( 'CS Monitor', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Frontend performance overlay panel', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'cs-monitor',
                    'CS Monitor',
                    array(
						array(
							'name' => 'What is CS Monitor?',
							'rec'  => 'Info',
							'html' => 'CS Monitor is a floating overlay panel visible only to admins. It appears on every page of your site and tracks DB queries, HTTP requests, PHP errors, and hook counts in real time, without affecting other users.',
						),
						array(
							'name' => 'N+1 DB query detection',
							'rec'  => 'Info',
							'html' => 'CS Monitor groups SQL queries by normalised pattern and flags any pattern that fires 3 or more times on a single page load as a potential N+1 issue. WP core patterns (options, post counts, site icon) are downgraded to info, they are eliminated by installing the persistent object cache below.',
						),
						array(
							'name' => 'When to disable',
							'rec'  => 'Recommended',
							'html' => 'Leave CS Monitor <strong>on during development and debugging</strong>. Turn it off on production pages that are publicly visited or performance-benchmarked, as the overlay JS adds a small overhead and its query tracking touches the same request lifecycle you are measuring.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body" style="padding:14px 20px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;">
                <label style="display:flex;align-items:center;gap:10px;cursor:pointer;flex:1;min-width:200px;">
                    <input type="checkbox" id="cs-perf-monitor-toggle" <?php checked( $perf_on ); ?>>
                    <span style="font-size:13px;color:#1d2327;"><?php esc_html_e( 'Show the ⚡ CS Monitor performance panel on all pages', 'cloudscale-cyber-devtools' ); ?></span>
                </label>
                <span class="cs-hint" style="flex:2;min-width:200px;"><?php esc_html_e( 'Visible to admins only. Tracks DB queries, HTTP requests, PHP errors, and hook counts. Disable in production when not debugging.', 'cloudscale-cyber-devtools' ); ?></span>
                <div style="display:flex;align-items:center;gap:10px;">
                    <button type="button" id="cs-perf-monitor-save" class="cs-btn-primary cs-btn-sm">💾 <?php esc_html_e( 'Save', 'cloudscale-cyber-devtools' ); ?></button>
                    <span id="cs-perf-monitor-saved" class="cs-settings-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
            </div>
        </div>

        <!-- ── Object Cache ── -->
        <?php
        $oc       = CSDT_Code_Migrator::get_object_cache_status();
        $oc_type  = $oc['type'];
        $oc_label = $oc['label'];
        $oc_apcu  = $oc['apcu_ok'];
        $oc_color = match ( $oc_type ) {
            'ours'      => $oc_apcu ? '#16a34a' : '#d97706',
            'redis',
            'memcached',
            'other'     => '#7c3aed',
            default     => '#64748b',
        };
        $oc_dot_color = $oc_type === 'ours' && $oc_apcu ? '#16a34a' : ( $oc_type === 'none' ? '#94a3b8' : '#7c3aed' );
        ?>
        <div class="cs-panel" id="cs-panel-object-cache" style="margin-bottom:12px;">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#134e4a 0%,#0f766e 100%);border-left:3px solid #2dd4bf;">
                <span>🗄️ <?php esc_html_e( 'Object Cache', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Persistent in-memory cache, eliminates WP core N+1 DB patterns', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'object-cache',
                    'Object Cache',
                    array(
						array(
							'name' => 'What does Install do?',
							'rec'  => 'Info',
							'html' => 'Clicking <strong>Install APCu cache</strong> copies a WordPress object cache drop-in to <code>wp-content/object-cache.php</code>. WordPress automatically loads this file on every request and uses it to store database results (options, users, post counts) in PHP shared memory (APCu) between requests, eliminating repeated identical DB queries across page loads.',
						),
						array(
							'name' => 'APCu requirement',
							'rec'  => 'Required',
							'html' => 'APCu is a PHP extension that provides shared memory within the PHP-FPM process pool. If the extension is not loaded, the drop-in falls back to in-request-only caching (no persistence between requests). To install APCu in the Docker container, run: <code>bash wordpress-cyber-devtools/install-object-cache.sh</code> from the repo root. This installs the extension, restarts the container, and copies the drop-in in one step.',
						),
						array(
							'name' => 'WP core N+1 patterns',
							'rec'  => 'Info',
							'html' => 'WordPress core makes repeated DB calls for options, user data, post counts, and the site icon on every page load. These cannot be fixed in plugin code, they are inside WP core itself. A persistent object cache is the correct solution: WordPress checks the cache before hitting the DB, so the same record is only fetched once per cache lifetime.',
						),
						array(
							'name' => 'Redis / Memcached',
							'rec'  => 'Info',
							'html' => 'If your site already has Redis or Memcached installed, their drop-in will be detected automatically and this panel will show its status instead of the Install button. APCu is a simpler alternative, no external service, no TCP overhead, works entirely within PHP shared memory.',
						),
						array(
							'name' => 'Removing the drop-in',
							'rec'  => 'Info',
							'html' => 'The <strong>Remove</strong> button deletes <code>wp-content/object-cache.php</code> only when it is our own drop-in. Third-party drop-ins (Redis, Memcached) must be removed manually to prevent accidental data loss.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body" style="padding:14px 20px;">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 14px;"><?php esc_html_e( 'APCu object cache stores frequently-read database results in PHP shared memory, reducing database round-trips on every page request. Requires the APCu PHP extension to be installed on your server.', 'cloudscale-cyber-devtools' ); ?></p>
                <div style="display:flex;align-items:center;gap:16px;flex-wrap:wrap;">
                <!-- Status badge -->
                <div style="display:flex;align-items:center;gap:8px;flex:1;min-width:220px;">
                    <span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:<?php echo esc_attr( $oc_dot_color ); ?>;flex-shrink:0;"></span>
                    <span style="font-size:13px;font-weight:600;color:#1d2327;">
                        <?php esc_html_e( 'Status:', 'cloudscale-cyber-devtools' ); ?>
                        <span style="color:<?php echo esc_attr( $oc_color ); ?>;"><?php echo esc_html( $oc_label ); ?></span>
                        <?php if ( $oc_type === 'ours' && ! $oc_apcu ) : ?>
                            <span style="color:#d97706;font-size:11px;">, APCu extension not loaded</span>
                        <?php elseif ( $oc_type === 'ours' ) : ?>
                            <span style="color:#16a34a;font-size:11px;"> ✓ Active</span>
                        <?php endif; ?>
                    </span>
                </div>

                <!-- APCu availability -->
                <span class="cs-hint" style="flex:2;min-width:200px;">
                    <?php if ( $oc_apcu ) : ?>
                        <?php esc_html_e( 'APCu extension: loaded ✓, shared PHP memory available.', 'cloudscale-cyber-devtools' ); ?>
                    <?php else : ?>
                        <?php esc_html_e( 'APCu extension: not loaded. Install php-apcu in the container to enable.', 'cloudscale-cyber-devtools' ); ?>
                    <?php endif; ?>
                </span>

                <!-- Action buttons -->
                <div style="display:flex;align-items:center;gap:10px;flex-shrink:0;">
                    <?php if ( $oc_type === 'none' ) : ?>
                        <button type="button" id="csdt-obj-cache-btn" data-action="install"
                                class="cs-btn-primary cs-btn-sm"
                                <?php echo ! $oc_apcu ? 'title="' . esc_attr__( 'APCu is not loaded, the drop-in will install but caching will be in-memory only until APCu is enabled', 'cloudscale-cyber-devtools' ) . '"' : ''; ?>>
                            ⬇ <?php esc_html_e( 'Install APCu cache', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                    <?php elseif ( $oc_type === 'ours' && ! $oc_apcu ) : ?>
                        <button type="button" id="csdt-apcu-install-btn"
                                class="cs-btn-primary cs-btn-sm">
                            ⚙ <?php esc_html_e( 'Install APCu Extension', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                        <button type="button" id="csdt-obj-cache-btn" data-action="uninstall"
                                class="cs-btn-sm" style="background:#fee2e2;color:#dc2626;border:1px solid #fca5a5;border-radius:4px;padding:5px 10px;cursor:pointer;font-size:12px;font-weight:600;">
                            ✕ <?php esc_html_e( 'Remove', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                    <?php elseif ( $oc_type === 'ours' ) : ?>
                        <button type="button" id="csdt-obj-cache-btn" data-action="uninstall"
                                class="cs-btn-sm" style="background:#fee2e2;color:#dc2626;border:1px solid #fca5a5;border-radius:4px;padding:5px 10px;cursor:pointer;font-size:12px;font-weight:600;">
                            ✕ <?php esc_html_e( 'Remove', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                    <?php elseif ( in_array( $oc_type, array( 'redis', 'memcached', 'other' ), true ) ) : ?>
                        <span style="font-size:12px;color:#7c3aed;font-weight:600;">
                            <?php
                            if ( $oc_type === 'redis' ) {
                                esc_html_e( 'Redis is handling object caching, no action needed.', 'cloudscale-cyber-devtools' );
                            } elseif ( $oc_type === 'memcached' ) {
                                esc_html_e( 'Memcached is handling object caching, no action needed.', 'cloudscale-cyber-devtools' );
                            } else {
                                esc_html_e( 'Third-party drop-in detected, remove manually to switch.', 'cloudscale-cyber-devtools' );
                            }
                            ?>
                        </span>
                    <?php endif; ?>
                    <span id="csdt-obj-cache-msg" style="font-size:12px;"></span>
                </div>
                </div><!-- /flex row -->
            </div>
        </div>

        <div class="cs-panel" id="cs-panel-debug">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#1e3a5f 0%,#1d4ed8 100%);border-left:3px solid #60a5fa;">
                <span>🔔 <?php esc_html_e( 'Error Monitoring', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'PHP error alerting and PHP-FPM saturation monitoring', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'error-monitoring',
                    'Error Monitoring',
                    array(
						array(
							'name' => 'PHP Error Alerting',
							'rec'  => 'Info',
							'html' => 'Monitors the PHP error log inside the Docker container for errors at or above the configured threshold (Notice / Warning / Fatal). When a new matching error appears it sends an email alert to the WordPress admin address. The log is polled on a WP-Cron schedule, no persistent daemon required.',
						),
						array(
							'name' => 'PHP-FPM Saturation Monitor',
							'rec'  => 'Info',
							'html' => 'Tracks PHP-FPM worker pool utilisation by reading <code>php-fpm status</code> inside the container. When active workers approach the pool maximum (configurable threshold), an alert is sent before the site starts queuing requests. High saturation causes slow page loads even when DB and server CPU are healthy.',
						),
						array(
							'name' => 'Alert threshold',
							'rec'  => 'Recommended',
							'html' => 'Set the PHP error threshold to <strong>Warning</strong> for production sites. <em>Notice</em> produces too much noise from WP core and plugins. <em>Fatal</em> is too late, by the time a fatal fires, users are already seeing errors. Warning catches deprecation chains and resource issues before they escalate.',
						),
					)
                );
                ?>
            </div>
            <div style="padding:24px;">
                <!-- PHP Error Alerting settings -->
                <?php
                $mon_enabled   = get_option( 'csdt_php_error_monitor_enabled', '1' ) === '1';
                $mon_threshold = (int) get_option( 'csdt_php_error_monitor_threshold', '1' );
                $last_trigger  = get_option( 'csdt_php_error_monitor_last_trigger', null );
                $tg_set        = CloudScale_Telegram::is_configured();
                $last_pos      = get_option( 'csdt_php_error_last_pos', array() );
                ?>
                <div class="cs-panel" style="margin:0;">
                    <div class="cs-section-header" style="background:linear-gradient(90deg,#92400e 0%,#b45309 100%);border-left:3px solid #fcd34d;">
                        <span>🔔 <?php esc_html_e( 'PHP Error Alerting', 'cloudscale-cyber-devtools' ); ?></span>
                        <span class="cs-header-hint"><?php esc_html_e( 'Polls PHP + WP debug logs every 5 min, alerts via email and Telegram when new fatals appear', 'cloudscale-cyber-devtools' ); ?></span>
                        <?php
                        self::render_explain_btn(
                            'php-error-alerting',
                            'PHP Error Alerting',
                            array(
								array(
									'name' => 'How it works',
									'rec'  => 'Info',
									'html' => 'A WP-Cron job runs every 5 minutes. It tracks the last byte-offset read for each log file and only reads <em>new</em> lines since the previous check, you are only alerted about errors that just appeared, not the full log history.',
								),
								array(
									'name' => 'Alert channels',
									'rec'  => 'Recommended',
									'html' => '<strong>Email</strong>, sent to the WordPress admin email address. <strong>Telegram</strong>, instant push notification to your phone. Configure your bot token and chat ID under <strong>Security Scan &rarr; Scheduled Scans &rarr; Notification Settings</strong>. Both channels fire independently.',
								),
								array(
									'name' => 'Threshold',
									'rec'  => 'Info',
									'html' => 'Minimum new errors per 5-minute check before an alert fires. Set to <code>1</code> to be notified about every error. <strong>PHP Fatal</strong> and <strong>WordPress die()</strong> always trigger an alert regardless of threshold.',
								),
								array(
									'name' => 'Log paths',
									'rec'  => 'Recommended',
									'html' => 'Watches the same log sources configured in the Server Logs panel. If PHP is logging to <code>/dev/stderr</code> (default in many Docker setups), errors are not readable here. Enable the mu-plugin under <strong>Server Logs → PHP Error Log</strong> to redirect errors to <code>wp-content/php-error.log</code>.',
								),
							)
                        );
                        ?>
                        <div style="display:flex;align-items:center;gap:10px;margin-left:auto;">
                            <label style="display:flex;align-items:center;gap:6px;cursor:pointer;">
                                <input type="checkbox" id="csdt-errmon-enabled" <?php checked( $mon_enabled ); ?>>
                                <span style="font-size:.82em;color:rgba(255,255,255,.8);"><?php esc_html_e( 'Enabled', 'cloudscale-cyber-devtools' ); ?></span>
                            </label>
                            <button type="button" id="csdt-errmon-save" class="cs-btn-sm cs-btn-primary"><?php esc_html_e( 'Save', 'cloudscale-cyber-devtools' ); ?></button>
                            <span id="csdt-errmon-status" style="font-size:.82em;color:rgba(255,255,255,.8);"></span>
                        </div>
                    </div>
                    <div class="cs-panel-body" style="display:flex;flex-direction:column;gap:8px;font-size:.82em;color:#64748b;">
                        <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                            <span style="white-space:nowrap;"><?php esc_html_e( 'Alert after', 'cloudscale-cyber-devtools' ); ?></span>
                            <input type="number" id="csdt-errmon-threshold" min="1" max="50" value="<?php echo esc_attr( $mon_threshold ); ?>" style="width:52px;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:2px 6px;font-size:1em;text-align:center;">
                            <span style="white-space:nowrap;"><?php esc_html_e( 'new error(s) per check', 'cloudscale-cyber-devtools' ); ?></span>
                            <span style="color:#94a3b8;font-size:.9em;"><?php esc_html_e( '(fatals always alert)', 'cloudscale-cyber-devtools' ); ?></span>
                        </div>
                        <?php if ( ! $tg_set ) : ?>
                            <span style="color:#f59e0b;">
                                <?php
                                printf(
                                    wp_kses(
                                        /* translators: %s: link to site audit settings */
                                        __( '&#9888; Telegram not configured, <a href="%s" style="color:#f59e0b;">set up in Security Scan &rarr; Scheduled Scans</a>', 'cloudscale-cyber-devtools' ),
                                        array(
											'a' => array(
												'href'  => array(),
												'style' => array(),
											),
                                        )
                                    ),
                                    esc_url( admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=security' ) )
                                );
                                ?>
                            </span>
                        <?php endif; ?>
                        <?php if ( $last_trigger ) : ?>
                            <span>
                                <?php
                                printf(
                                    /* translators: 1: human time diff, 2: fatal count, 3: error count */
                                    esc_html__( 'Last alert: %1$s ago (%2$d fatal, %3$d error)', 'cloudscale-cyber-devtools' ),
                                    esc_html( human_time_diff( (int) $last_trigger['ts'] ) ),
                                    (int) $last_trigger['fatal'],
                                    (int) $last_trigger['errors']
                                );
                                ?>
                            </span>
                        <?php elseif ( $mon_enabled && ! empty( $last_pos ) ) : ?>
                            <span style="color:#86efac;"><?php esc_html_e( 'Monitoring, no new errors detected', 'cloudscale-cyber-devtools' ); ?></span>
                        <?php elseif ( $mon_enabled ) : ?>
                            <span style="color:#94a3b8;"><?php esc_html_e( 'Will begin monitoring on next cron run (within 5 min)', 'cloudscale-cyber-devtools' ); ?></span>
                        <?php endif; ?>
                    </div>
                </div>

                <hr style="border:none;border-top:1px solid #e2e8f0;margin:28px 0;">

                <!-- PHP-FPM Saturation Monitor -->
                <?php
                $fpm_enabled          = get_option( 'csdt_fpm_enabled', '1' ) === '1';
                $fpm_threshold        = (int) get_option( 'csdt_fpm_threshold', '3' );
                $fpm_cooldown         = (int) get_option( 'csdt_fpm_cooldown', '1800' );
                $fpm_probe_url        = get_option( 'csdt_fpm_probe_url', 'http://localhost:8082/' );
                $fpm_timeout          = (int) get_option( 'csdt_fpm_probe_timeout', '5' );
                $fpm_wp_ctr           = get_option( 'csdt_fpm_wp_container', 'pi_wordpress' );
                $fpm_db_ctr           = get_option( 'csdt_fpm_db_container', 'pi_mariadb' );
                $fpm_auto_restart     = get_option( 'csdt_fpm_auto_restart', '0' ) === '1';
                $fpm_restart_cooldown = (int) get_option( 'csdt_fpm_restart_cooldown', '1200' );
                $fpm_token            = get_option( 'csdt_fpm_token', '' );
                if ( empty( $fpm_token ) ) {
                    $fpm_token = wp_generate_password( 32, false );
                    update_option( 'csdt_fpm_token', $fpm_token, false );
                }
                $fpm_last             = get_option( 'csdt_fpm_last_event', null );
                $fpm_event_log        = is_array( get_option( 'csdt_fpm_event_log', array() ) ) ? get_option( 'csdt_fpm_event_log', array() ) : array();
                $fpm_report_url       = admin_url( 'admin-ajax.php' );
                $fpm_auto_restart_val = $fpm_auto_restart ? 'true' : 'false';
                $fpm_config_snippet   = "# ── PHP-FPM Saturation Monitor ─────────────────────────────────────────────\n"
                    . "FPM_SATURATION_THRESHOLD={$fpm_threshold}\n"
                    . "FPM_PROBE_URL={$fpm_probe_url}\n"
                    . "FPM_PROBE_TIMEOUT={$fpm_timeout}\n"
                    . "FPM_WP_CONTAINER={$fpm_wp_ctr}\n"
                    . "FPM_DB_CONTAINER={$fpm_db_ctr}\n"
                    . "FPM_ALERT_COOLDOWN={$fpm_cooldown}\n"
                    . "FPM_AUTO_RESTART={$fpm_auto_restart_val}\n"
                    . "FPM_RESTART_COOLDOWN={$fpm_restart_cooldown}\n"
                    . "FPM_CALLBACK_URL={$fpm_report_url}\n"
                    . "FPM_CALLBACK_TOKEN={$fpm_token}";
                ?>
                <div class="cs-panel" style="margin:0;">
                    <div class="cs-section-header" style="background:linear-gradient(90deg,#3730a3 0%,#6366f1 100%);border-left:3px solid #a5b4fc;">
                        <span>🖥️ <?php esc_html_e( 'PHP-FPM Saturation Monitor', 'cloudscale-cyber-devtools' ); ?></span>
                        <span style="display:inline-flex;align-items:center;padding:1px 8px;background:rgba(255,255,255,.15);border-radius:10px;font-size:.72em;color:#e0e7ff;margin-left:4px;">HOST CRON</span>
                        <span class="cs-header-hint"><?php esc_html_e( 'Detects when all PHP-FPM workers are exhausted. Runs on the host (not WP-Cron), so it fires even when PHP is fully saturated.', 'cloudscale-cyber-devtools' ); ?></span>
                        <?php
                        self::render_explain_btn(
                            'fpm_monitor',
                            'PHP-FPM Saturation Monitor',
                            array(
								array(
									'name' => 'What is PHP-FPM saturation?',
									'rec'  => 'Info',
									'html' => 'PHP-FPM (FastCGI Process Manager) maintains a pool of worker processes that handle requests. When all workers are busy (e.g. a traffic spike, a slow DB query holding workers open, or a runaway loop), new requests queue up and the site appears frozen or times out. This is called saturation.',
								),
								array(
									'name' => 'Why a host cron, not WP-Cron?',
									'rec'  => 'Critical',
									'html' => 'WP-Cron runs inside PHP-FPM. If PHP-FPM is fully saturated, WP-Cron can\'t execute, so a WordPress-based monitor would be silenced exactly when you need it most. This monitor runs as a shell script on the host OS (outside Docker), so it fires even when every PHP worker is consumed.',
								),
								array(
									'name' => 'How the detection works',
									'rec'  => 'Info',
									'html' => 'Every minute the script probes the HTTP URL. If the probe times out or fails N consecutive times (the threshold), saturation is declared. It then sends a Telegram notification and email alert, optionally restarts the WordPress container, and POSTs an event to this panel via the callback URL.',
								),
								array(
									'name' => 'Current Workers display',
									'rec'  => 'Info',
									'html' => 'Shows live active / idle / total worker counts from the PHP-FPM status page (<code>pm.status_path</code>). Requires <code>pm.status_path = /fpm-status</code> in your <code>www.conf</code> and a matching nginx location block. Click Refresh at any time to re-poll.',
								),
								array(
									'name' => 'Auto-restart',
									'rec'  => 'Optional',
									'html' => 'When enabled, the script issues a <code>docker restart {container}</code> command after declaring saturation. A restart cooldown prevents thrashing. Use with care on production, a restart drops all in-flight requests.',
								),
								array(
									'name' => 'Setup',
									'rec'  => 'Info',
									'html' => 'Copy the crontab line and config.env snippet from the Host Cron Setup section below. The callback URL and token wire the script back to this panel so saturation events appear in the audit trail automatically.',
								),
							),
							'Monitors PHP-FPM worker exhaustion from the host OS. Alerts via Telegram + email, can auto-restart the container, and logs events back to this panel.'
                        );
                        ?>
                        <div style="display:flex;align-items:center;gap:10px;margin-left:auto;">
                            <label style="display:flex;align-items:center;gap:6px;cursor:pointer;">
                                <input type="checkbox" id="csdt-fpm-enabled" <?php checked( $fpm_enabled ); ?>>
                                <span style="font-size:.82em;color:rgba(255,255,255,.8);"><?php esc_html_e( 'Enabled', 'cloudscale-cyber-devtools' ); ?></span>
                            </label>
                            <button type="button" id="csdt-fpm-save" class="cs-btn-sm cs-btn-primary"><?php esc_html_e( 'Save', 'cloudscale-cyber-devtools' ); ?></button>
                            <span id="csdt-fpm-status" style="font-size:.82em;color:rgba(255,255,255,.8);"></span>
                        </div>
                    </div>
                    <div class="cs-panel-body">

                    <!-- Workers live status -->
                    <div style="background:#f1f5f9;border:1px solid #e2e8f0;border-radius:6px;padding:10px 14px;margin-bottom:14px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;">
                        <span style="font-size:.8em;color:#64748b;font-weight:600;"><?php esc_html_e( 'Current workers', 'cloudscale-cyber-devtools' ); ?></span>
                        <span id="csdt-fpm-workers-active" style="font-size:.82em;color:#1e293b;">
                            <span style="color:#64748b;"><?php esc_html_e( 'Active:', 'cloudscale-cyber-devtools' ); ?></span>
                            <span id="csdt-fpm-w-active" style="color:#dc2626;font-weight:700;">&middot;</span>
                        </span>
                        <span style="font-size:.82em;color:#1e293b;">
                            <span style="color:#64748b;"><?php esc_html_e( 'Idle:', 'cloudscale-cyber-devtools' ); ?></span>
                            <span id="csdt-fpm-w-idle" style="color:#16a34a;font-weight:700;">&middot;</span>
                        </span>
                        <span style="font-size:.82em;color:#1e293b;">
                            <span style="color:#64748b;"><?php esc_html_e( 'Total:', 'cloudscale-cyber-devtools' ); ?></span>
                            <span id="csdt-fpm-w-total" style="color:#374151;font-weight:700;">&middot;</span>
                        </span>
                        <span style="font-size:.82em;color:#1e293b;">
                            <span style="color:#64748b;"><?php esc_html_e( 'Mem:', 'cloudscale-cyber-devtools' ); ?></span>
                            <span id="csdt-fpm-w-mem" style="color:#1e293b;font-weight:700;" title="Total memory across all workers">&middot;</span>
                        </span>
                        <button type="button" id="csdt-fpm-workers-refresh" class="cs-btn-sm cs-btn-secondary" style="padding:5px 12px;font-size:.78em;line-height:1.4;">↻ <?php esc_html_e( 'Refresh', 'cloudscale-cyber-devtools' ); ?></button>
                        <button type="button" id="csdt-fpm-detail-toggle" class="cs-btn-sm cs-btn-secondary" style="padding:5px 12px;font-size:.78em;line-height:1.4;">▼ <?php esc_html_e( 'Workers', 'cloudscale-cyber-devtools' ); ?></button>
                        <button type="button" id="csdt-fpm-setup-btn" class="cs-btn-sm cs-btn-secondary" style="padding:5px 12px;font-size:.78em;line-height:1.4;background:#dbeafe;color:#1d4ed8;border-color:#93c5fd;">⚙ <?php esc_html_e( 'Setup Status Page', 'cloudscale-cyber-devtools' ); ?></button>
                        <span id="csdt-fpm-workers-status" style="font-size:.78em;color:#64748b;"></span>
                    </div>

                    <!-- Per-worker detail table -->
                    <div id="csdt-fpm-detail-panel" style="display:none;margin-bottom:14px;">
                        <div style="overflow-x:auto;">
                            <table id="csdt-fpm-detail-table" style="width:100%;border-collapse:collapse;font-size:.76em;color:#1e293b;">
                                <thead>
                                    <tr style="border-bottom:1px solid #e2e8f0;color:#64748b;text-align:left;">
                                        <th style="padding:5px 8px;white-space:nowrap;">PID</th>
                                        <th style="padding:5px 8px;white-space:nowrap;">State</th>
                                        <th style="padding:5px 8px;white-space:nowrap;">Reqs</th>
                                        <th style="padding:5px 8px;white-space:nowrap;">Running</th>
                                        <th style="padding:5px 8px;white-space:nowrap;">Last URI</th>
                                        <th style="padding:5px 8px;white-space:nowrap;">Script</th>
                                        <th style="padding:5px 8px;white-space:nowrap;" title="CPU% used by the last completed request. Running workers show, until their current request finishes.">Last CPU%</th>
                                        <th style="padding:5px 8px;white-space:nowrap;">Mem</th>
                                    </tr>
                                </thead>
                                <tbody id="csdt-fpm-detail-tbody">
                                    <tr><td colspan="8" style="padding:8px;color:#475569;">Loading…</td></tr>
                                </tbody>
                                <tfoot id="csdt-fpm-detail-tfoot"></tfoot>
                            </table>
                        </div>
                        <p style="margin:4px 0 8px;font-size:.72em;color:#475569;">Last CPU% = CPU used by the most recently <em>completed</em> request. Idle workers show their last value; Running workers show, because their current request hasn't finished yet.</p>
                        <div id="csdt-fpm-pool-info" style="margin-top:4px;font-size:.74em;color:#94a3b8;"></div>
                    </div>

                    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px;">
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'Saturation threshold (consecutive checks)', 'cloudscale-cyber-devtools' ); ?></label>
                            <input type="number" id="csdt-fpm-threshold" min="1" max="30" value="<?php echo esc_attr( $fpm_threshold ); ?>" style="width:80px;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:3px 8px;font-size:.9em;">
                        </div>
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'Alert cooldown (seconds)', 'cloudscale-cyber-devtools' ); ?></label>
                            <input type="number" id="csdt-fpm-cooldown" min="60" max="86400" value="<?php echo esc_attr( $fpm_cooldown ); ?>" style="width:100px;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:3px 8px;font-size:.9em;">
                        </div>
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'HTTP probe URL', 'cloudscale-cyber-devtools' ); ?></label>
                            <input type="text" id="csdt-fpm-probe-url" value="<?php echo esc_attr( $fpm_probe_url ); ?>" style="width:100%;box-sizing:border-box;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:3px 8px;font-size:.9em;">
                        </div>
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'Probe timeout (seconds)', 'cloudscale-cyber-devtools' ); ?></label>
                            <input type="number" id="csdt-fpm-probe-timeout" min="1" max="30" value="<?php echo esc_attr( $fpm_timeout ); ?>" style="width:80px;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:3px 8px;font-size:.9em;">
                        </div>
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'WordPress container name', 'cloudscale-cyber-devtools' ); ?></label>
                            <input type="text" id="csdt-fpm-wp-container" value="<?php echo esc_attr( $fpm_wp_ctr ); ?>" style="width:100%;box-sizing:border-box;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:3px 8px;font-size:.9em;">
                        </div>
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'MariaDB container name', 'cloudscale-cyber-devtools' ); ?></label>
                            <input type="text" id="csdt-fpm-db-container" value="<?php echo esc_attr( $fpm_db_ctr ); ?>" style="width:100%;box-sizing:border-box;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:3px 8px;font-size:.9em;">
                        </div>
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'Auto-restart on saturation', 'cloudscale-cyber-devtools' ); ?></label>
                            <label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin-top:4px;">
                                <input type="checkbox" id="csdt-fpm-auto-restart" <?php checked( $fpm_auto_restart ); ?>>
                                <span style="font-size:.85em;color:#94a3b8;"><?php esc_html_e( 'Restart container automatically', 'cloudscale-cyber-devtools' ); ?></span>
                            </label>
                        </div>
                        <div>
                            <label style="display:block;font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'Restart cooldown (seconds)', 'cloudscale-cyber-devtools' ); ?></label>
                            <input type="number" id="csdt-fpm-restart-cooldown" min="60" max="86400" value="<?php echo esc_attr( $fpm_restart_cooldown ); ?>" style="width:100px;background:#fff;color:#1e293b;border:1px solid #d1d5db;border-radius:4px;padding:3px 8px;font-size:.9em;">
                            <span style="font-size:.75em;color:#475569;margin-left:6px;"><?php /* translators: %d is the number of minutes */ echo esc_html( sprintf( __( '%d min', 'cloudscale-cyber-devtools' ), (int) round( $fpm_restart_cooldown / 60 ) ) ); ?></span>
                        </div>
                    </div>

                    <div style="background:#f1f5f9;border:1px solid #d1d5db;border-radius:6px;padding:14px 16px;margin-bottom:16px;">
                        <div style="font-size:.82em;color:#2563eb;font-weight:600;margin-bottom:10px;">📋 <?php esc_html_e( 'Host Cron Setup', 'cloudscale-cyber-devtools' ); ?></div>
                        <div style="font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'Add to crontab on your Pi host (crontab -e):', 'cloudscale-cyber-devtools' ); ?></div>
                        <code style="display:block;background:#fff;border:1px solid #d1d5db;border-radius:4px;padding:8px 12px;font-size:.8em;color:#16a34a;white-space:nowrap;overflow-x:auto;margin-bottom:10px;">* * * * * /home/pi/pi2s3/fpm-saturation-monitor.sh 2&gt;/dev/null</code>
                        <div style="font-size:.78em;color:#64748b;margin-bottom:4px;"><?php esc_html_e( 'Add to ~/pi2s3/config.env (includes callback so last event appears above):', 'cloudscale-cyber-devtools' ); ?></div>
                        <code id="csdt-fpm-config-snippet" style="display:block;background:#fff;border:1px solid #d1d5db;border-radius:4px;padding:8px 12px;font-size:.78em;color:#374151;white-space:pre;overflow-x:auto;margin-bottom:10px;"><?php echo esc_html( $fpm_config_snippet ); ?></code>
                        <button type="button" id="csdt-fpm-copy-snippet" class="cs-btn-sm cs-btn-secondary"><?php esc_html_e( 'Copy config.env snippet', 'cloudscale-cyber-devtools' ); ?></button>
                        <span id="csdt-fpm-copy-status" style="font-size:.78em;color:#16a34a;margin-left:8px;"></span>
                    </div>

                    <!-- Event audit trail -->
                    <?php if ( ! empty( $fpm_event_log ) ) : ?>
                    <div style="margin-top:4px;">
                        <div style="font-size:.78em;color:#64748b;margin-bottom:6px;display:flex;align-items:center;justify-content:space-between;">
                            <span><?php /* translators: %d is the number of events */ printf( esc_html__( 'Last %d events (newest first)', 'cloudscale-cyber-devtools' ), count( $fpm_event_log ) ); ?></span>
                        </div>
                        <div style="max-height:240px;overflow-y:auto;border:1px solid #e2e8f0;border-radius:6px;">
                            <table style="width:100%;border-collapse:collapse;font-size:.78em;">
                                <thead>
                                    <tr style="background:#f1f5f9;position:sticky;top:0;">
                                        <th style="text-align:left;padding:5px 10px;color:#475569;font-weight:600;white-space:nowrap;"><?php esc_html_e( 'Time', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th style="text-align:left;padding:5px 10px;color:#475569;font-weight:600;"><?php esc_html_e( 'Event', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th style="text-align:left;padding:5px 10px;color:#475569;font-weight:600;"><?php esc_html_e( 'Detail', 'cloudscale-cyber-devtools' ); ?></th>
                                    </tr>
                                </thead>
                                <tbody>
                                <?php
                                foreach ( $fpm_event_log as $i => $ev ) :
                                    $ev_color = match ( $ev['type'] ?? '' ) {
                                        'recovered' => '#16a34a',
                                        'restarted' => '#d97706',
                                        default     => '#dc2626',
                                    };
                                    $ev_icon = match ( $ev['type'] ?? '' ) {
                                        'recovered' => '✓',
                                        'restarted' => '🔄',
                                        default     => '🚨',
                                    };
                                    $ev_label = match ( $ev['type'] ?? '' ) {
                                        'recovered' => __( 'Recovered', 'cloudscale-cyber-devtools' ),
                                        'restarted' => __( 'Auto-restarted', 'cloudscale-cyber-devtools' ),
                                        default     => __( 'Saturated', 'cloudscale-cyber-devtools' ),
                                    };
                                    $row_bg = $i % 2 === 0 ? '#ffffff' : '#f8fafc';
									?>
                                    <tr style="background:<?php echo esc_attr( $row_bg ); ?>;border-top:1px solid #e2e8f0;">
                                        <td style="padding:5px 10px;color:#64748b;white-space:nowrap;" title="<?php echo esc_attr( wp_date( 'Y-m-d H:i:s', (int) $ev['ts'] ) ); ?>">
                                            <?php echo esc_html( human_time_diff( (int) $ev['ts'] ) . ' ago' ); ?>
                                        </td>
                                        <td style="padding:5px 10px;white-space:nowrap;">
                                            <span style="color:<?php echo esc_attr( $ev_color ); ?>;"><?php echo esc_html( $ev_icon ) . ' ' . esc_html( $ev_label ); ?></span>
                                        </td>
                                        <td style="padding:5px 10px;color:#94a3b8;"><?php echo esc_html( $ev['msg'] ?? '' ); ?></td>
                                    </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>
                    <?php else : ?>
                    <div style="font-size:.82em;">
                        <span style="color:#475569;"><?php esc_html_e( 'No saturation events recorded yet. Install the host cron and set FPM_CALLBACK_URL + FPM_CALLBACK_TOKEN to enable the audit trail.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                    <?php endif; ?>
                </div>

                <!-- PHP-FPM Status Page Setup Modal (inline so it's always in the DOM with the button) -->
                <div id="csdt-fpm-setup-modal" style="display:none;position:fixed;inset:0;z-index:100000;background:rgba(0,0,0,.7);align-items:center;justify-content:center;">
                    <div style="background:#fff;border:1px solid #d1d5db;border-radius:10px;max-width:620px;width:94%;padding:24px;position:relative;max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,.15);">
                        <button id="csdt-fpm-setup-close" style="position:absolute;top:12px;right:14px;background:none;border:none;font-size:20px;cursor:pointer;color:#64748b;line-height:1;" title="Close">✕</button>
                        <h3 style="margin:0 0 4px;font-size:15px;font-weight:700;color:#1e293b;">⚙ PHP-FPM Status Page Setup</h3>
                        <p style="font-size:12px;color:#64748b;margin:0 0 18px;">Enables the <code style="background:#f1f5f9;padding:1px 5px;border-radius:3px;color:#16a34a;">/fpm-status</code> endpoint so the Current Workers panel shows live counts.</p>
                        <div id="csdt-fpm-setup-steps" style="display:flex;gap:0;margin-bottom:20px;">
                            <?php
                            foreach ( array(
								1 => 'Detect',
								2 => 'www.conf',
								3 => 'nginx',
							) as $n => $lbl ) :
								?>
                            <div class="csdt-fpm-step" data-step="<?php echo (int) $n; ?>" style="flex:1;text-align:center;padding:6px 0;font-size:11px;font-weight:600;border-bottom:2px solid #e2e8f0;color:#475569;"><?php echo (int) $n; ?>. <?php echo esc_html( $lbl ); ?></div>
                            <?php endforeach; ?>
                        </div>
                        <div id="csdt-fpm-step-1">
                            <p style="font-size:13px;color:#94a3b8;margin:0 0 14px;">Scans for your PHP-FPM config file and probes common URLs to find nginx.</p>
                            <button type="button" id="csdt-fpm-detect-btn" class="button button-primary" style="font-size:13px;">🔍 Run Detection</button>
                            <div id="csdt-fpm-detect-result" style="margin-top:14px;font-size:12px;"></div>
                        </div>
                        <div id="csdt-fpm-step-2" style="display:none;">
                            <div id="csdt-fpm-patch-info" style="font-size:13px;color:#94a3b8;margin-bottom:14px;"></div>
                            <button type="button" id="csdt-fpm-patch-btn" class="button button-primary" style="font-size:13px;">✏️ Patch www.conf &amp; Reload php-fpm</button>
                            <div id="csdt-fpm-patch-result" style="margin-top:14px;font-size:12px;"></div>
                            <div style="margin-top:14px;display:flex;gap:8px;">
                                <button type="button" id="csdt-fpm-step2-next" class="button" style="font-size:12px;display:none;">Next →</button>
                                <button type="button" id="csdt-fpm-step2-skip" class="button" style="font-size:12px;">Skip (already done)</button>
                            </div>
                        </div>
                        <div id="csdt-fpm-step-3" style="display:none;">
                            <p style="font-size:13px;color:#475569;margin:0 0 10px;">Add this location block inside your nginx <code style="background:#f1f5f9;padding:1px 5px;border-radius:3px;color:#16a34a;">server {}</code> block, then reload nginx.</p>
                            <pre id="csdt-fpm-nginx-snippet" style="background:#f1f5f9;border:1px solid #d1d5db;border-radius:6px;padding:12px;font-size:.78em;color:#374151;overflow-x:auto;white-space:pre;margin:0 0 10px;"></pre>
                            <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
                                <button type="button" id="csdt-fpm-copy-nginx" class="button" style="font-size:12px;">📋 Copy snippet</button>
                                <span id="csdt-fpm-copy-nginx-status" style="font-size:12px;color:#16a34a;"></span>
                            </div>
                            <p style="font-size:12px;color:#64748b;margin:12px 0 6px;">Then reload nginx:</p>
                            <code id="csdt-fpm-nginx-reload-cmd" style="display:block;background:#f1f5f9;border:1px solid #d1d5db;border-radius:4px;padding:6px 10px;font-size:.78em;color:#16a34a;"></code>
                            <div style="margin-top:14px;display:flex;gap:8px;align-items:center;">
                                <button type="button" id="csdt-fpm-test-btn" class="button button-primary" style="font-size:12px;">✅ Test &amp; Finish</button>
                                <span id="csdt-fpm-test-result" style="font-size:12px;color:#94a3b8;"></span>
                            </div>
                        </div>
                    </div>
                    </div><!-- /.cs-panel-body -->
                </div><!-- /.cs-panel -->

            </div>
        </div>

        <!-- ── Uptime Monitor ────────────────────────────────────────────── -->
        <div class="cs-panel" id="cs-panel-uptime-monitor">
            <div class="cs-section-header cs-section-header-green">
                <span>⏱ <?php esc_html_e( 'Uptime Monitor', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'WordPress pushes a heartbeat to a Cloudflare Worker every 3 minutes. No heartbeat for 8 minutes = site down alert.', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'uptime-monitor',
                    'Uptime Monitor',
                    array(
						array(
							'name' => 'Setup, step by step',
							'rec'  => 'Required',
							'html' => '<ol style="margin:0;padding-left:1.4em;line-height:1.8;"><li><strong>Telegram alerts</strong>, Configure your Telegram bot token and chat ID under <strong>Security Scan &rarr; Scheduled Scans &rarr; Notification Settings</strong>. Down and recovery alerts are sent via Telegram from the Cloudflare edge.</li><li><strong>Cloudflare credentials</strong>, Enter your Cloudflare Zone ID and an API Token with <code>Workers:Edit</code> and <code>Workers KV Storage:Edit</code> permissions, then click <em>Save Settings</em>.</li><li><strong>Deploy Worker</strong>, Click <em>Deploy Worker to Cloudflare</em>. This creates a KV namespace, uploads the Worker with your Telegram credentials bound as environment variables, and schedules the <code>* * * * *</code> cron trigger.</li><li><strong>Host cron</strong>, Run <code>deploy-cf-worker.sh</code> on your server to install a host cron that triggers WP-Cron locally (bypasses Cloudflare cache). WP-Cron pushes a heartbeat to the Worker every 3 minutes. The cron line is shown in the Host cron section below.</li><li><strong>Test</strong>, Click <em>Test Endpoint</em> to send a heartbeat to the Worker immediately and confirm the connection.</li></ol>',
						),
						array(
							'name' => 'How it works',
							'rec'  => 'Overview',
							'html' => 'A Pi host cron hits <code>http://127.0.0.1:PORT/wp-cron.php</code> (localhost, bypasses Cloudflare cache) every minute. PHP-FPM processes the request, WP-Cron runs, and WordPress pushes a small heartbeat POST to the Cloudflare Worker every 3 minutes. The Worker stores the timestamp in CF KV and its own cron checks every minute: no heartbeat for 8 minutes or more means the site is down, a Telegram alert fires. Recovery alert is sent when heartbeats resume. Localhost is required because Cloudflare caches the public wp-cron.php URL and returns a cached 200 without PHP ever executing.',
						),
						array(
							'name' => 'Down vs recovery',
							'rec'  => 'Overview',
							'html' => 'Down alerts fire after ~8 minutes of missed heartbeats. Repeat alerts are throttled to once every 30 minutes while the site remains down. Recovery fires as soon as one heartbeat arrives after a down period, with the total outage duration included in the message.',
						),
						array(
							'name' => 'Test Alert (pause 5 min)',
							'rec'  => 'Testing',
							'html' => 'Click <em>Test Alert (pause 5 min)</em> to pause heartbeats for 5 minutes. After about 8 minutes of silence the Cloudflare Worker will treat the site as down and fire a Telegram down alert. When the 5-minute pause ends, heartbeats resume automatically and you should receive a recovery alert. A live countdown is shown while paused. Click <em>Cancel Pause</em> at any time to resume heartbeats immediately (you will still get a recovery alert once the next heartbeat is sent).',
						),
						array(
							'name' => 'Alert notifications',
							'rec'  => 'Recommended',
							'html' => 'Alerts are sent via Telegram from the Cloudflare edge, so they arrive even if your PHP, database, and server are all offline. The Worker holds your bot token and chat ID as environment variable bindings, injected at deploy time via <code>deploy-cf-worker.sh</code>. To update credentials, save new values in Notification Settings and redeploy the Worker.',
						),
						array(
							'name' => 'KV storage',
							'rec'  => 'Info',
							'html' => 'A Cloudflare KV namespace (<code>csdt-uptime-state</code>) is created automatically on first deploy. It stores three small values: last heartbeat timestamp, down-since timestamp, and last-alert timestamp. The deploy script and Deploy button both handle KV creation, no manual setup needed.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <div>
                    <p style="color:#4b5563;margin:0 0 6px;line-height:1.65;font-size:.95em;">
                        <?php esc_html_e( 'A host cron job hits WordPress locally every minute (bypassing Cloudflare cache), triggering a heartbeat push to the Cloudflare Worker every 3 minutes. If no heartbeat arrives for 8 minutes, the Worker fires a down alert via Telegram. When heartbeats resume, a recovery alert is sent automatically with outage duration.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <div id="csdt-uptime-setup-wrap">
                        <div style="background:#f8fafc;border:1px solid #e5e7eb;border-radius:8px;padding:16px 20px;margin-bottom:12px;">
                            <p style="margin:0 0 12px;font-weight:700;color:#0f172a;font-size:.9em;">Cloudflare credentials <span style="font-weight:400;color:#6b7280;">(<?php esc_html_e( 'required, API Token needs Workers:Edit and Workers KV Storage:Edit permissions', 'cloudscale-cyber-devtools' ); ?>)</span></p>
                            <div style="display:flex;flex-direction:column;gap:10px;max-width:420px;">
                                <div>
                                    <label for="csdt-cf-zone-id" style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:4px;">Zone ID</label>
                                    <div style="position:relative;display:flex;align-items:center;">
                                        <?php $z = get_option( 'csdt_devtools_cf_zone_id', '' ); ?>
                                        <input id="csdt-cf-zone-id" type="text" class="cs-input" style="width:100%;padding-right:36px;"
                                                placeholder="<?php esc_attr_e( 'Cloudflare Zone ID', 'cloudscale-cyber-devtools' ); ?>"
                                                value="<?php echo esc_attr( $z ? str_repeat( '•', 16 ) . substr( $z, -4 ) : '' ); ?>"
                                                data-real="<?php echo esc_attr( $z ); ?>"
                                                data-masked="<?php echo esc_attr( $z ? str_repeat( '•', 16 ) . substr( $z, -4 ) : '' ); ?>"
                                                autocomplete="off">
                                        <button type="button" class="csdt-cf-eye-btn" data-target="csdt-cf-zone-id" title="Show / hide"
                                                style="position:absolute;right:8px;background:none;border:none;cursor:pointer;padding:0;color:#94a3b8;font-size:15px;line-height:1;">👁</button>
                                    </div>
                                </div>
                                <div>
                                    <label for="csdt-cf-api-token" style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:4px;">API Token</label>
                                    <div style="position:relative;display:flex;align-items:center;">
                                        <?php $t = get_option( 'csdt_devtools_cf_api_token', '' ); ?>
                                        <input id="csdt-cf-api-token" type="text" class="cs-input" style="width:100%;padding-right:36px;"
                                                placeholder="<?php esc_attr_e( 'Cloudflare API Token', 'cloudscale-cyber-devtools' ); ?>"
                                                value="<?php echo esc_attr( $t ? str_repeat( '•', 16 ) . substr( $t, -4 ) : '' ); ?>"
                                                data-real="<?php echo esc_attr( $t ); ?>"
                                                data-masked="<?php echo esc_attr( $t ? str_repeat( '•', 16 ) . substr( $t, -4 ) : '' ); ?>"
                                                autocomplete="new-password">
                                        <button type="button" class="csdt-cf-eye-btn" data-target="csdt-cf-api-token" title="Show / hide"
                                                style="position:absolute;right:8px;background:none;border:none;cursor:pointer;padding:0;color:#94a3b8;font-size:15px;line-height:1;">👁</button>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div style="background:#f0f9ff;border:1px solid #b3e5fc;border-radius:8px;padding:14px 20px;margin-bottom:16px;font-size:.85em;color:#0277bd;">
                            &#9992; <?php esc_html_e( 'Down/recovery alerts are sent via Telegram. Configure your bot token and chat ID in Security Scan &rarr; Scheduled Scans &rarr; Notification Settings, then redeploy the Worker.', 'cloudscale-cyber-devtools' ); ?>
                            <?php if ( ! CloudScale_Telegram::is_configured() ) : ?>
                            <span style="display:block;margin-top:6px;color:#92400e;font-weight:600;">&#9888; <?php esc_html_e( 'Telegram not configured.', 'cloudscale-cyber-devtools' ); ?></span>
                            <?php endif; ?>
                        </div>
                        <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
                            <button id="csdt-uptime-save-btn" class="cs-btn-secondary">
                                💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                            <span id="csdt-uptime-save-status" style="display:none;font-size:.88em;"></span>
                        </div>
                        <div style="margin-top:16px;padding-top:16px;border-top:1px solid #e5e7eb;display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
                            <button id="csdt-uptime-deploy-btn" class="cs-btn-primary">
                                🚀 <?php esc_html_e( 'Deploy Worker to Cloudflare', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                            <button id="csdt-uptime-test-btn" class="cs-btn-secondary">
                                🧪 <?php esc_html_e( 'Test Endpoint', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                            <span id="csdt-uptime-deploying" style="display:none;color:#6b7280;font-size:13px;">⏳ <?php esc_html_e( 'Deploying…', 'cloudscale-cyber-devtools' ); ?></span>
                        </div>
                        <div id="csdt-uptime-deploy-result" style="margin-top:12px;"></div>
                        <details style="margin-top:16px;">
                            <summary style="cursor:pointer;font-size:.85em;font-weight:600;color:#6366f1;">🛠 Manual deploy (copy-paste Worker script)</summary>
                            <div id="csdt-uptime-manual-wrap" style="margin-top:12px;"></div>
                        </details>
                        <details style="margin-top:10px;" open>
                            <summary style="cursor:pointer;font-size:.85em;font-weight:600;color:#374151;">⏱ Host cron, reliable every-3-minute heartbeats</summary>
                            <div style="margin-top:10px;background:#f1f5f9;border:1px solid #d1d5db;border-radius:6px;padding:12px 14px;">
                                <p style="margin:0 0 6px;font-size:.82em;color:#475569;"><?php esc_html_e( 'Must use localhost (not the public URL), Cloudflare caches wp-cron.php and returns a cached 200 without WordPress ever executing. Hitting nginx directly bypasses CF. Replace PORT with your nginx host port (run', 'cloudscale-cyber-devtools' ); ?> <code>docker port pi_nginx 80/tcp</code><?php esc_html_e( '):', 'cloudscale-cyber-devtools' ); ?></p>
                                <code id="csdt-uptime-cron-line" style="display:block;background:#fff;border:1px solid #d1d5db;border-radius:4px;padding:8px 12px;font-size:.8em;color:#16a34a;word-break:break-all;margin-bottom:8px;">* * * * * curl -sf -m 10 -H 'Host: <?php echo esc_html( wp_parse_url( get_site_url(), PHP_URL_HOST ) ); ?>' 'http://127.0.0.1:PORT/wp-cron.php?doing_wp_cron' -o /dev/null 2&gt;/dev/null</code>
                                <p style="margin:0;font-size:.78em;color:#64748b;"><?php esc_html_e( 'If FPM is down nginx returns 502, curl exits non-zero, WP-Cron does not run, no heartbeat is pushed, and the CF Worker alerts after 8 minutes. deploy-cf-worker.sh detects the port and installs this automatically.', 'cloudscale-cyber-devtools' ); ?></p>
                            </div>
                        </details>
                    </div>
                    <div id="csdt-uptime-status-wrap" style="display:none;margin-top:4px;">
                        <div id="csdt-uptime-status-inner"></div>
                        <div style="margin-top:12px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
                            <button id="csdt-uptime-refresh-btn" class="cs-btn-secondary" style="font-size:.82em;">↻ <?php esc_html_e( 'Push Heartbeat + Refresh', 'cloudscale-cyber-devtools' ); ?></button>
                            <button id="csdt-uptime-pause-btn" class="cs-btn-secondary" style="font-size:.82em;">🔕 <?php esc_html_e( 'Test Alert (pause 5 min)', 'cloudscale-cyber-devtools' ); ?></button>
                            <button id="csdt-uptime-cancel-pause-btn" class="cs-btn-secondary" style="font-size:.82em;display:none;">✕ <?php esc_html_e( 'Cancel Pause', 'cloudscale-cyber-devtools' ); ?></button>
                            <span id="csdt-uptime-push-status" style="display:none;font-size:.82em;"></span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <?php
        self::render_opcache_panel();
        self::render_server_logs_panel();
        self::render_sql_panel();
        CSDT_Custom_404::render_404_panel();
        self::render_settings_panel();
        // Migrator moved to its own top-level "Code Block Migrator" tab.
    }

    private static function render_opcache_panel(): void {
        $opcache_token = get_option( 'csdt_opcache_token', '' );
        if ( empty( $opcache_token ) ) {
            $opcache_token = wp_generate_password( 32, false );
            update_option( 'csdt_opcache_token', $opcache_token, false );
        }
        $last_flush = (int) get_option( 'csdt_opcache_last_flush', 0 );
        ?>
        <div class="cs-panel" id="cs-panel-opcache">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#164e63 0%,#0e7490 100%);border-left:3px solid #22d3ee;">
                <span>⚡ <?php esc_html_e( 'OPcache', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'PHP OPcache status, statistics, and flush control', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'opcache',
                    'OPcache',
                    array(
						array(
							'name' => 'What is OPcache?',
							'rec'  => 'Info',
							'html' => 'PHP OPcache compiles PHP scripts to bytecode and caches them in shared memory, so they don\'t need to be parsed and compiled on every request. This typically reduces response time by 30-50% and is enabled by default in PHP 5.5+.',
						),
						array(
							'name' => 'When to flush',
							'rec'  => 'Info',
							'html' => 'OPcache must be flushed after PHP files change on disk, otherwise running workers may serve stale bytecode. This can cause <strong>SIGSEGV crashes</strong> if a worker has an old cached version that conflicts with the new file. The deploy script flushes OPcache automatically via this endpoint after every plugin update.',
						),
						array(
							'name' => 'Deploy script flush',
							'rec'  => 'Recommended',
							'html' => 'The <code>deploy-wordpress.sh</code> script calls this endpoint using the deploy token after syncing plugin files. This ensures PHP-FPM workers reload fresh bytecode without a full container restart. The token is stored in <code>csdt_opcache_token</code> and is separate from other credentials.',
						),
						array(
							'name' => 'Hit rate',
							'rec'  => 'Info',
							'html' => 'A healthy OPcache hit rate is above 95%. Low hit rates (below 70%) indicate the cache is too small for your codebase, or that scripts are being invalidated too frequently. Increase <code>opcache.memory_consumption</code> in <code>php.ini</code> if the cache is frequently full.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 14px;"><?php esc_html_e( 'OPcache compiles PHP files once and stores the bytecode in server memory, so subsequent requests skip the parse-and-compile step entirely. Flush it after a deployment to ensure the latest code is running.', 'cloudscale-cyber-devtools' ); ?></p>

                <div id="csdt-opcache-stats-wrap">
                    <div id="csdt-opcache-stats" style="font-size:.82em;color:#64748b;"><?php esc_html_e( 'Loading…', 'cloudscale-cyber-devtools' ); ?></div>
                </div>

                <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:14px;">
                    <button type="button" id="csdt-opcache-flush-btn" class="cs-btn-primary">
                        ⚡ <?php esc_html_e( 'Flush OPcache', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" id="csdt-opcache-refresh-btn" class="cs-btn-secondary">
                        🔄 <?php esc_html_e( 'Refresh Stats', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <span id="csdt-opcache-status" style="font-size:.82em;color:#16a34a;"></span>
                </div>

                <div style="margin-top:18px;padding:12px 14px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;">
                    <div style="font-size:.78em;color:#64748b;margin-bottom:6px;"><?php esc_html_e( 'Deploy token, used by deploy-wordpress.sh to flush OPcache after each deploy', 'cloudscale-cyber-devtools' ); ?></div>
                    <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                        <code id="csdt-opcache-token-display" style="font-size:.78em;background:#f1f5f9;padding:4px 10px;border-radius:4px;color:#475569;word-break:break-all;"><?php echo esc_html( substr( $opcache_token, 0, 8 ) . str_repeat( '•', 16 ) ); ?></code>
                        <button type="button" id="csdt-opcache-token-reveal" class="cs-btn-secondary cs-btn-sm"><?php esc_html_e( 'Reveal', 'cloudscale-cyber-devtools' ); ?></button>
                        <button type="button" id="csdt-opcache-token-copy" class="cs-btn-secondary cs-btn-sm" data-token="<?php echo esc_attr( $opcache_token ); ?>"><?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></button>
                        <span id="csdt-opcache-copy-status" style="font-size:.78em;color:#16a34a;"></span>
                    </div>
                    <div style="margin-top:10px;font-size:.75em;color:#475569;">
                        <?php esc_html_e( 'Last flush:', 'cloudscale-cyber-devtools' ); ?>
                        <span id="csdt-opcache-last-flush" style="color:#94a3b8;">
                            <?php echo $last_flush ? esc_html( human_time_diff( $last_flush ) . ' ago' ) : esc_html__( 'never', 'cloudscale-cyber-devtools' ); ?>
                        </span>
                    </div>
                </div>

            </div>
        </div>
        <?php
    }

    private static function render_server_logs_panel(): void {
        $sources        = self::get_log_sources();
        $php_configured = ! empty( get_option( 'csdt_php_error_log_path', '' ) );
        $custom_paths   = get_option( 'csdt_custom_log_paths', array() );
        ?>
        <div class="cs-panel" id="cs-panel-logs">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#1e3a8a 0%,#1d4ed8 100%);border-left:3px solid #60a5fa;">
                <span>📋 <?php esc_html_e( 'Server Logs', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Read-only view of PHP error log, WordPress debug log, and web server logs', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'server-logs',
                    'Server Logs',
                    array(
						array(
							'name' => 'Log Sources',
							'rec'  => 'Info',
							'html' => 'The panel automatically detects common log file locations for your server stack, Apache, Nginx, PHP-FPM, WordPress debug log, and any custom paths you add. Each source button shows a colour-coded status: <strong>green</strong> (readable), <strong>amber</strong> (empty), <strong>red</strong> (not found or permission denied).',
						),
						array(
							'name' => 'PHP Error Log',
							'rec'  => 'Recommended',
							'html' => 'If PHP is logging to <code>/dev/stderr</code> (the default in many Docker/container setups), errors cannot be read here. Click <strong>Enable</strong> to install a mu-plugin that redirects PHP errors to <code>wp-content/php-error.log</code>. The mu-plugin runs on every request before other plugins load.',
						),
						array(
							'name' => 'Filters',
							'rec'  => 'Info',
							'html' => '<ul><li><strong>Search</strong>, live text filter across all visible lines</li><li><strong>Level</strong>, show only lines at or above a severity (emergency → debug)</li><li><strong>Lines</strong>, how many tail lines to fetch from the server (100 to 2000)</li></ul>Colour coding: red = error/critical, amber = warning, blue = notice/info, grey = debug.',
						),
						array(
							'name' => 'Auto-refresh',
							'rec'  => 'Optional',
							'html' => 'Enable <em>Tail mode</em> to poll the selected log every 30 seconds automatically. Useful when watching a running process or debugging a live issue without leaving the page.',
						),
						array(
							'name' => 'Custom Log Paths',
							'rec'  => 'Optional',
							'html' => 'Add any absolute file path your web server user can read. Common extras: application logs (Laravel <code>storage/logs/laravel.log</code>), cron output files, or a custom PHP-FPM pool log. Labels are free-text, choose something descriptive. Custom paths are saved as a WordPress option and survive plugin updates.',
						),
						array(
							'name' => 'Permissions',
							'rec'  => 'Info',
							'html' => 'System logs (e.g. <code>/var/log/syslog</code>, <code>/var/log/auth.log</code>) are typically owned by <code>root</code> and not readable by <code>www-data</code>. This is intentional OS hardening, the plugin shows a clear "permission denied" message rather than an error. To expose a system log, add your web server user to the <code>adm</code> group or use a log-shipping tool.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">

                <?php if ( ! $php_configured ) : ?>
					<?php
                    $mu_dir      = WPMU_PLUGIN_DIR;
					$mu_writable = is_dir( $mu_dir ) && is_writable( $mu_dir ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable -- WP_Filesystem has no is_writable() equivalent; needed to gate the "Auto-install" button 
                    ?>
                <div id="cs-logs-php-setup" style="padding:14px 16px;margin-bottom:16px;background:#fffbeb;border:1px solid #fcd34d;border-radius:6px;">
                    <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:6px;">
                        <strong style="font-size:13px;color:#92400e;"><?php esc_html_e( 'PHP Error Log not writing to a file', 'cloudscale-cyber-devtools' ); ?></strong>
                        <button type="button" class="cs-btn-primary cs-btn-sm" id="cs-logs-php-setup-btn"
                                style="white-space:nowrap;flex-shrink:0;"
                                <?php echo ! $mu_writable ? 'disabled title="' . esc_attr__( 'Fix the permissions warning first', 'cloudscale-cyber-devtools' ) . '"' : ''; ?>>
                            ⚡ <?php esc_html_e( 'Enable', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                    </div>
                    <p style="margin:0 0 0;font-size:12px;color:#78350f;line-height:1.5;"><?php esc_html_e( 'PHP is currently logging to a system stream (e.g. /dev/stderr) that cannot be read here. Click Enable to install a mu-plugin that redirects PHP errors to wp-content/php-error.log.', 'cloudscale-cyber-devtools' ); ?></p>
                    <?php if ( ! $mu_writable ) : ?>
                    <div id="cs-logs-perm-warning" style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px;padding:8px 12px;background:#fef3c7;border-radius:4px;font-size:12px;color:#78350f;">
                        <span style="flex:1;">⚠️ <?php esc_html_e( 'wp-content/mu-plugins is not writable by the web server.', 'cloudscale-cyber-devtools' ); ?></span>
                        <button type="button" class="cs-btn-secondary cs-btn-sm" id="cs-logs-fix-perm-btn" style="flex-shrink:0;white-space:nowrap;">
                            🔧 <?php esc_html_e( 'Fix Permissions', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                    </div>
                    <?php endif; ?>
                </div>
                <?php endif; ?>

                <!-- Source picker -->
                <div id="cs-logs-sources" style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;">
                    <?php foreach ( $sources as $key => $src ) : ?>
                        <button class="cs-btn-secondary cs-log-src-btn" data-source="<?php echo esc_attr( $key ); ?>">
                            <?php echo esc_html( $src['label'] ); ?>
                        </button>
                    <?php endforeach; ?>
                    <?php if ( empty( $sources ) ) : ?>
                        <span style="color:#888;font-size:13px;"><?php esc_html_e( 'No log paths detected on this server.', 'cloudscale-cyber-devtools' ); ?></span>
                    <?php endif; ?>
                </div>

                <!-- Toolbar -->
                <div style="display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;">
                    <input type="text" id="cs-logs-search" placeholder="<?php esc_attr_e( 'Search…', 'cloudscale-cyber-devtools' ); ?>"
                            style="flex:1;min-width:160px;max-width:320px;" class="cs-text-input">
                    <select id="cs-logs-level" class="cs-sec-select" style="width:auto;">
                        <option value=""><?php esc_html_e( 'All levels', 'cloudscale-cyber-devtools' ); ?></option>
                        <option value="error"><?php esc_html_e( 'Error+', 'cloudscale-cyber-devtools' ); ?></option>
                        <option value="warn"><?php esc_html_e( 'Warning only', 'cloudscale-cyber-devtools' ); ?></option>
                        <option value="notice"><?php esc_html_e( 'Notice only', 'cloudscale-cyber-devtools' ); ?></option>
                    </select>
                    <select id="cs-logs-lines" class="cs-sec-select" style="width:auto;">
                        <option value="100">100 lines</option>
                        <option value="300" selected>300 lines</option>
                        <option value="500">500 lines</option>
                        <option value="1000">1000 lines</option>
                    </select>
                    <button id="cs-logs-refresh" class="cs-btn-secondary">🔄 <?php esc_html_e( 'Refresh', 'cloudscale-cyber-devtools' ); ?></button>
                    <label style="display:flex;align-items:center;gap:6px;font-size:13px;color:#9da5b4;cursor:pointer;">
                        <input type="checkbox" id="cs-logs-tail" style="cursor:pointer;">
                        <?php esc_html_e( 'Auto-refresh (30s)', 'cloudscale-cyber-devtools' ); ?>
                    </label>
                    <span id="cs-logs-status" style="font-size:12px;color:#888;margin-left:auto;"></span>
                </div>

                <!-- Log viewer -->
                <div id="cs-logs-viewer" style="
                    background:#f8fafc;
                    border:1px solid #d1d5db;
                    border-radius:6px;
                    padding:12px;
                    height:520px;
                    overflow-y:auto;
                    font-family:'SF Mono','Fira Code',monospace;
                    font-size:12px;
                    line-height:1.6;
                    color:#1e293b;
                ">
                    <div class="cs-logs-placeholder" style="color:#94a3b8;padding:20px;text-align:center;">
                        <?php esc_html_e( 'Select a log source above to view entries.', 'cloudscale-cyber-devtools' ); ?>
                    </div>
                </div>

                <!-- Custom log paths -->
                <div style="margin-top:20px;border-top:1px solid #e8edf5;padding-top:16px;">
                    <div style="font-weight:600;font-size:13px;color:#1d2327;margin-bottom:8px;">
                        <?php esc_html_e( 'Custom log paths', 'cloudscale-cyber-devtools' ); ?>
                    </div>
                    <p style="font-size:12px;color:#6b7280;margin:0 0 10px;">
                        <?php esc_html_e( 'Add any log file your web server user can read (e.g. a custom nginx log, a container log written to a shared volume, or any application log file).', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <div id="cs-logs-custom-list">
                        <?php foreach ( (array) $custom_paths as $i => $cp ) : ?>
                        <div class="cs-logs-custom-row" style="display:flex;gap:8px;align-items:center;margin-bottom:6px;">
                            <input type="text" class="cs-text-input cs-logs-custom-label" placeholder="<?php esc_attr_e( 'Label', 'cloudscale-cyber-devtools' ); ?>" value="<?php echo esc_attr( $cp['label'] ?? '' ); ?>" style="width:140px;flex-shrink:0;">
                            <input type="text" class="cs-text-input cs-logs-custom-path" placeholder="<?php esc_attr_e( '/path/to/file.log', 'cloudscale-cyber-devtools' ); ?>" value="<?php echo esc_attr( $cp['path'] ?? '' ); ?>" style="flex:1;min-width:0;">
                            <button type="button" class="cs-btn-secondary cs-btn-sm cs-logs-custom-remove" style="color:#dc2626;border-color:#fca5a5;flex-shrink:0;">✕</button>
                        </div>
                        <?php endforeach; ?>
                    </div>
                    <div style="display:flex;gap:8px;margin-top:8px;">
                        <button type="button" class="cs-btn-secondary cs-btn-sm" id="cs-logs-custom-add">+ <?php esc_html_e( 'Add path', 'cloudscale-cyber-devtools' ); ?></button>
                        <button type="button" class="cs-btn-primary cs-btn-sm" id="cs-logs-custom-save">💾 <?php esc_html_e( 'Save', 'cloudscale-cyber-devtools' ); ?></button>
                        <span id="cs-logs-custom-saved" class="cs-settings-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

            </div>
        </div>
        <?php
    }

    public static function ajax_server_logs_status(): void {
        check_ajax_referer( self::LOGS_NONCE, 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        try {
            $sources  = self::get_log_sources();
            $statuses = array();
            foreach ( $sources as $key => $src ) {
                $path = $src['path'];
                if ( ! file_exists( $path ) ) {
                    $statuses[ $key ] = array( 'status' => 'not_found' );
                } elseif ( ! is_readable( $path ) ) {
                    $statuses[ $key ] = array( 'status' => 'permission_denied' );
                } elseif ( filesize( $path ) === 0 ) {
                    $statuses[ $key ] = array( 'status' => 'empty' );
                } else {
                    $statuses[ $key ] = array( 'status' => 'ok' );
                }
            }
            wp_send_json_success( $statuses );
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }

    public static function ajax_server_logs_fetch(): void {
        check_ajax_referer( self::LOGS_NONCE, 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        try {
            $source_key = isset( $_POST['source'] ) ? sanitize_key( $_POST['source'] ) : '';
            $lines_req  = min( 1000, max( 50, absint( wp_unslash( $_POST['lines'] ?? 300 ) ) ) );
            $sources    = self::get_log_sources();

            if ( ! isset( $sources[ $source_key ] ) ) {
                wp_send_json_error( array( 'message' => 'Unknown source.' ) );
                return;
            }

            $path = $sources[ $source_key ]['path'];

            if ( ! file_exists( $path ) ) {
                wp_send_json_success(
                    array(
						'status' => 'not_found',
						'lines'  => array(),
						'count'  => 0,
						'path'   => $path,
                    )
                );
                return;
            }
            if ( ! is_readable( $path ) ) {
                wp_send_json_success(
                    array(
						'status' => 'permission_denied',
						'lines'  => array(),
						'count'  => 0,
						'path'   => $path,
                    )
                );
                return;
            }

            // Read last N lines efficiently without loading the entire file
            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- streaming local log file; WP Filesystem API lacks partial-read/seek support
            $handle = is_readable( $path ) ? fopen( $path, 'rb' ) : false;
            if ( ! $handle ) {
                wp_send_json_success(
                    array(
						'status' => 'error',
						'lines'  => array(),
						'count'  => 0,
						'path'   => $path,
                    )
                );
                return;
            }

            fseek( $handle, 0, SEEK_END );
            $file_size  = ftell( $handle );
            $chunk_size = 65536; // 64 KB chunks, read from end
            $buffer     = '';
            $pos        = $file_size;
            $line_count = 0;

            while ( $pos > 0 && $line_count < $lines_req + 1 ) {
                $read = min( $chunk_size, $pos );
                $pos -= $read;
                fseek( $handle, $pos );
                // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- partial read of local log file
                $buffer     = fread( $handle, $read ) . $buffer;
                $line_count = substr_count( $buffer, "\n" );
            }
            fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- paired with fopen above

            $all_lines = explode( "\n", $buffer );
            // Remove trailing empty line
            if ( end( $all_lines ) === '' ) {
				array_pop( $all_lines ); }
            $lines = array_slice( $all_lines, -$lines_req );

            wp_send_json_success(
                array(
					'status' => count( $lines ) > 0 ? 'ok' : 'empty',
					'lines'  => $lines,
					'count'  => count( $lines ),
					'path'   => $path,
                )
            );
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }

    private static function render_migrate_panel() {
        ?>
        <div class="cs-panel" id="cs-panel-migrator">
            <div class="cs-section-header">
                <span>🔄 CODE BLOCK MIGRATOR</span>
                <?php
                self::render_explain_btn(
                    'migrator',
                    'Code Block Migrator',
                    array(
						array(
							'name' => 'Scan Posts',
							'rec'  => 'Informational',
							'html' => 'Scans all posts and pages for legacy WordPress <code>wp:code</code> and <code>wp:preformatted</code> blocks that can be upgraded to CloudScale Code Blocks with full syntax highlighting.',
						),
						array(
							'name' => 'Preview',
							'rec'  => 'Recommended',
							'html' => 'Shows a side-by-side before/after diff for each post <em>before</em> committing any changes, so you can review exactly what will be converted.',
						),
						array(
							'name' => 'Migrate',
							'rec'  => 'Optional',
							'html' => 'Converts detected legacy blocks to CloudScale format. Each post is saved with the converted markup.<br><br><strong>Take a backup first</strong>, this cannot be undone without one.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#555;margin:0 0 16px;font-size:13px;line-height:1.6">
                    <?php esc_html_e( 'Scan your posts for legacy WordPress code blocks, preview changes, then migrate one at a time or all at once.', 'cloudscale-cyber-devtools' ); ?>
                </p>

                <div class="cs-migrate-toolbar">
                    <button id="cs-scan-btn" class="cs-btn-primary" style="padding:8px 20px;font-size:13px">
                        <span class="dashicons dashicons-search" style="font-size:14px;width:14px;height:14px;margin-top:1px"></span> <?php esc_html_e( 'Scan Posts', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button id="cs-migrate-all-btn" class="cs-btn-orange" style="padding:8px 20px;font-size:13px" disabled>
                        <span class="dashicons dashicons-update" style="font-size:14px;width:14px;height:14px;margin-top:1px"></span> <?php esc_html_e( 'Migrate All Remaining', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <span id="cs-scan-status" class="cs-status"></span>
                </div>

                <div id="cs-results-area">
                    <p class="cs-migrate-hint"><?php /* translators: %s is the label of the "Scan Posts" button */ printf( wp_kses( __( 'Click %s to find all posts with legacy code blocks.', 'cloudscale-cyber-devtools' ), array( 'strong' => array() ) ), '<strong>' . esc_html__( 'Scan Posts', 'cloudscale-cyber-devtools' ) . '</strong>' ); ?></p>
                </div>
            </div>
        </div>

        <?php
        $default_theme  = get_option( 'csdt_devtools_code_default_theme', 'dark' );
        $pair_slug      = get_option( 'csdt_devtools_code_theme_pair', 'atom-one' );
        $theme_registry = self::get_theme_registry();
        ?>
        <div class="cs-panel" id="cs-panel-restyle">
            <div class="cs-section-header">
                <span>🎨 <?php esc_html_e( 'RE-APPLY DEFAULT THEME', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'restyle',
                    'Re-apply Default Theme',
                    array(
						array(
							'name' => 'Pick palette + mode',
							'rec'  => 'Step 1',
							'html' => 'In the two dropdowns above, choose a <strong>Color Theme</strong> (Atom One, Nord, Dracula, Tokyo Night, GitHub, Monokai, etc.) and a <strong>Default Mode</strong> (Dark or Light). These become the site-wide defaults.',
						),
						array(
							'name' => 'Save the default',
							'rec'  => 'Step 2',
							'html' => 'Click <strong>💾 Save default theme</strong>. The values are written to the site-wide options. From now on, every <em>new</em> code block automatically inherits these.',
						),
						array(
							'name' => 'Apply to existing',
							'rec'  => 'Step 3',
							'html' => 'Click <strong>🎨 Re-apply default theme to all blocks</strong>. The plugin walks every CloudScale Code Block in every post and clears its per-block <code>theme</code> override so existing blocks snap to the new default too. Without this step, only new blocks pick up the change.',
						),
						array(
							'name' => 'What it leaves alone',
							'rec'  => 'Informational',
							'html' => 'Only the per-block <code>theme</code> attribute is reset. <code>language</code>, <code>title</code>, GitHub source URL, prefix/suffix, and all other per-block settings are <strong>not</strong> touched.',
						),
						array(
							'name' => 'Per-block overrides',
							'rec'  => 'Informational',
							'html' => 'After re-applying, any block can still set its own theme override from the block inspector, handy for a single demo block that should always render dark or always light regardless of the site default.',
						),
						array(
							'name' => 'Reversibility',
							'rec'  => 'Warning',
							'html' => 'There is no undo button. If you have many blocks with intentional per-block theme overrides you want to keep, take a database backup first.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#555;margin:0 0 14px;font-size:13px;line-height:1.6">
                    <?php esc_html_e( 'Pick the default below, save it, then click Re-apply to update every existing block.', 'cloudscale-cyber-devtools' ); ?>
                </p>

                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-restyle-pair"><?php esc_html_e( 'Color Theme:', 'cloudscale-cyber-devtools' ); ?></label>
                        <select id="cs-restyle-pair" class="cs-input">
                            <?php foreach ( $theme_registry as $slug => $info ) : ?>
                                <option value="<?php echo esc_attr( $slug ); ?>" <?php selected( $pair_slug, $slug ); ?>>
                                    <?php echo esc_html( $info['label'] ); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                        <span class="cs-hint"><?php esc_html_e( 'Syntax highlighting palette used as the site-wide default.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                    <div class="cs-field">
                        <label class="cs-label" for="cs-restyle-theme"><?php esc_html_e( 'Default Mode:', 'cloudscale-cyber-devtools' ); ?></label>
                        <select id="cs-restyle-theme" class="cs-input">
                            <option value="dark"  <?php selected( $default_theme, 'dark' ); ?>><?php esc_html_e( 'Dark', 'cloudscale-cyber-devtools' ); ?></option>
                            <option value="light" <?php selected( $default_theme, 'light' ); ?>><?php esc_html_e( 'Light', 'cloudscale-cyber-devtools' ); ?></option>
                        </select>
                        <span class="cs-hint"><?php esc_html_e( 'Site-wide default. Visitors can still toggle per block.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <div class="cs-migrate-toolbar" style="margin-top:16px;">
                    <button id="cs-restyle-save-btn" class="cs-btn-primary" style="padding:8px 20px;font-size:13px">
                        💾 <?php esc_html_e( 'Save default theme', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button id="cs-restyle-all-btn" class="cs-btn-orange" style="padding:8px 20px;font-size:13px">
                        <span class="dashicons dashicons-art" style="font-size:14px;width:14px;height:14px;margin-top:1px"></span>
                        <?php esc_html_e( 'Re-apply default theme to all blocks', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <span id="cs-restyle-status" class="cs-status"></span>
                </div>
                <div id="cs-restyle-results" style="margin-top:14px;"></div>
            </div>
        </div>

        <div id="cs-preview-modal" class="cs-modal" style="display:none;">
            <div class="cs-modal-backdrop"></div>
            <div class="cs-modal-content">
                <div class="cs-modal-header">
                    <h2 id="cs-modal-title"><?php esc_html_e( 'Preview', 'cloudscale-cyber-devtools' ); ?></h2>
                    <button class="cs-modal-close">&times;</button>
                </div>
                <div class="cs-modal-body" id="cs-modal-body">
                    <?php esc_html_e( 'Loading...', 'cloudscale-cyber-devtools' ); ?>
                </div>
                <div class="cs-modal-footer">
                    <button id="cs-modal-migrate-btn" class="cs-btn-primary" data-post-id="" style="padding:8px 20px">
                        <span class="dashicons dashicons-yes-alt"></span> <?php esc_html_e( 'Migrate This Post', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button class="cs-modal-close-btn" style="background:#fff;border:1.5px solid #dce3ef;border-radius:5px;padding:6px 16px;font-size:12px;font-weight:600;cursor:pointer"><?php esc_html_e( 'Cancel', 'cloudscale-cyber-devtools' ); ?></button>
                </div>
            </div>
        </div>
        <?php
    }

    /*
    ==================================================================
        5c. SQL Command panel
        ================================================================== */

    /**
     * Renders the SQL Command query panel including quick-query buttons.
     *
     * @since  1.6.0
     * @return void
     */
    private static function render_sql_panel() {
        global $wpdb;
        $prefix = $wpdb->prefix;
        ?>
        <div class="cs-panel" id="cs-panel-sql">
            <div class="cs-section-header cs-section-header-purple">
                <span>🗄️ <?php esc_html_e( 'SQL Query', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><code style="background:rgba(255,255,255,.15);padding:1px 6px;border-radius:3px;"><?php echo esc_html( $prefix ); ?></code> &nbsp;·&nbsp; ⚠ <?php esc_html_e( 'Read only (SELECT, SHOW, DESCRIBE, EXPLAIN)', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'sql',
                    'SQL Query Tool',
                    array(
						array(
							'name' => 'Read-only',
							'rec'  => 'Informational',
							'html' => 'Only <code>SELECT</code>, <code>SHOW</code>, <code>DESCRIBE</code>, and <code>EXPLAIN</code> queries are permitted. Write operations (<code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code>, <code>DROP</code>, <code>ALTER</code>, <code>TRUNCATE</code>) are blocked to prevent accidental data loss.',
						),
						array(
							'name' => 'Table Prefix',
							'rec'  => 'Informational',
							'html' => 'Your WordPress table prefix is shown in the header. Use it in your queries, e.g. <code>SELECT * FROM wp_posts LIMIT 10</code> or <code>SELECT * FROM wp_options WHERE option_name = \'siteurl\'</code>.',
						),
						array(
							'name' => 'Quick Queries',
							'rec'  => 'Recommended',
							'html' => 'Use the preset queries below for common diagnostics without needing to write SQL from scratch. Press <code>Enter</code> or <code>Ctrl+Enter</code> to run a query, <code>Shift+Enter</code> to insert a newline.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 14px;"><?php esc_html_e( 'Run SELECT queries directly against your WordPress database. Results are shown in a sortable table below. Write access is restricted, use this for read-only diagnostics and data lookups, not structural changes.', 'cloudscale-cyber-devtools' ); ?></p>
                <textarea id="cs-sql-input" class="cs-sql-textarea" placeholder="SELECT option_name, option_value FROM <?php echo esc_attr( $prefix ); ?>options WHERE option_name = 'siteurl';"></textarea>
                <div style="display:flex;align-items:center;gap:10px;margin-top:12px">
                    <button type="button" class="cs-btn-primary" id="cs-sql-run" style="padding:8px 20px;font-size:13px">▶ <?php esc_html_e( 'Run Query', 'cloudscale-cyber-devtools' ); ?></button>
                    <button type="button" class="cs-btn-pink" id="cs-sql-clear">🧹 <?php esc_html_e( 'Clear', 'cloudscale-cyber-devtools' ); ?></button>
                    <span id="cs-sql-status" style="font-size:12px;color:#888"></span>
                    <span style="margin-left:auto;font-size:11px;color:#999"><?php esc_html_e( 'Enter or Ctrl+Enter to run', 'cloudscale-cyber-devtools' ); ?></span>
                </div>

                <!-- Results subsection -->
                <div style="margin:24px 0 10px;padding-bottom:8px;border-bottom:2px solid #e2e8f0;display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                    <span style="font-size:12px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:.06em;">📊 <?php esc_html_e( 'Results', 'cloudscale-cyber-devtools' ); ?></span>
                    <span id="cs-sql-meta" style="font-size:12px;color:#64748b;"></span>
                    <?php
                    self::render_explain_btn(
                        'sql-results',
                        'SQL Results',
                        array(
							array(
								'name' => 'Table output',
								'rec'  => 'Informational',
								'html' => 'Query results are shown in a scrollable table with column headers. HTTP URLs in cells are highlighted for easy identification.',
							),
							array(
								'name' => 'Row count / timing',
								'rec'  => 'Informational',
								'html' => 'The header shows the number of rows returned and the query execution time in milliseconds.',
							),
						)
                    );
                    ?>
                </div>
                <div id="cs-sql-results" style="overflow-x:auto;font-size:13px">
                    <div style="text-align:center;color:#999;padding:40px 0"><?php esc_html_e( 'Run a query to see results here', 'cloudscale-cyber-devtools' ); ?></div>
                </div>

                <!-- Quick Queries subsection -->
                <div style="margin:28px 0 12px;padding-bottom:8px;border-bottom:2px solid #e2e8f0;display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                    <span style="font-size:12px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:.06em;">⚡ <?php esc_html_e( 'Quick Queries', 'cloudscale-cyber-devtools' ); ?></span>
                    <?php
                    self::render_explain_btn(
                        'quick-queries',
                        'Quick Queries',
                        array(
							array(
								'name' => 'Health & Diagnostics',
								'rec'  => 'Recommended',
								'html' => 'MySQL version, table sizes, connection limits, and WordPress table row counts at a glance. Good first check when diagnosing slow sites.',
							),
							array(
								'name' => 'Content Summary',
								'rec'  => 'Informational',
								'html' => 'Counts posts by type and status, revisions, auto-drafts, spam comments, and users for a quick content audit. Useful before a site migration.',
							),
							array(
								'name' => 'Cleanup Candidates',
								'rec'  => 'Optional',
								'html' => 'Identifies orphaned <code>postmeta</code> rows, expired transients, and bloated <code>wp_options</code> autoloaded rows that may be slowing down your database.',
							),
							array(
								'name' => 'Security Checks',
								'rec'  => 'Optional',
								'html' => 'Looks for <code>http://</code> (non-HTTPS) URLs or stale IP addresses in <code>wp_options</code> and post GUIDs, common indicators of old content or unfinished HTTP→HTTPS migrations.',
							),
						)
                    );
                    ?>
                </div>
                <p class="cs-quick-group-label">🏥 <?php esc_html_e( 'Health and Diagnostics', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-quick-grid">
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT @@version AS mysql_version, @@global.max_connections AS max_connections, @@global.wait_timeout AS wait_timeout_sec, @@global.max_allowed_packet / 1024 / 1024 AS max_packet_mb, DATABASE() AS current_db;">
                        🩺 <?php esc_html_e( 'Database health check', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT option_id, option_name, LEFT(option_value, 200) AS option_value_preview FROM <?php echo esc_attr( $prefix ); ?>options WHERE option_name IN ('siteurl','home','blogname','blogdescription','wp_version','db_version');">
                        🏠 <?php esc_html_e( 'Site identity options', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT table_name, engine, table_rows, ROUND(data_length/1024/1024, 2) AS data_mb, ROUND(index_length/1024/1024, 2) AS index_mb, ROUND((data_length + index_length)/1024/1024, 2) AS total_mb FROM information_schema.tables WHERE table_schema = DATABASE() ORDER BY (data_length + index_length) DESC;">
                        📊 <?php esc_html_e( 'Table names, sizes and rows', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                </div>

                <p class="cs-quick-group-label">📈 <?php esc_html_e( 'Content Summary', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-quick-grid">
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT post_type, post_status, COUNT(*) AS total FROM <?php echo esc_attr( $prefix ); ?>posts GROUP BY post_type, post_status ORDER BY total DESC;">
                        📰 <?php esc_html_e( 'Posts by type and status', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>posts WHERE post_status='publish') AS published_posts, (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>posts WHERE post_type='revision') AS revisions, (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>posts WHERE post_status='auto-draft') AS auto_drafts, (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>posts WHERE post_status='trash') AS trashed, (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>comments) AS total_comments, (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>comments WHERE comment_approved='spam') AS spam_comments, (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>users) AS users, (SELECT COUNT(*) FROM <?php echo esc_attr( $prefix ); ?>options WHERE option_name LIKE '%_transient_%') AS transients;">
                        📋 <?php esc_html_e( 'Site stats summary', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT ID, post_title, post_date, post_status FROM <?php echo esc_attr( $prefix ); ?>posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY post_date DESC LIMIT 20;">
                        📝 <?php esc_html_e( 'Latest 20 published posts', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                </div>

                <p class="cs-quick-group-label">🧹 <?php esc_html_e( 'Bloat and Cleanup Checks', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-quick-grid">
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT COUNT(*) AS orphaned_postmeta FROM <?php echo esc_attr( $prefix ); ?>postmeta pm LEFT JOIN <?php echo esc_attr( $prefix ); ?>posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;">
                        🗑️ <?php esc_html_e( 'Orphaned postmeta count', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT COUNT(*) AS expired_transients FROM <?php echo esc_attr( $prefix ); ?>options WHERE option_name LIKE '_transient_timeout_%' AND option_value < UNIX_TIMESTAMP();">
                        ⏰ <?php esc_html_e( 'Expired transients count', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT post_type, COUNT(*) AS total FROM <?php echo esc_attr( $prefix ); ?>posts WHERE post_type = 'revision' OR post_status = 'auto-draft' OR post_status = 'trash' GROUP BY post_type, post_status ORDER BY total DESC;">
                        📦 <?php esc_html_e( 'Revisions, drafts and trash', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT LEFT(option_name, 40) AS option_name, LENGTH(option_value) AS value_bytes FROM <?php echo esc_attr( $prefix ); ?>options WHERE autoload = 'yes' ORDER BY LENGTH(option_value) DESC LIMIT 30;">
                        ⚖️ <?php esc_html_e( 'Largest autoloaded options', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                </div>

                <p class="cs-quick-group-label">🔍 <?php esc_html_e( 'URL and Migration Helpers', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-quick-grid">
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT option_id, option_name, option_value FROM <?php echo esc_attr( $prefix ); ?>options WHERE option_value LIKE '%http://%' LIMIT 50;">
                        🔗 <?php esc_html_e( 'Insecure HTTP references (options)', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT ID, post_title, post_type, post_status, guid FROM <?php echo esc_attr( $prefix ); ?>posts WHERE guid LIKE '%http://%' LIMIT 50;">
                        📰 <?php esc_html_e( 'Posts with HTTP GUIDs', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT post_id, meta_key, LEFT(meta_value, 200) AS meta_value_preview FROM <?php echo esc_attr( $prefix ); ?>postmeta WHERE meta_value LIKE '%http://54.195%' LIMIT 50;">
                        🖥️ <?php esc_html_e( 'Old IP references (postmeta)', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button type="button" class="cs-quick-btn cs-sql-quick" data-sql="SELECT ID, post_title, post_type FROM <?php echo esc_attr( $prefix ); ?>posts WHERE post_status = 'publish' AND ID NOT IN (SELECT post_id FROM <?php echo esc_attr( $prefix ); ?>postmeta WHERE meta_key = '_csdt_devtools_seo_desc' AND meta_value != '') ORDER BY post_date DESC LIMIT 50;">
                        📝 <?php esc_html_e( 'Posts missing meta descriptions', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                </div>

                <div style="margin-top:12px;background:#fef3c7;border:1px solid #fcd34d;border-radius:8px;padding:14px 18px;">
                    <p style="margin:0 0 6px;font-size:13px;font-weight:600;color:#92400e;">🔧 <?php esc_html_e( 'Fix HTTP → HTTPS', 'cloudscale-cyber-devtools' ); ?></p>
                    <p style="margin:0 0 12px;font-size:12px;color:#78350f;line-height:1.5;"><?php esc_html_e( 'Runs WP-CLI search-replace server-side, safely handles serialised data. Dry Run previews the count without making changes.', 'cloudscale-cyber-devtools' ); ?></p>
                    <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
                        <button type="button" id="cs-http-fix-dry" class="cs-btn-secondary cs-btn-sm" style="border-color:#f59e0b;color:#92400e;">🔍 <?php esc_html_e( 'Dry Run', 'cloudscale-cyber-devtools' ); ?></button>
                        <button type="button" id="cs-http-fix-run" class="cs-btn-primary cs-btn-sm" style="background:#d97706;border-color:#d97706;">⚡ <?php esc_html_e( 'Fix It', 'cloudscale-cyber-devtools' ); ?></button>
                        <span id="cs-http-fix-status" style="font-size:12px;color:#92400e;"></span>
                    </div>
                    <pre id="cs-http-fix-output" style="display:none;margin-top:12px;background:#f1f5f9;color:#1e293b;padding:10px 14px;border-radius:6px;font-size:11px;line-height:1.6;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto;border:1px solid #d1d5db;"></pre>
                </div>
            </div>
        </div>

        <?php
    }

    /*
    ==================================================================
        5d. Login Security panel
        ================================================================== */

    /**
     * Renders the Login Security admin panel (Hide Login + 2FA settings).
     *
     * @since  1.9.4
     * @return void
     */
    private static function render_login_panel(): void {
        $hide_on      = get_option( 'csdt_devtools_login_hide_enabled', '0' ) === '1';
        $slug         = get_option( 'csdt_devtools_login_slug', '' );
        $method       = get_option( 'csdt_devtools_2fa_method', 'off' );
        $force        = get_option( 'csdt_devtools_2fa_force_admins', '0' ) === '1';
        $user_id      = get_current_user_id();
        $totp_active  = get_user_meta( $user_id, 'csdt_devtools_totp_enabled', true ) === '1';
        $email_active = get_user_meta( $user_id, 'csdt_devtools_2fa_email_enabled', true ) === '1';
        $current_url  = empty( $slug ) ? wp_login_url() : home_url( '/' . $slug );

        // Success notice after email verification callback.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $email_just_activated = isset( $_GET['email_2fa_activated'] ) && '1' === $_GET['email_2fa_activated'];
        ?>

        <?php if ( $email_just_activated ) : ?>
        <div class="cs-modal-overlay" id="cs-email-verified-modal" role="dialog" aria-modal="true" aria-labelledby="cs-modal-title">
            <div class="cs-modal-card">
                <div class="cs-modal-icon">✅</div>
                <h2 class="cs-modal-title" id="cs-modal-title"><?php esc_html_e( 'Email Verified!', 'cloudscale-cyber-devtools' ); ?></h2>
                <p class="cs-modal-msg"><?php esc_html_e( 'Email 2FA is now active on your account. You\'ll receive a one-time code after each password login.', 'cloudscale-cyber-devtools' ); ?></p>
                <button type="button" class="cs-btn-primary cs-modal-btn" id="cs-email-modal-close">
                    <?php esc_html_e( 'Got it', 'cloudscale-cyber-devtools' ); ?>
                </button>
                <p class="cs-modal-auto"><?php esc_html_e( 'Closing in', 'cloudscale-cyber-devtools' ); ?> <span id="cs-modal-countdown">6</span>s…</p>
            </div>
        </div>
        <?php endif; ?>
        <?php
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( isset( $_GET['email_verify_expired'] ) && '1' === $_GET['email_verify_expired'] ) :
			?>
        <div class="notice notice-error" style="margin:0 0 18px">
            <p>⏰ <strong><?php esc_html_e( 'Verification link expired. Please click Enable again to send a new one.', 'cloudscale-cyber-devtools' ); ?></strong></p>
        </div>
        <?php endif; ?>

        <!-- ── Hide Login ─────────────────────────────── -->
        <div class="cs-panel" id="cs-panel-hide-login">
            <div class="cs-section-header cs-section-header-purple">
                <span>🔒 HIDE LOGIN URL</span>
                <span class="cs-header-hint"><?php esc_html_e( 'Move wp-login.php to a secret address', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'hide-login',
                    'Hide Login URL',
                    array(
						array(
							'name' => 'Enable Hide Login',
							'rec'  => 'Critical',
							'html' => 'Moves your login page to a secret URL. Direct requests to <code>/wp-login.php</code> immediately return a 404 and the hit is recorded in the Blocked Probes chart. This stops the vast majority of automated credential-stuffing bots, they probe the default path and give up when they get a 404.<br><br>If your Failed Login Attempts chart shows active events, enabling this is the single most effective first step.',
						),
						array(
							'name' => 'Custom Login Path',
							'rec'  => 'Recommended',
							'html' => 'The URL slug that serves your login page, e.g. <code>/my-secret-login</code>. Use letters, numbers, and hyphens only. The Randomise button generates a cryptographically random 16-character slug.<br><br><strong>⚠️ Save the full login URL somewhere safe before saving</strong>, if you forget it you will need SSH access to recover.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p class="cs-login-desc"><?php esc_html_e( 'Disables direct access to wp-login.php and serves your login page at a custom URL. Bots and scanners that probe /wp-login.php will get a 404.', 'cloudscale-cyber-devtools' ); ?></p>

                <div class="cs-toggle-row">
                    <label class="cs-toggle-label">
                        <input type="checkbox" id="cs-hide-enabled" <?php checked( $hide_on ); ?>>
                        <span class="cs-toggle-switch"></span>
                        <span class="cs-toggle-text"><?php esc_html_e( 'Enable Hide Login', 'cloudscale-cyber-devtools' ); ?></span>
                    </label>
                </div>

                <div class="cs-field-row" style="margin-top:16px">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-login-slug"><?php esc_html_e( 'Custom Login Path:', 'cloudscale-cyber-devtools' ); ?></label>
                        <div class="cs-slug-row">
                            <span class="cs-slug-base"><?php echo esc_html( trailingslashit( home_url() ) ); ?></span>
                            <input type="password" id="cs-login-slug" class="cs-input cs-slug-input"
                                    value="<?php echo esc_attr( $slug ); ?>"
                                    placeholder="my-secret-login"
                                    maxlength="60" autocomplete="new-password" spellcheck="false">
                            <button type="button" id="cs-login-slug-show" style="padding:5px 10px;background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;cursor:pointer;font-size:13px;white-space:nowrap;">👁 <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                            <button type="button" id="cs-login-slug-random" title="<?php esc_attr_e( 'Generate a random, unguessable login path', 'cloudscale-cyber-devtools' ); ?>" style="padding:5px 10px;background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;cursor:pointer;font-size:13px;white-space:nowrap;">🎲 <?php esc_html_e( 'Randomise', 'cloudscale-cyber-devtools' ); ?></button>
                        </div>
                        <span class="cs-hint"><?php esc_html_e( 'Letters, numbers, and hyphens only. Save this URL, you will need it to log in.', 'cloudscale-cyber-devtools' ); ?></span>
                        <span id="cs-slug-weak-warn" style="display:none;margin-top:4px;font-size:12px;font-weight:600;color:#92400e;">⚠ <?php esc_html_e( 'This looks guessable. Use the Randomise button for a secure path.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <?php
                $masked_login_url = str_repeat( '•', 32 );
                ?>
                <div class="cs-login-current-url" style="margin-top:14px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                    <span class="cs-label" style="display:inline"><?php esc_html_e( 'Current Login URL:', 'cloudscale-cyber-devtools' ); ?></span>
                    <code id="cs-current-login-url-display" style="font-size:13px;color:#1e6fd9;" data-real="<?php echo esc_attr( $current_url ); ?>" data-masked="<?php echo esc_attr( $masked_login_url ); ?>"><?php echo esc_html( $masked_login_url ); ?></code>
                    <button type="button" id="cs-login-url-show" style="padding:3px 10px;background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;cursor:pointer;font-size:12px;">👁 <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                    <a id="cs-current-login-url-open" href="<?php echo esc_url( $current_url ); ?>" target="_blank" style="font-size:12px;color:#6366f1;font-weight:600;text-decoration:none;"><?php esc_html_e( 'Open →', 'cloudscale-cyber-devtools' ); ?></a>
                </div>

                <div style="margin-top:18px;display:flex;align-items:center;gap:10px">
                    <button type="button" class="cs-btn-primary" id="cs-hide-save">💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                    <span class="cs-settings-saved" id="cs-hide-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
            </div>
        </div>

        <!-- ── Session Duration ──────────────────────── -->
        <?php
        $session_duration = get_option( 'csdt_devtools_session_duration', 'default' );
        $duration_options = array(
            'default' => __( 'WordPress default (2 days / 14 days with Remember Me)', 'cloudscale-cyber-devtools' ),
            '1'       => __( '1 day', 'cloudscale-cyber-devtools' ),
            '7'       => __( '7 days', 'cloudscale-cyber-devtools' ),
            '14'      => __( '14 days', 'cloudscale-cyber-devtools' ),
            '30'      => __( '30 days', 'cloudscale-cyber-devtools' ),
            '90'      => __( '90 days', 'cloudscale-cyber-devtools' ),
            '365'     => __( '1 year', 'cloudscale-cyber-devtools' ),
        );
        ?>
        <div class="cs-panel" id="cs-panel-session">
            <div class="cs-section-header cs-section-header-blue">
                <span>⏱ SESSION DURATION</span>
                <span class="cs-header-hint"><?php esc_html_e( 'How long login sessions stay valid', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'session-duration',
                    'Session Duration',
                    array(
						array(
							'name' => 'Session Lifetime',
							'rec'  => 'Recommended',
							'html' => 'Sets how long the WordPress auth cookie stays valid before the user must log in again.<br><br><ul><li><strong>1 to 7 days</strong>, higher-security environments (banking, staging, admin-heavy sites)</li><li><strong>30 to 90 days</strong>, convenience for trusted personal devices</li><li><strong>WordPress default</strong>, 2 days (48 hours), or 14 days when "Remember Me" is checked at login</li></ul>',
						),
						array(
							'name' => 'Remember Me & timing',
							'rec'  => 'Note',
							'html' => 'When a custom duration is set, the <strong>Remember Me</strong> checkbox is overridden, all new sessions get the same lifetime regardless.<br><br>Changing this setting only affects <em>new</em> logins. Users already logged in keep their current session cookie until it expires or they log out.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 16px;"><?php esc_html_e( 'Controls how long WordPress keeps you logged in before requiring a password again. A shorter session reduces exposure if a device is left unattended or a session cookie is stolen.', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-session-duration"><?php esc_html_e( 'Session expires after:', 'cloudscale-cyber-devtools' ); ?></label>
                        <select id="cs-session-duration" class="cs-input" style="max-width:360px">
                            <?php foreach ( $duration_options as $val => $label ) : ?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $session_duration, (string) $val ); ?>><?php echo esc_html( $label ); ?></option>
                            <?php endforeach; ?>
                        </select>
                        <span class="cs-hint"><?php esc_html_e( 'Applies from the next login. Existing sessions are not affected.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
                <div style="margin-top:18px;display:flex;align-items:center;gap:10px">
                    <button type="button" class="cs-btn-primary" id="cs-session-save">💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                    <span class="cs-settings-saved" id="cs-session-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
            </div>
        </div>

        <!-- ── Brute-Force Protection ───────────────── -->
        <?php
        $bf_enabled                   = get_option( 'csdt_devtools_brute_force_enabled', '1' );
        $bf_attempts                  = get_option( 'csdt_devtools_brute_force_attempts', '5' );
        $bf_lockout                   = get_option( 'csdt_devtools_brute_force_lockout', '10' );
        $bf_enum_protect              = get_option( 'csdt_devtools_enum_protect', '1' );
        $bf_auto_block_threshold      = (int) get_option( 'csdt_devtools_bf_auto_block_threshold', '10' );
        $wplogin_auto_block_threshold = (int) get_option( 'csdt_wplogin_auto_block_threshold', '0' );
        $tg_login_valid               = get_option( 'csdt_telegram_login_valid_user', '0' );
        $tg_login_invalid             = get_option( 'csdt_telegram_login_invalid_user', '0' );
        $tg_configured                = CloudScale_Telegram::is_configured();
        $honeypot_2fa_enabled         = get_option( 'csdt_honeypot_2fa_enabled', '0' );
        $wplogin_stats                = get_option( 'csdt_wplogin_blocked_stats', array() );
        ?>
        <!-- ── Rename WordPress User ─────────────────────────────────── -->
        <div class="cs-panel" id="cs-panel-rename-user">
            <div class="cs-section-header" style="background:linear-gradient(135deg,#1e3a5f 0%,#0f2942 100%)">
                <span>✏️ <?php esc_html_e( 'RENAME WP USER', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Change the WordPress username of any account, use after a compromise', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'rename-wp-user',
                    'Rename WP User',
                    array(
						array(
							'name' => 'What it does',
							'rec'  => 'Info',
							'html' => 'Updates the <code>user_login</code> column in <code>wp_users</code> directly. The display name and nicename are also updated to match. All active sessions for the renamed account are immediately invalidated, the user must log in again with the new username.',
						),
						array(
							'name' => 'When to use it',
							'rec'  => 'Critical',
							'html' => 'Use immediately after a compromise or credential leak. Changing the username invalidates any saved credentials an attacker may hold, and forces all active sessions to expire. Pair this with a forced password reset for maximum effect.',
						),
						array(
							'name' => 'Safe to use',
							'rec'  => 'Info',
							'html' => 'Works for any WordPress account, not just administrators. The rename is instant and reversible (just rename it back). It does <strong>not</strong> change the user\'s email, password, or role.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-rename-current"><?php esc_html_e( 'Current username:', 'cloudscale-cyber-devtools' ); ?></label>
                        <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
                            <select id="cs-rename-current" class="cs-input" style="max-width:220px;">
                                <option value=""><?php esc_html_e( ',  select user , ', 'cloudscale-cyber-devtools' ); ?></option>
                                <?php
                                foreach ( get_users(
                                    array(
										'orderby' => 'login',
										'order'   => 'ASC',
										'number'  => -1,
                                    )
                                ) as $wp_u ) :
									?>
                                    <option value="<?php echo esc_attr( $wp_u->user_login ); ?>">
													<?php echo esc_html( $wp_u->user_login . ' (' . implode( ', ', $wp_u->roles ) . ')' ); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                            <span style="font-size:12px;color:#94a3b8;"><?php esc_html_e( '→', 'cloudscale-cyber-devtools' ); ?></span>
                            <input type="text" id="cs-rename-new" class="cs-input" placeholder="new-username"
                                    maxlength="60" autocomplete="off" spellcheck="false" style="max-width:220px;">
                            <button type="button" class="cs-btn-primary" id="cs-rename-submit">✏️ <?php esc_html_e( 'Rename', 'cloudscale-cyber-devtools' ); ?></button>
                        </div>
                        <span class="cs-hint" style="margin-top:6px;display:block;"><?php esc_html_e( 'Changes the wp_users.user_login column directly. All active sessions for the account are terminated immediately. Works for any WordPress account, not just test accounts.', 'cloudscale-cyber-devtools' ); ?></span>
                        <div id="cs-rename-msg" style="display:none;margin-top:8px;font-size:13px;"></div>
                    </div>
                </div>
            </div>
        </div>
        <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript ?>
        <script>
        (function() {
            var submitBtn = document.getElementById('cs-rename-submit');
            if (!submitBtn) return;
            submitBtn.addEventListener('click', function() {
                var current = (document.getElementById('cs-rename-current') || {}).value.trim();
                var newName = (document.getElementById('cs-rename-new') || {}).value.trim();
                var msgEl   = document.getElementById('cs-rename-msg');
                if (!current || !newName) { if (msgEl) { msgEl.style.cssText = 'display:block;color:#dc2626;'; msgEl.textContent = 'Both fields are required.'; } return; }
                if (newName === current) { if (msgEl) { msgEl.style.cssText = 'display:block;color:#dc2626;'; msgEl.textContent = 'New username is the same as current.'; } return; }
                submitBtn.disabled = true; submitBtn.textContent = '⏳';
                var fd = new FormData();
                fd.append('action', 'csdt_rename_wp_user');
                fd.append('nonce',  <?php echo wp_json_encode( wp_create_nonce( self::LOGIN_NONCE ) ); ?>);
                fd.append('current_login', current);
                fd.append('new_login',     newName);
                fetch(<?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>, { method: 'POST', body: fd })
                    .then(function(r) { return r.json(); })
                    .then(function(res) {
                        submitBtn.disabled = false; submitBtn.textContent = '✏️ Rename';
                        if (res.success) {
                            if (msgEl) { msgEl.style.cssText = 'display:block;color:#166534;'; msgEl.textContent = '✓ Renamed "' + current + '" → "' + newName + '". All sessions terminated.'; }
                            var sel = document.getElementById('cs-rename-current');
                            var opt = sel ? sel.querySelector('option[value="' + current.replace(/"/g, '\\"') + '"]') : null;
                            if (opt) { opt.value = newName; opt.textContent = opt.textContent.replace(current, newName); }
                            if (sel) sel.value = '';
                            document.getElementById('cs-rename-new').value = '';
                        } else {
                            if (msgEl) { msgEl.style.cssText = 'display:block;color:#dc2626;'; msgEl.textContent = '✗ ' + (res.data || 'Error'); }
                        }
                    })
                    .catch(function() { submitBtn.disabled = false; submitBtn.textContent = '✏️ Rename'; });
            });
        }());
        </script>

        <div class="cs-panel" id="cs-panel-brute-force">
            <div class="cs-section-header cs-section-header-red">
                <span>🔒 BRUTE-FORCE PROTECTION</span>
                <span class="cs-header-hint"><?php esc_html_e( 'Temporarily lock accounts after repeated failed logins', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'brute-force',
                    'Brute-Force Protection',
                    array(
						array(
							'name' => 'How it works',
							'rec'  => 'Info',
							'html' => 'After <em>N</em> consecutive failed login attempts for the same username, the account is locked for the configured duration. The lock is <strong>per-username, not per-IP</strong>, distributed attacks hitting the same account from thousands of IPs are still caught. The counter resets automatically when the lockout expires.',
						),
						array(
							'name' => 'Failed attempts',
							'rec'  => 'Recommended',
							'html' => '<ul><li><code>3</code>, tightest, but may lock out users who mistype their password twice</li><li><code>5</code>, default, good balance for most sites</li><li><code>10</code>, more forgiving for sites with non-technical users</li></ul><br><strong>Unlock one account via SSH:</strong><br><code>wp transient delete csdt_devtools_bf_lock_$(php -r "echo md5(strtolower(\'USERNAME\'));") --path=/var/www/html</code><br><br><strong>Unlock all locked accounts:</strong><br><code>wp eval \'global $wpdb; $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE \"%csdt_devtools_bf_lock%\"");\' --path=/var/www/html</code>',
						),
						array(
							'name' => 'Lockout period',
							'rec'  => 'Recommended',
							'html' => 'Default is <code>10</code> minutes. The lock lifts automatically, no admin action needed.<br><br><ul><li><strong>10 min</strong>, default, stops automated attacks cold</li><li><strong>30 to 60 min</strong>, better for targeted attacks, small UX cost for forgotten-password users</li></ul>',
						),
						array(
							'name' => 'Account enumeration',
							'rec'  => 'Critical',
							'html' => '<p>WordPress by default reveals whether a username exists: wrong username → <em>"The username xyz is not registered."</em>, right username wrong password → <em>"The password is incorrect."</em> An attacker can automate this to map every account on your site in minutes.</p><p>Enable this to make both failures return the same generic message. The <strong>✓ Active</strong> badge appears next to the checkbox when it is on. Legitimate users who forget their username can still recover via <em>Lost your password?</em> using their email.</p>',
						),
						array(
							'name' => 'Attack Origins map',
							'rec'  => 'Info',
							'html' => 'A world map shows the country of origin for failed logins (amber circles), wp-login.php blocked probes (red circles), and Test Session API attacks (purple circles). Each type uses its own independent scale so low-volume countries still appear.<br><br>Country is resolved from the Cloudflare CF-IPCountry header first (zero overhead). If CF is absent, the DB-IP Lite database (~30 MB local file) is used as a fallback, download it using the button below the map. Auto-update keeps it current monthly. Data accumulates from new events.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 16px;"><?php esc_html_e( 'Locks a username after too many consecutive failed login attempts, stopping automated password-guessing attacks. Lockouts are per-username, not per IP, so legitimate users on shared networks are never collateral damage.', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label">
                            <input type="checkbox" id="cs-bf-enabled" <?php checked( $bf_enabled, '1' ); ?>>
                            <?php esc_html_e( 'Enable brute-force account lockout', 'cloudscale-cyber-devtools' ); ?>
                        </label>
                        <span class="cs-hint"><?php esc_html_e( 'Locks the account after too many failed login attempts.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
                <div class="cs-field-row" id="cs-bf-options">
                    <div class="cs-field" style="margin-right:32px">
                        <label class="cs-label" for="cs-bf-attempts"><?php esc_html_e( 'Failed attempts before lockout:', 'cloudscale-cyber-devtools' ); ?></label>
                        <input type="number" id="cs-bf-attempts" class="cs-input" min="1" max="100"
                                value="<?php echo esc_attr( $bf_attempts ); ?>" style="max-width:100px">
                        <span class="cs-hint"><?php esc_html_e( 'Consecutive failures for the same username. Default: 5.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                    <div class="cs-field">
                        <label class="cs-label" for="cs-bf-lockout"><?php esc_html_e( 'Lockout duration (minutes):', 'cloudscale-cyber-devtools' ); ?></label>
                        <input type="number" id="cs-bf-lockout" class="cs-input" min="1" max="1440"
                                value="<?php echo esc_attr( $bf_lockout ); ?>" style="max-width:100px">
                        <span class="cs-hint"><?php esc_html_e( 'How long the account stays locked. Default: 10.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
                <div class="cs-field-row" style="margin-top:14px;">
                    <div class="cs-field">
                        <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;font-weight:500;color:#334155;">
                            <input type="checkbox" id="cs-bf-enum-protect" <?php checked( $bf_enum_protect, '1' ); ?>>
                            <?php esc_html_e( 'Prevent account enumeration by using generic login error messages', 'cloudscale-cyber-devtools' ); ?>
                            <?php if ( $bf_enum_protect === '1' ) : ?>
                            <span style="font-size:10px;font-weight:700;padding:2px 7px;border-radius:10px;background:#dcfce7;color:#166534;border:1px solid #86efac;white-space:nowrap;">✓ Active</span>
                            <?php endif; ?>
                        </label>
                        <span class="cs-hint" style="margin-top:4px;display:block;"><?php esc_html_e( 'Returns "Invalid username or password." for all credential failures, prevents attackers from discovering which usernames are registered on this site.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
                <!-- Honeypot 2FA -->
                <div class="cs-field-row" style="margin-top:18px;">
                    <div class="cs-field">
                        <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;font-weight:500;color:#334155;">
                            <input type="checkbox" id="cs-honeypot-2fa" <?php checked( $honeypot_2fa_enabled, '1' ); ?>>
                            <?php esc_html_e( 'Show fake 2FA screen on invalid login (honeypot)', 'cloudscale-cyber-devtools' ); ?>
                            <span style="font-size:10px;font-weight:700;padding:2px 7px;border-radius:10px;background:#fef3c7;color:#92400e;border:1px solid #fcd34d;">Deception</span>
                        </label>
                        <span class="cs-hint" style="margin-top:4px;display:block;"><?php esc_html_e( 'When enabled, any failed login attempt redirects to a fake 6-digit PIN screen. Whatever they enter silently fails and shows a dark "you are being watched" page. Valid users with 2FA configured are unaffected, they complete real 2FA before ever triggering this.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <!-- Auto IP block (brute-force) -->
                <div class="cs-field-row" style="margin-top:18px;">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-bf-auto-block"><?php esc_html_e( 'Auto-block IP after N login failures / hour:', 'cloudscale-cyber-devtools' ); ?></label>
                        <input type="number" id="cs-bf-auto-block" class="cs-input" min="0" max="1000"
                                value="<?php echo esc_attr( $bf_auto_block_threshold ); ?>" style="max-width:100px">
                        <span class="cs-hint"><?php esc_html_e( 'Permanently blocks the IP after this many failed credential submissions within 1 hour. Set to 0 to disable. Default: 10.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <!-- Auto IP block (wp-login probes) -->
                <div class="cs-field-row" style="margin-top:12px;">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-wplogin-auto-block"><?php esc_html_e( 'Auto-block IP after N wp-login.php probes / 7 days:', 'cloudscale-cyber-devtools' ); ?></label>
                        <select id="cs-wplogin-auto-block" class="cs-input" style="max-width:160px;">
                            <?php
                            foreach ( array(
								0   => __( 'Off', 'cloudscale-cyber-devtools' ),
								20  => '20',
								50  => '50',
								100 => '100',
								200 => '200',
							) as $val => $label ) :
								?>
                            <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $wplogin_auto_block_threshold, $val ); ?>><?php echo esc_html( $label ); ?></option>
                            <?php endforeach; ?>
                        </select>
                        <span class="cs-hint"><?php esc_html_e( 'Permanently blocks the IP after this many wp-login.php probe hits in the rolling 7-day window. These are URL probes, not credential attempts. Default: Off.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <!-- Telegram login alerts -->
                <div class="cs-field-row" style="margin-top:18px;border-top:1px solid #e2e8f0;padding-top:16px;">
                    <div class="cs-field">
                        <div style="font-size:12px;font-weight:700;color:#374151;margin-bottom:8px;text-transform:uppercase;letter-spacing:.04em;">
                            &#9992; <?php esc_html_e( 'Telegram Login Alerts', 'cloudscale-cyber-devtools' ); ?>
                            <?php if ( ! $tg_configured ) : ?>
                            <span style="font-size:10px;font-weight:600;padding:2px 7px;border-radius:10px;background:#fef3c7;color:#92400e;border:1px solid #fcd34d;margin-left:6px;">
                                &#9888; <?php esc_html_e( 'Telegram not configured', 'cloudscale-cyber-devtools' ); ?>
                            </span>
                            <?php endif; ?>
                        </div>
                        <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;font-weight:500;color:#334155;margin-bottom:6px;">
                            <input type="checkbox" id="cs-tg-login-valid" <?php checked( $tg_login_valid, '1' ); ?>>
                            <?php esc_html_e( 'Alert on failed login for a valid (known) username', 'cloudscale-cyber-devtools' ); ?>
                            <span style="font-size:10px;font-weight:700;padding:2px 7px;border-radius:10px;background:#fef2f2;color:#dc2626;border:1px solid #fca5a5;"><?php esc_html_e( 'High priority', 'cloudscale-cyber-devtools' ); ?></span>
                        </label>
                        <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;font-weight:500;color:#334155;">
                            <input type="checkbox" id="cs-tg-login-invalid" <?php checked( $tg_login_invalid, '1' ); ?>>
                            <?php esc_html_e( 'Alert on failed login for an unknown username', 'cloudscale-cyber-devtools' ); ?>
                            <span style="font-size:10px;font-weight:700;padding:2px 7px;border-radius:10px;background:#fefce8;color:#854d0e;border:1px solid #fde047;"><?php esc_html_e( 'Default priority', 'cloudscale-cyber-devtools' ); ?></span>
                        </label>
                        <span class="cs-hint" style="margin-top:6px;display:block;"><?php esc_html_e( 'Security control changes (hide login off, brute-force off, 2FA off) always send a Telegram alert regardless of these settings.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <div style="position:sticky;bottom:0;z-index:20;background:#fff;border-top:2px solid #e2e8f0;margin-top:24px;padding:14px 0 10px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
                    <button type="button" class="cs-btn-primary" id="cs-bf-save">💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                    <span class="cs-settings-saved" id="cs-bf-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                    <button type="button" id="cs-bf-test-btn" style="background:#fef3c7;color:#92400e;border:1px solid #fcd34d;border-radius:6px;padding:6px 14px;font-size:13px;font-weight:600;cursor:pointer;">🧪 <?php esc_html_e( 'Test Brute-Force Protection', 'cloudscale-cyber-devtools' ); ?></button>
                    <span id="cs-bf-test-result" style="display:none;font-size:13px;font-weight:600;padding:5px 12px;border-radius:6px;"></span>
                </div>

                <div style="margin-top:10px;font-size:12px;color:#374151;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:8px 12px;line-height:1.7;">
                    🔒 <strong><?php esc_html_e( 'Account lock, not IP lock:', 'cloudscale-cyber-devtools' ); ?></strong>
                    <?php esc_html_e( 'Lockout is per-username. Distributed attacks using many IPs against the same account are still caught.', 'cloudscale-cyber-devtools' ); ?>
                </div>

            </div><!-- /cs-panel-body brute-force -->
        </div><!-- /cs-panel brute-force -->

        <!-- ── Attack Monitor ───────────────────────────── -->
        <div class="cs-panel" id="cs-panel-attack-monitor">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#1e3a5f 0%,#0f2942 100%);border-left:3px solid #ef4444;">
                <span>📊 <?php esc_html_e( 'ATTACK MONITOR', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Live threat charts — login attempts, probes, API attacks and blocked IPs', 'cloudscale-cyber-devtools' ); ?></span>
            </div>
            <div class="cs-panel-body">

                <div id="cs-bf-log-wrap" class="cs-bf-log-wrap" style="margin-top:0;border-top:none;padding-top:0;">
                    <div class="cs-bf-log-header">
                        <span class="cs-bf-log-title">📊 <?php esc_html_e( 'Failed Login Attempts - Last 14 Days', 'cloudscale-cyber-devtools' ); ?></span>
                        <span id="cs-bf-log-total" class="cs-bf-log-total"></span>
                        <?php
                        self::render_explain_btn(
                            'bf-failed-logins',
                            'Failed Login Attempts',
                            array(
								array(
									'name' => 'What this shows',
									'rec'  => 'Overview',
									'desc' => 'Every time a bot or person submits a username and password on your login form and gets it wrong, it is counted here. These are real credential attempts — the login page was reached and a form was submitted.',
								),
								array(
									'name' => 'Risk level',
									'rec'  => 'High',
									'desc' => 'A high and sustained count means attackers are actively trying to guess your credentials. If they succeed, they get full access to your site. Brute-force protection locks out an IP after repeated failures to slow this down.',
								),
								array(
									'name' => 'Best defence',
									'rec'  => 'Recommended',
									'html' => 'Enable <strong>Hide Login URL</strong> — bots never find the form, so they can\'t submit credentials at all. Combined with brute-force lockout and 2FA, this reduces successful attacks to near zero.',
								),
							),
							'',
							true
                        );
                        ?>
                    </div>
                    <div class="cs-bf-layout">
                        <div id="cs-bf-chart" class="cs-bf-chart"></div>
                        <div id="cs-bf-table-wrap" class="cs-bf-table-wrap">
                            <div class="cs-bf-loading"><?php esc_html_e( 'Loading…', 'cloudscale-cyber-devtools' ); ?></div>
                        </div>
                    </div>
                </div>

                <?php
                // ── wp-login.php blocked hits, chart + probe IP table ────────
                $ip_blocklist = get_option( 'csdt_ip_blocklist', array() );
                if ( ! is_array( $ip_blocklist ) ) {
					$ip_blocklist = array(); }
                $daily_hits = isset( $wplogin_stats['daily'] ) && is_array( $wplogin_stats['daily'] ) ? $wplogin_stats['daily'] : array();
                $today      = gmdate( 'Y-m-d' );
                $days       = array();
                for ( $i = 6; $i >= 0; $i-- ) {
                    $d          = gmdate( 'Y-m-d', strtotime( "-{$i} days" ) );
                    $days[ $d ] = $daily_hits[ $d ] ?? 0;
                }
                $total_hits   = array_sum( $days );
                $last_hit_ts  = $wplogin_stats['last_ts'] ?? 0;
                $last_hit_ip  = $wplogin_stats['last_ip'] ?? '';
                $day_max      = max( 1, max( array_values( $days ) ) );
                $day_mid      = (int) round( $day_max / 2 );
                $ip_stats_raw = isset( $wplogin_stats['ip_stats'] ) && is_array( $wplogin_stats['ip_stats'] ) ? $wplogin_stats['ip_stats'] : array();
                uasort( $ip_stats_raw, fn( $a, $b ) => $b['last_ts'] - $a['last_ts'] );
                $probe_recent = array_slice( $ip_stats_raw, 0, 50, true );
                ?>
                <div style="margin-top:22px;border-top:1px solid #e8edf5;padding-top:20px;">
                    <div class="cs-bf-log-header">
                        <span class="cs-bf-log-title">🚫 <?php esc_html_e( 'wp-login.php Blocked, Last 7 Days', 'cloudscale-cyber-devtools' ); ?></span>
                        <span class="cs-bf-log-total"><?php echo (int) $total_hits; ?> blocked</span>
                        <?php
                        self::render_explain_btn(
                            'wplogin-blocked',
                            'wp-login.php Blocked',
                            array(
								array(
									'name' => 'What this shows',
									'rec'  => 'Overview',
									'desc' => 'Bots and scanners probing for the default WordPress login URL (/wp-login.php and /wp-admin). These requests were intercepted before the login form ever loaded — no username or password was submitted. They are URL probes, not login attempts.',
								),
								array(
									'name' => 'Risk level',
									'rec'  => 'Note',
									'desc' => 'Low on its own. These bots are searching for the login page; since Hide Login is active they never find it. A high count is normal and expected — automated scanners hit every WordPress site on the internet constantly.',
								),
								array(
									'name' => 'IP table',
									'rec'  => 'Info',
									'desc' => 'Shows which IPs are probing most frequently. You can manually block persistent offenders, but since they never reach the login form the practical risk of not blocking them is minimal.',
								),
							),
							'',
							true
                        );
                        ?>
                    </div>
                    <div class="cs-bf-layout">
                        <div class="cs-bf-chart">
                            <div class="cs-bf-yaxis">
                                <span class="cs-bf-ytick"><?php echo esc_html( number_format( $day_max ) ); ?></span>
                                <span class="cs-bf-ytick"><?php echo esc_html( number_format( $day_mid ) ); ?></span>
                                <span class="cs-bf-ytick">0</span>
                            </div>
                            <?php
                            foreach ( $days as $d => $cnt ) :
                                $pct     = $cnt > 0 ? max( 2, (int) round( $cnt / $day_max * 100 ) ) : 0;
                                $cls     = $cnt === 0 ? ' cs-bf-bar-zero' : ( $cnt >= $day_max * 0.75 ? ' cs-bf-bar-high' : ( $cnt >= $day_max * 0.4 ? ' cs-bf-bar-mid' : '' ) );
                                $lbl_clr = $cnt === 0 ? '#16a34a' : '#64748b';
								?>
                            <div class="cs-bf-day" style="flex:1;min-width:28px;">
                                <div class="cs-bf-bar-track" style="position:relative;">
                                    <span style="position:absolute;top:-15px;left:50%;transform:translateX(-50%);font-size:9px;font-weight:700;color:<?php echo esc_attr( $lbl_clr ); ?>;white-space:nowrap;"><?php echo esc_html( number_format( $cnt ) ); ?></span>
                                    <div class="cs-bf-bar<?php echo esc_attr( $cls ); ?>"
                                        style="height:<?php echo (int) $pct; ?>%;"
                                        title="<?php echo esc_attr( number_format( $cnt ) . ' blocked on ' . gmdate( 'M j', strtotime( $d ) ) ); ?>"></div>
                                </div>
                                <div class="cs-bf-day-label"><?php echo esc_html( gmdate( 'M j', strtotime( $d ) ) ); ?></div>
                            </div>
                            <?php endforeach; ?>
                        </div>
                        <div class="cs-bf-table-wrap">
                            <?php if ( ! empty( $probe_recent ) ) : ?>
                            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;flex-wrap:wrap;gap:4px;">
                                <span style="font-size:11px;color:#64748b;">
                                <?php
                                /* translators: %d: number of unique source IPs */
                                echo esc_html( sprintf( _n( '%d IP', '%d IPs', count( $probe_recent ), 'cloudscale-cyber-devtools' ), count( $probe_recent ) ) );
                                ?>
                                </span>
                                <div style="display:flex;gap:4px;">
                                    <button type="button" id="cs-probe-sort-ts"
                                            style="font-size:10px;padding:2px 8px;border-radius:4px;border:1px solid #3b82f6;background:#3b82f6;color:#fff;cursor:pointer;font-weight:600;">
                                        Last attempt ↓
                                    </button>
                                    <button type="button" id="cs-probe-sort-cnt"
                                            style="font-size:10px;padding:2px 8px;border-radius:4px;border:1px solid #cbd5e1;background:#f8fafc;color:#475569;cursor:pointer;">
                                        Count ↓
                                    </button>
                                </div>
                            </div>
                            <table class="cs-bf-table" id="cs-probe-table">
                                <thead>
                                    <tr>
                                        <th class="cs-bf-th"><?php esc_html_e( 'Last attempt', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"><?php esc_html_e( 'IP / Country', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th" style="text-align:right;"><?php esc_html_e( 'Attempts', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"></th>
                                    </tr>
                                </thead>
                                <tbody id="cs-probe-tbody">
                                <?php
                                foreach ( $probe_recent as $probe_ip => $probe_data ) :
                                    $probe_count = array_sum( $probe_data['days'] ?? array() );
                                    $is_blocked  = isset( $ip_blocklist[ $probe_ip ] );
									?>
                                    <tr data-ts="<?php echo (int) $probe_data['last_ts']; ?>" data-cnt="<?php echo (int) $probe_count; ?>" data-ip="<?php echo esc_attr( $probe_ip ); ?>">
                                        <td class="cs-bf-td cs-bf-td-time">
                                        <?php
                                            $ts_diff = (int) ( time() - $probe_data['last_ts'] );
										if ( $ts_diff < 60 ) {
											echo esc_html( $ts_diff . ' secs ago' );
										} elseif ( $ts_diff < 3600 ) {
											echo esc_html( (int) floor( $ts_diff / 60 ) . ' min ago' );
										} elseif ( $ts_diff < 86400 ) {
											echo esc_html( (int) floor( $ts_diff / 3600 ) . 'h ago' );
										} else {
											echo esc_html( (int) floor( $ts_diff / 86400 ) . 'd ago' );
										}
                                        ?>
                                        </td>
                                        <td class="cs-bf-td cs-bf-td-ip"><?php echo esc_html( $probe_ip ); ?>
                                            <?php
                                            if ( ! empty( $probe_data['country'] ) ) :
                                                $probe_cc   = strtoupper( $probe_data['country'] );
                                                $probe_flag = mb_chr( 0x1F1E6 + ord( $probe_cc[0] ) - 65 ) . mb_chr( 0x1F1E6 + ord( $probe_cc[1] ) - 65 );
												?>
                                            <div style="font-size:10px;color:#64748b;margin-top:2px;"><?php echo esc_html( $probe_flag . ' ' . $probe_cc ); ?></div>
                                            <?php endif; ?>
                                        </td>
                                        <td class="cs-bf-td" style="text-align:right;font-weight:700;color:#dc2626;"><?php echo number_format( (int) $probe_count ); ?></td>
                                        <td class="cs-bf-td" style="white-space:nowrap;text-align:right;">
                                            <a href="https://ipinfo.io/<?php echo esc_attr( $probe_ip ); ?>" target="_blank" rel="noopener"
                                                style="font-size:10px;padding:2px 6px;border:1px solid #cbd5e1;border-radius:4px;background:#f8fafc;color:#475569;text-decoration:none;margin-right:4px;">Whois</a>
                                            <?php if ( $is_blocked ) : ?>
                                            <span style="font-size:10px;padding:2px 6px;border:1px solid #86efac;border-radius:4px;background:#dcfce7;color:#15803d;font-weight:600;">🚫 Blocked</span>
                                            <?php else : ?>
                                            <button type="button" class="cs-ip-block-btn"
                                                    data-ip="<?php echo esc_attr( $probe_ip ); ?>"
                                                    style="font-size:10px;padding:2px 6px;border:1px solid #fca5a5;border-radius:4px;background:#fef2f2;color:#dc2626;cursor:pointer;font-weight:600;">Block</button>
                                            <?php endif; ?>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                            <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- inline sort handler; wp_add_inline_script not available at this render point ?>
                            <script>
                            (function(){
                                var btnTs  = document.getElementById('cs-probe-sort-ts');
                                var btnCnt = document.getElementById('cs-probe-sort-cnt');
                                var tbody  = document.getElementById('cs-probe-tbody');
                                if (!btnTs || !btnCnt || !tbody) return;

                                function styleActive(active, inactive) {
                                    active.style.background   = '#3b82f6';
                                    active.style.color        = '#fff';
                                    active.style.borderColor  = '#3b82f6';
                                    inactive.style.background = '#f8fafc';
                                    inactive.style.color      = '#475569';
                                    inactive.style.borderColor = '#cbd5e1';
                                }

                                function sortBy(attr) {
                                    var rows = Array.from(tbody.querySelectorAll('tr'));
                                    rows.sort(function(a, b) {
                                        return parseInt(b.getAttribute(attr), 10) - parseInt(a.getAttribute(attr), 10);
                                    });
                                    rows.forEach(function(r) { tbody.appendChild(r); });
                                }

                                btnTs.addEventListener('click', function() {
                                    sortBy('data-ts');
                                    styleActive(btnTs, btnCnt);
                                });
                                btnCnt.addEventListener('click', function() {
                                    sortBy('data-cnt');
                                    styleActive(btnCnt, btnTs);
                                });
                            })();
                            </script>
                            <?php else : ?>
                            <div class="cs-bf-empty"><?php echo $total_hits > 0 ? esc_html__( 'Per-IP log populates as new probes arrive.', 'cloudscale-cyber-devtools' ) : esc_html__( 'No wp-login.php hits recorded yet.', 'cloudscale-cyber-devtools' ); ?></div>
                            <?php endif; ?>
                        </div>
                    </div>
                    <?php if ( $last_hit_ts > 0 ) : ?>
                    <div style="font-size:12px;color:#475569;margin-top:8px;">
                        <?php esc_html_e( 'Last attempt:', 'cloudscale-cyber-devtools' ); ?>
                        <strong><?php echo esc_html( human_time_diff( $last_hit_ts ) . ' ago' ); ?></strong>
                        <?php if ( $last_hit_ip ) : ?>
                        &nbsp;from&nbsp;<code style="font-size:11px;"><?php echo esc_html( $last_hit_ip ); ?></code>
                        <?php endif; ?>
                    </div>
                    <?php endif; ?>

                    <!-- API Attacks panel -->
                    <?php
                    $api_evts_raw = get_option( 'csdt_security_events', array() );
                    if ( ! is_array( $api_evts_raw ) ) {
						$api_evts_raw = array(); }
                    $api_evts = array_values( array_filter( $api_evts_raw, fn( $e ) => in_array( $e['type'] ?? '', array( 'api_attack', 'rest_fail' ), true ) ) );

                    // Build 7-day daily counts.
                    $api_days = array();
                    for ( $i = 6; $i >= 0; $i-- ) {
                        $d              = gmdate( 'Y-m-d', strtotime( "-{$i} days" ) );
                        $api_days[ $d ] = 0;
                    }
                    // Per-IP aggregation: last_ts, count, country.
                    $api_ip_stats = array();
                    $api_last_ts  = 0;
                    $api_last_ip  = '';
                    foreach ( $api_evts as $ae ) {
                        $ae_ts = (int) ( $ae['time'] ?? 0 );
                        $ae_d  = gmdate( 'Y-m-d', $ae_ts );
                        if ( isset( $api_days[ $ae_d ] ) ) {
							++$api_days[ $ae_d ]; }
                        $ae_ip = '';
                        if ( preg_match( '/IP:\s*([\d.a-fA-F:]+)/', $ae['detail'] ?? '', $ipm ) ) {
							$ae_ip = $ipm[1]; }
                        $ae_cc = '';
                        if ( preg_match( '/·\s*([A-Z]{2})\b/', $ae['detail'] ?? '', $ccm ) ) {
							$ae_cc = $ccm[1]; }
                        $ae_user = '';
                        if ( preg_match( '/user:([^\s·]+)/', $ae['detail'] ?? '', $um ) ) {
							$ae_user = $um[1]; }
                        if ( $ae_ip ) {
                            if ( ! isset( $api_ip_stats[ $ae_ip ] ) ) {
                                $api_ip_stats[ $ae_ip ] = array(
									'count'   => 0,
									'last_ts' => 0,
									'country' => $ae_cc,
									'users'   => array(),
								);
                            }
                            ++$api_ip_stats[ $ae_ip ]['count'];
                            if ( $ae_ts > $api_ip_stats[ $ae_ip ]['last_ts'] ) {
                                $api_ip_stats[ $ae_ip ]['last_ts'] = $ae_ts;
                            }
                            if ( $ae_cc ) {
								$api_ip_stats[ $ae_ip ]['country'] = $ae_cc; }
                            if ( $ae_user && ! in_array( $ae_user, $api_ip_stats[ $ae_ip ]['users'], true ) ) {
                                $api_ip_stats[ $ae_ip ]['users'][] = $ae_user;
                            }
                        }
                        if ( $ae_ts > $api_last_ts ) {
							$api_last_ts = $ae_ts;
							$api_last_ip = $ae_ip; }
                    }
                    $api_total   = count( $api_evts );
                    $api_day_max = max( 1, max( array_values( $api_days ) ) );
                    $api_day_mid = (int) round( $api_day_max / 2 );
                    uasort( $api_ip_stats, fn( $a, $b ) => $b['last_ts'] - $a['last_ts'] );
                    $api_ip_recent   = array_slice( $api_ip_stats, 0, 50, true );
                    $blocklist_check = get_option( 'csdt_ip_blocklist', array() );
                    ?>
                    <div style="margin-top:22px;border-top:1px solid #e8edf5;padding-top:20px;">
                        <div class="cs-bf-log-header">
                            <span class="cs-bf-log-title">🔌 <?php esc_html_e( 'API Failed Attempts, Last 7 Days', 'cloudscale-cyber-devtools' ); ?></span>
                            <span class="cs-bf-log-total"><?php echo (int) $api_total; ?> events</span>
                            <a href="<?php echo esc_url( admin_url( 'tools.php?page=cyber-devtools&tab=credentials' ) ); ?>" style="font-size:11px;color:#991b1b;font-weight:600;text-decoration:none;">↺ <?php esc_html_e( 'Rotate path token', 'cloudscale-cyber-devtools' ); ?></a>
                            <?php
                            self::render_explain_btn(
                                'api-attacks',
                                'API Failed Attempts',
                                array(
									array(
										'name' => 'What this shows',
										'rec'  => 'Overview',
										'desc' => 'Failed requests to the WordPress REST API and admin-ajax.php that were rejected by the secret path token. Bots attempting to enumerate users, scrape content, or submit credentials via the API are counted here.',
									),
									array(
										'name' => 'Risk level',
										'rec'  => 'High',
										'desc' => 'API-based credential attacks bypass the login form entirely, meaning Hide Login does not protect against them. A high count means bots are actively probing your API endpoints.',
									),
									array(
										'name' => 'Path token',
										'rec'  => 'Recommended',
										'html' => 'Rotating the secret path token immediately invalidates all ongoing API attack campaigns targeting your current endpoint. Do this if you see a sustained spike.',
									),
								),
								'',
								true
                            );
                            ?>
                        </div>
                        <?php if ( $api_total === 0 ) : ?>
                        <div class="cs-bf-empty" style="margin-top:8px;color:#15803d;">✅ <?php esc_html_e( 'No API attack attempts, the hidden path is working.', 'cloudscale-cyber-devtools' ); ?></div>
                        <?php endif; ?>
                        <div class="cs-bf-layout">
                            <div class="cs-bf-chart">
                                <div class="cs-bf-yaxis">
                                    <span class="cs-bf-ytick"><?php echo esc_html( number_format( $api_day_max ) ); ?></span>
                                    <span class="cs-bf-ytick"><?php echo esc_html( number_format( $api_day_mid ) ); ?></span>
                                    <span class="cs-bf-ytick">0</span>
                                </div>
                                <?php
                                foreach ( $api_days as $ad => $ac ) :
                                    $apct = $ac > 0 ? max( 2, (int) round( $ac / $api_day_max * 100 ) ) : 0;
                                    $acls = $ac === 0 ? ' cs-bf-bar-zero' : ( $ac >= $api_day_max * 0.75 ? ' cs-bf-bar-high' : ( $ac >= $api_day_max * 0.4 ? ' cs-bf-bar-mid' : '' ) );
                                    $alcl = $ac === 0 ? '#16a34a' : '#64748b';
									?>
                                <div class="cs-bf-day" style="flex:1;min-width:28px;">
                                    <div class="cs-bf-bar-track" style="position:relative;">
                                        <span style="position:absolute;top:-15px;left:50%;transform:translateX(-50%);font-size:9px;font-weight:700;color:<?php echo esc_attr( $alcl ); ?>;white-space:nowrap;"><?php echo esc_html( number_format( $ac ) ); ?></span>
                                        <div class="cs-bf-bar<?php echo esc_attr( $acls ); ?>"
                                            style="height:<?php echo (int) $apct; ?>%;<?php echo $ac > 0 ? 'background:#7c3aed!important;' : ''; ?>"
                                            title="<?php echo esc_attr( number_format( $ac ) . ' attempts on ' . gmdate( 'M j', strtotime( $ad ) ) ); ?>"></div>
                                    </div>
                                    <div class="cs-bf-day-label"><?php echo esc_html( gmdate( 'M j', strtotime( $ad ) ) ); ?></div>
                                </div>
                                <?php endforeach; ?>
                            </div>
                            <div class="cs-bf-table-wrap">
                                <?php if ( ! empty( $api_ip_recent ) ) : ?>
                                <table class="cs-bf-table">
                                    <thead><tr>
                                        <th class="cs-bf-th"><?php esc_html_e( 'Last attempt', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"><?php esc_html_e( 'IP / Country', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"><?php esc_html_e( 'Username(s) tried', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th" style="text-align:right;"><?php esc_html_e( 'Attempts', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"></th>
                                    </tr></thead>
                                    <tbody>
                                    <?php
                                    foreach ( $api_ip_recent as $a_ip => $a_data ) :
                                        $a_blocked = isset( $blocklist_check[ $a_ip ] );
                                        $a_cc      = strtoupper( $a_data['country'] ?? '' );
                                        $a_flag    = $a_cc && strlen( $a_cc ) === 2
                                            ? mb_chr( 0x1F1E6 + ord( $a_cc[0] ) - 65 ) . mb_chr( 0x1F1E6 + ord( $a_cc[1] ) - 65 )
                                            : '';
                                        $a_users   = $a_data['users'] ?? array();
										?>
                                    <tr data-ts="<?php echo (int) $a_data['last_ts']; ?>" data-cnt="<?php echo (int) $a_data['count']; ?>">
                                        <td class="cs-bf-td cs-bf-td-time"><?php echo $a_data['last_ts'] ? esc_html( human_time_diff( (int) $a_data['last_ts'] ) . ' ago' ) : '·'; ?></td>
                                        <td class="cs-bf-td cs-bf-td-ip"><?php echo esc_html( $a_ip ); ?>
                                            <?php if ( $a_cc ) : ?>
                                            <div style="font-size:10px;color:#64748b;margin-top:2px;"><?php echo esc_html( $a_flag . ' ' . $a_cc ); ?></div>
                                            <?php endif; ?>
                                        </td>
                                        <td class="cs-bf-td" style="font-size:11px;color:#374151;">
                                            <?php if ( ! empty( $a_users ) ) : ?>
                                                <?php foreach ( $a_users as $a_u ) : ?>
                                                <div style="font-family:monospace;<?php echo get_user_by( 'login', $a_u ) ? 'color:#dc2626;font-weight:700;' : ''; ?>" title="<?php echo get_user_by( 'login', $a_u ) ? esc_attr__( 'Valid account!', 'cloudscale-cyber-devtools' ) : ''; ?>">
                                                    <?php echo esc_html( $a_u ); ?>
                                                    <?php
                                                    if ( get_user_by( 'login', $a_u ) ) :
														?>
                                                        <span style="font-size:9px;background:#fef2f2;color:#dc2626;padding:1px 4px;border-radius:4px;border:1px solid #fca5a5;">valid</span><?php endif; ?>
                                                </div>
                                                <?php endforeach; ?>
                                            <?php else : ?>
                                            <span style="color:#94a3b8;">&middot;</span>
                                            <?php endif; ?>
                                        </td>
                                        <td class="cs-bf-td" style="text-align:right;font-weight:700;color:#7c3aed;"><?php echo number_format( (int) $a_data['count'] ); ?></td>
                                        <td class="cs-bf-td" style="white-space:nowrap;text-align:right;">
                                            <a href="https://ipinfo.io/<?php echo esc_attr( $a_ip ); ?>" target="_blank" rel="noopener"
                                                style="font-size:10px;padding:2px 6px;border:1px solid #cbd5e1;border-radius:4px;background:#f8fafc;color:#475569;text-decoration:none;margin-right:4px;">Whois</a>
                                            <?php if ( $a_blocked ) : ?>
                                            <span style="font-size:10px;padding:2px 6px;border:1px solid #86efac;border-radius:4px;background:#dcfce7;color:#15803d;font-weight:600;">🚫 Blocked</span>
                                            <?php else : ?>
                                            <button type="button" class="cs-ip-block-btn" data-ip="<?php echo esc_attr( $a_ip ); ?>"
                                                    style="font-size:10px;padding:2px 6px;border:1px solid #fca5a5;border-radius:4px;background:#fef2f2;color:#dc2626;cursor:pointer;font-weight:600;">Block</button>
                                            <?php endif; ?>
                                        </td>
                                    </tr>
                                    <?php endforeach; ?>
                                    </tbody>
                                </table>
                                <?php else : ?>
                                <div class="cs-bf-empty"><?php esc_html_e( 'Per-IP log populates as new attacks arrive.', 'cloudscale-cyber-devtools' ); ?></div>
                                <?php endif; ?>
                            </div>
                        </div>
                        <?php if ( $api_last_ts > 0 ) : ?>
                        <div style="font-size:12px;color:#475569;margin-top:8px;">
                            <?php esc_html_e( 'Last attempt:', 'cloudscale-cyber-devtools' ); ?>
                            <strong><?php echo esc_html( human_time_diff( $api_last_ts ) . ' ago' ); ?></strong>
                            <?php if ( $api_last_ip ) : ?>
                            &nbsp;from&nbsp;<code style="font-size:11px;"><?php echo esc_html( $api_last_ip ); ?></code>
                            <?php endif; ?>
                        </div>
                        <?php endif; ?>
                    </div>

                    <!-- Attack Origins map -->
                    <div style="margin-top:22px;border-top:1px solid #e8edf5;padding-top:20px;">
                        <div class="cs-bf-log-header">
                            <span class="cs-bf-log-title">🌍 <?php esc_html_e( 'Attack Origins', 'cloudscale-cyber-devtools' ); ?></span>
                            <?php
                            self::render_explain_btn(
                                'attack-origins',
                                'Attack Origins Map',
                                array(
									array(
										'name' => 'What this shows',
										'rec'  => 'Overview',
										'desc' => 'Geographic heatmap of where attacks are originating. Orange dots are login-form brute-force attempts; red dots are API attacks. Each dot represents activity from that country in the last 7 days.',
									),
									array(
										'name' => 'How to read it',
										'rec'  => 'Info',
										'desc' => 'High concentration from one country is normal — most automated attack traffic originates from a small number of hosting regions. The map is for situational awareness; blocking by country is not recommended as it can catch legitimate visitors.',
									),
								),
								'',
								true
                            );
                            ?>
                            <span style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
                                <label class="cs-map-layer-toggle" style="--dot:#f59e0b;--bg:rgba(245,158,11,0.12);--border:#f59e0b;--text:#92400e;">
                                    <input type="checkbox" id="cs-map-layer-bf" checked>
                                    <span class="cs-map-layer-dot"></span>
                                    <?php esc_html_e( 'Failed Logins', 'cloudscale-cyber-devtools' ); ?>
                                </label>
                                <label class="cs-map-layer-toggle" style="--dot:#dc2626;--bg:rgba(220,38,38,0.10);--border:#dc2626;--text:#991b1b;">
                                    <input type="checkbox" id="cs-map-layer-blocked" checked>
                                    <span class="cs-map-layer-dot"></span>
                                    <?php esc_html_e( 'Blocked Probes', 'cloudscale-cyber-devtools' ); ?>
                                </label>
                                <label class="cs-map-layer-toggle" style="--dot:#7c3aed;--bg:rgba(124,58,237,0.10);--border:#7c3aed;--text:#5b21b6;">
                                    <input type="checkbox" id="cs-map-layer-api" checked>
                                    <span class="cs-map-layer-dot"></span>
                                    <?php esc_html_e( 'API Attacks', 'cloudscale-cyber-devtools' ); ?>
                                </label>
                            </span>
                        </div>
                        <div id="cs-bf-geo-map" style="height:400px;border-radius:6px;overflow:hidden;border:1px solid #e2e8f0;background:#e8f4f4;margin-top:8px;"></div>
                        <div style="margin-top:6px;font-size:11px;color:#94a3b8;"><?php esc_html_e( 'Each ring scales independently, circle size reflects volume within that category only. Country via Cloudflare CF-IPCountry or DB-IP Lite fallback.', 'cloudscale-cyber-devtools' ); ?></div>
                        <?php CSDT_Geo::render_dbip_status(); ?>
                    </div>

                    <div id="cs-ip-blocklist-wrap" class="cs-bf-log-wrap" style="margin-top:22px;<?php echo empty( $ip_blocklist ) ? 'display:none;' : ''; ?>">
                        <div class="cs-bf-log-header">
                            <span class="cs-bf-log-title">🚫 <?php esc_html_e( 'Blocked IPs', 'cloudscale-cyber-devtools' ); ?></span>
                            <span class="cs-bf-log-total" id="cs-blocklist-count"><?php echo count( $ip_blocklist ); ?> blocked</span>
                            <?php
                            self::render_explain_btn(
                                'blocked-ips',
                                'Blocked IPs',
                                array(
									array(
										'name' => 'What this shows',
										'rec'  => 'Overview',
										'desc' => 'IP addresses that have been permanently blocked from accessing the site. Blocked IPs receive a 403 response on every request — they cannot reach any page, not just the login form.',
									),
									array(
										'name' => 'Adding blocks',
										'rec'  => 'Note',
										'desc' => 'Blocks can be added manually from the probe IP table above, or automatically when an IP exceeds the brute-force threshold. Private and internal IP addresses (e.g. 192.168.x.x, 172.x.x.x) cannot be blocked to prevent accidental self-lockout.',
									),
									array(
										'name' => 'Lockout risk',
										'rec'  => 'Important',
										'desc' => 'If you accidentally block your own IP, you can recover by logging into the server via SSH and removing the entry with WP-CLI: wp option get csdt_ip_blocklist, then wp option patch delete csdt_ip_blocklist <your-ip>.',
									),
								),
								'',
								true
                            );
                            ?>
                        </div>
                        <div class="cs-bf-table-wrap">
                            <table class="cs-bf-table">
                                <thead>
                                    <tr>
                                        <th class="cs-bf-th"><?php esc_html_e( 'IP address', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"><?php esc_html_e( 'Reason', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"><?php esc_html_e( 'Blocked', 'cloudscale-cyber-devtools' ); ?></th>
                                        <th class="cs-bf-th"></th>
                                    </tr>
                                </thead>
                                <tbody id="cs-blocklist-tbody">
                                <?php foreach ( $ip_blocklist as $bl_ip => $bl_data ) : ?>
                                    <tr id="cs-bl-row-<?php echo esc_attr( str_replace( '.', '-', $bl_ip ) ); ?>">
                                        <td class="cs-bf-td cs-bf-td-ip"><?php echo esc_html( $bl_ip ); ?></td>
                                        <td class="cs-bf-td cs-bf-td-time"><?php echo esc_html( $bl_data['reason'] ?? 'Manual block' ); ?></td>
                                        <td class="cs-bf-td cs-bf-td-time"><?php echo esc_html( isset( $bl_data['blocked_at'] ) ? human_time_diff( $bl_data['blocked_at'] ) . ' ago' : '·' ); ?></td>
                                        <td class="cs-bf-td" style="text-align:right;white-space:nowrap;">
                                            <a href="https://ipinfo.io/<?php echo esc_attr( $bl_ip ); ?>" target="_blank" rel="noopener"
                                                style="font-size:10px;padding:2px 6px;border:1px solid #cbd5e1;border-radius:4px;background:#f8fafc;color:#475569;text-decoration:none;margin-right:4px;">Whois</a>
                                            <button type="button" class="cs-ip-unblock-btn" data-ip="<?php echo esc_attr( $bl_ip ); ?>"
                                                    style="font-size:10px;padding:2px 6px;border:1px solid #cbd5e1;border-radius:4px;background:#f8fafc;color:#475569;cursor:pointer;">Unblock</button>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>

                    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- inline nonce config; wp_add_inline_script not available at this render point ?>
                    <script>
                    (function(){
                        var nonce = <?php echo wp_json_encode( wp_create_nonce( self::SECURITY_NONCE ) ); ?>;

                        function doAjax(action, ip, reason, cb) {
                            var fd = new FormData();
                            fd.append('action', action);
                            fd.append('nonce', nonce);
                            fd.append('ip', ip);
                            if (reason) fd.append('reason', reason);
                            fetch(ajaxurl, {method:'POST', body:fd})
                                .then(function(r){ return r.json(); })
                                .then(cb)
                                .catch(function(){ alert('Request failed.'); });
                        }

                        function addBlocklistRow(ip) {
                            var wrap  = document.getElementById('cs-ip-blocklist-wrap');
                            var tbody = document.getElementById('cs-blocklist-tbody');
                            var count = document.getElementById('cs-blocklist-count');
                            if (!wrap || !tbody) return;
                            wrap.style.display = '';
                            var rowId = 'cs-bl-row-' + ip.replace(/\./g,'-');
                            if (document.getElementById(rowId)) return;
                            var n = tbody.rows.length + 1;
                            if (count) count.textContent = n + ' blocked';
                            var tr = document.createElement('tr');
                            tr.id = rowId;
                            tr.innerHTML =
                                '<td class="cs-bf-td cs-bf-td-ip">' + ip + '</td>' +
                                '<td class="cs-bf-td cs-bf-td-time">wp-login probe</td>' +
                                '<td class="cs-bf-td cs-bf-td-time">just now</td>' +
                                '<td class="cs-bf-td" style="text-align:right;white-space:nowrap;">' +
                                '<a href="https://ipinfo.io/' + encodeURIComponent(ip) + '" target="_blank" rel="noopener" style="font-size:10px;padding:2px 6px;border:1px solid #cbd5e1;border-radius:4px;background:#f8fafc;color:#475569;text-decoration:none;margin-right:4px;">Whois</a>' +
                                '<button type="button" class="cs-ip-unblock-btn" data-ip="' + ip + '" style="font-size:10px;padding:2px 6px;border:1px solid #cbd5e1;border-radius:4px;background:#f8fafc;color:#475569;cursor:pointer;">Unblock</button>' +
                                '</td>';
                            tbody.prepend(tr);
                            wireUnblock(tr.querySelector('.cs-ip-unblock-btn'));
                        }

                        function wireUnblock(btn) {
                            if (!btn) return;
                            btn.addEventListener('click', function(){
                                var ip = btn.getAttribute('data-ip');
                                btn.disabled = true; btn.textContent = '⏳';
                                doAjax('csdt_ip_unblock', ip, null, function(resp){
                                    if (resp.success) {
                                        var row = document.getElementById('cs-bl-row-' + ip.replace(/\./g,'-'));
                                        if (row) row.remove();
                                        // Restore Block button on probe table row
                                        var probeRow = document.querySelector('#cs-probe-tbody tr[data-ip="' + ip + '"]');
                                        if (probeRow) {
                                            var cell = probeRow.querySelector('td:last-child');
                                            if (cell) {
                                                var blocked = cell.querySelector('span');
                                                if (blocked) {
                                                    var nb = document.createElement('button');
                                                    nb.type = 'button'; nb.className = 'cs-ip-block-btn';
                                                    nb.setAttribute('data-ip', ip);
                                                    nb.style.cssText = 'font-size:10px;padding:2px 6px;border:1px solid #fca5a5;border-radius:4px;background:#fef2f2;color:#dc2626;cursor:pointer;font-weight:600;';
                                                    nb.textContent = 'Block';
                                                    blocked.replaceWith(nb);
                                                    wireBlock(nb);
                                                }
                                            }
                                        }
                                        var tbody = document.getElementById('cs-blocklist-tbody');
                                        var wrap  = document.getElementById('cs-ip-blocklist-wrap');
                                        var count = document.getElementById('cs-blocklist-count');
                                        if (tbody && tbody.rows.length === 0 && wrap) { wrap.style.display = 'none'; }
                                        if (count) count.textContent = (tbody ? tbody.rows.length : 0) + ' blocked';
                                    } else {
                                        btn.disabled = false; btn.textContent = 'Unblock';
                                        alert(resp.data || 'Unblock failed.');
                                    }
                                });
                            });
                        }

                        function wireBlock(btn) {
                            if (!btn) return;
                            btn.addEventListener('click', function(){
                                var ip = btn.getAttribute('data-ip');
                                btn.disabled = true; btn.textContent = '⏳';
                                doAjax('csdt_ip_block', ip, 'wp-login probe', function(resp){
                                    if (resp.success) {
                                        btn.replaceWith((function(){
                                            var s = document.createElement('span');
                                            s.style.cssText = 'font-size:10px;padding:2px 6px;border:1px solid #86efac;border-radius:4px;background:#dcfce7;color:#15803d;font-weight:600;';
                                            s.textContent = '🚫 Blocked';
                                            return s;
                                        })());
                                        addBlocklistRow(ip);
                                    } else {
                                        btn.disabled = false; btn.textContent = 'Block';
                                        alert(resp.data || 'Block failed.');
                                    }
                                });
                            });
                        }

                        document.querySelectorAll('.cs-ip-block-btn').forEach(wireBlock);
                        document.querySelectorAll('.cs-ip-unblock-btn').forEach(wireUnblock);

                        // DB-IP download + auto-update toggle
                        (function(){
                            var dlBtn   = document.getElementById('csdt-dbip-download-btn');
                            var autoChk = document.getElementById('csdt-dbip-auto-update');
                            var msg     = document.getElementById('csdt-dbip-msg');
                            function showMsg(text, ok) {
                                if (!msg) return;
                                msg.textContent = text;
                                msg.style.color = ok ? '#15803d' : '#b91c1c';
                                msg.style.display = 'inline';
                            }
                            if (autoChk) {
                                autoChk.addEventListener('change', function() {
                                    var fd = new FormData();
                                    fd.append('action', 'csdt_save_dbip_settings');
                                    fd.append('nonce', nonce);
                                    fd.append('auto_update', autoChk.checked ? 'yes' : 'no');
                                    fetch(ajaxurl, {method:'POST', body:fd})
                                        .then(function(r){ return r.json(); })
                                        .then(function(res){ showMsg(res.success ? '✓ Saved' : '⚠ Save failed', res.success); setTimeout(function(){ if(msg) msg.style.display='none'; }, 2000); })
                                        .catch(function(){ showMsg('⚠ Error', false); });
                                });
                            }
                            if (dlBtn) {
                                dlBtn.addEventListener('click', function() {
                                    dlBtn.disabled = true;
                                    dlBtn.textContent = '⏳ Downloading…';
                                    var fd = new FormData();
                                    fd.append('action', 'csdt_download_dbip');
                                    fd.append('nonce', nonce);
                                    fetch(ajaxurl, {method:'POST', body:fd})
                                        .then(function(r){ return r.json(); })
                                        .then(function(res){
                                            dlBtn.disabled = false;
                                            if (res.success) {
                                                dlBtn.textContent = '🔄 Update DB-IP Lite';
                                                showMsg('✓ Installed, ' + (res.data.size || ''), true);
                                                var row = document.getElementById('csdt-dbip-status-row');
                                                if (row) {
                                                    row.style.background = '#f0fdf4';
                                                    row.style.borderColor = '#86efac';
                                                    var s = row.querySelector('span');
                                                    if (s) s.innerHTML = '<span style="color:#15803d;font-weight:600;">✓ DB-IP Lite installed</span>&nbsp;, &nbsp;' + (res.data.size || '') + '&nbsp;<span style="color:#64748b;">(country shown for all sites, not just Cloudflare)</span>';
                                                }
                                            } else {
                                                dlBtn.textContent = '⬇️ Download DB-IP Lite';
                                                showMsg('⚠ ' + (res.data || 'Download failed'), false);
                                            }
                                        })
                                        .catch(function(){
                                            dlBtn.disabled = false;
                                            dlBtn.textContent = '⬇️ Download DB-IP Lite';
                                            showMsg('⚠ Network error', false);
                                        });
                                });
                            }
                        })();
                    })();
                    </script>
                </div>
            </div>
        </div>

        <!-- ── SSH Brute-Force Monitor ─────────────────── -->
        <?php
        $ssh_mon_enabled   = get_option( 'csdt_ssh_monitor_enabled', '1' ) === '1';
        $ssh_mon_threshold = get_option( 'csdt_ssh_monitor_threshold', '10' );
        $ssh_last_check    = get_option( 'csdt_ssh_monitor_last_check', null );
        $ssh_last_alert    = (int) get_option( 'csdt_ssh_monitor_last_alert', 0 );
        $ssh_alert_log     = get_option( 'csdt_ssh_monitor_alert_log', array() );
        $auth_log_readable = false;
        foreach ( array( '/var/log/auth.log', '/var/log/secure', '/var/log/messages' ) as $_p ) {
            if ( is_readable( $_p ) ) {
				$auth_log_readable = true;
				break; }
        }
        ?>
        <div class="cs-panel" id="cs-panel-ssh-monitor">
            <div class="cs-section-header cs-section-header-red">
                <span>🖥️ SSH BRUTE-FORCE MONITOR</span>
                <span class="cs-header-hint"><?php esc_html_e( 'Real-time SSH attack detection via auth.log, alerts via email and Telegram', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'ssh-monitor',
                    'SSH Brute-Force Monitor',
                    array(
						array(
							'name' => 'How it works',
							'rec'  => 'Overview',
							'html' => 'A WP-Cron job runs every minute and reads new lines appended to <code>/var/log/auth.log</code> since the last check (byte-offset tracking, only new content is read, never the whole file). It counts <code>Failed password</code> lines in that window and fires an alert when the count exceeds your threshold.',
						),
						array(
							'name' => 'Threshold',
							'rec'  => 'Recommended',
							'html' => 'Default is <strong>10 failed attempts per minute</strong>. Lower (3 to 5) is appropriate if your server has only known, trusted users. Higher (20 to 50) reduces noise on servers exposed to constant internet scanning.',
						),
						array(
							'name' => 'Alert channels',
							'rec'  => 'Recommended',
							'html' => '<strong>Email</strong>, sent to the WordPress admin email address.<br><strong>Telegram</strong>, instant push notification. Configure your bot token and chat ID under Security Scan &rarr; Scheduled Scans.',
						),
						array(
							'name' => 'fail2ban',
							'rec'  => 'Critical',
							'html' => 'This monitor <em>detects</em> and alerts, it does not block IPs. <strong>fail2ban</strong> must be installed and configured to automatically ban offending IPs. Without it, SSH attacks continue unimpeded regardless of how many alerts you receive. Install: <code>sudo apt install fail2ban</code>, the default SSH jail is enabled automatically.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <?php if ( ! $auth_log_readable ) : ?>
                <div class="cs-notice cs-notice-warn" style="margin-bottom:16px;" id="cs-ssh-perm-notice">
                    <div style="display:flex;align-items:flex-start;gap:10px;flex-wrap:wrap;">
                        <div style="flex:1;min-width:0;">
                            ⚠️ <strong><?php esc_html_e( 'Auth log not readable.', 'cloudscale-cyber-devtools' ); ?></strong>
                            <?php esc_html_e( 'The web server user needs read access to the auth log.', 'cloudscale-cyber-devtools' ); ?>
                        </div>
                        <button type="button" id="cs-ssh-fix-btn"
                                style="flex-shrink:0;background:#2563eb;color:#fff;border:none;border-radius:5px;padding:6px 14px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap;">
                            🔧 <?php esc_html_e( 'Fix it automatically', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                    </div>
                    <div id="cs-ssh-fix-result" style="margin-top:8px;display:none;font-size:12px;padding:6px 10px;border-radius:4px;"></div>
                    <details style="margin-top:8px;">
                        <summary style="font-size:11px;color:rgba(255,255,255,.7);cursor:pointer;">Manual command</summary>
                        <div style="position:relative;margin-top:6px;">
                            <code id="cs-ssh-adm-cmd" style="display:block;padding:8px 44px 8px 10px;background:#f6f7f7;border-radius:4px;color:#1a1a1a;font-size:12px;line-height:1.5;word-break:break-all;">sudo usermod -a -G adm www-data &amp;&amp; sudo systemctl restart php-fpm</code>
                            <button type="button" onclick="(function(b){var t=document.getElementById('cs-ssh-adm-cmd');navigator.clipboard.writeText(t.textContent).then(function(){var o=b.textContent;b.textContent='✓';setTimeout(function(){b.textContent=o;},1500);});})(this)"
                                    style="position:absolute;top:50%;right:6px;transform:translateY(-50%);background:#e2e8f0;border:none;border-radius:4px;padding:3px 7px;font-size:11px;cursor:pointer;color:#334155;white-space:nowrap;">Copy</button>
                        </div>
                    </details>
                </div>
                <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- inline SSH fix button handler; wp_add_inline_script not available at this render point ?>
                <script>
                (function(){
                    var btn = document.getElementById('cs-ssh-fix-btn');
                    if (!btn) return;
                    btn.addEventListener('click', function(){
                        btn.disabled = true;
                        btn.textContent = '⏳ Running…';
                        var fd = new FormData();
                        fd.append('action', 'csdt_ssh_fix_permissions');
                        fd.append('nonce', csdtVulnScan.nonce);
                        fetch(ajaxurl, {method:'POST', body:fd})
                            .then(function(r){ return r.json(); })
                            .then(function(resp){
                                var el = document.getElementById('cs-ssh-fix-result');
                                el.style.display = 'block';
                                if (resp.success) {
                                    el.style.background = '#dcfce7';
                                    el.style.color = '#15803d';
                                    el.textContent = '✅ ' + (resp.data.note || 'Done.');
                                    btn.textContent = '✅ Fixed';
                                    if (resp.data.readable) {
                                        setTimeout(function(){ location.reload(); }, 2000);
                                    }
                                } else {
                                    el.style.background = '#fff7ed';
                                    el.style.color = '#c2410c';
                                    el.textContent = '❌ ' + (resp.data || 'Failed, use the manual command below.');
                                    btn.disabled = false;
                                    btn.textContent = '🔧 Retry';
                                }
                            })
                            .catch(function(){
                                var el = document.getElementById('cs-ssh-fix-result');
                                el.style.display = 'block';
                                el.style.background = '#fff7ed';
                                el.style.color = '#c2410c';
                                el.textContent = '❌ Request failed, use the manual command below.';
                                btn.disabled = false;
                                btn.textContent = '🔧 Retry';
                            });
                    });
                })();
                </script>
                <?php endif; ?>

                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label">
                            <input type="checkbox" id="cs-ssh-mon-enabled" <?php checked( $ssh_mon_enabled ); ?>>
                            <?php esc_html_e( 'Enable SSH brute-force monitor (checks every 60 seconds)', 'cloudscale-cyber-devtools' ); ?>
                        </label>
                        <span class="cs-hint"><?php esc_html_e( 'Reads /var/log/auth.log every minute and alerts if the failure threshold is crossed. On by default.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-ssh-mon-threshold"><?php esc_html_e( 'Alert threshold (failures per 60 s):', 'cloudscale-cyber-devtools' ); ?></label>
                        <input type="number" id="cs-ssh-mon-threshold" class="cs-input" min="1" max="1000"
                                value="<?php echo esc_attr( $ssh_mon_threshold ); ?>" style="max-width:100px">
                        <span class="cs-hint"><?php esc_html_e( 'Default: 10. Sends email + Telegram alert when this many failures occur in the last 60 seconds. Alerts are throttled to once per 5 minutes.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
                <?php if ( $ssh_last_check ) : ?>
                <div class="cs-field-row" style="padding:10px 0 0;">
                    <div style="font-size:12px;color:#64748b;">
                        <strong><?php esc_html_e( 'Last check:', 'cloudscale-cyber-devtools' ); ?></strong>
                        <?php echo esc_html( human_time_diff( $ssh_last_check['ts'] ) . ' ago' ); ?>, <strong><?php echo (int) $ssh_last_check['count']; ?></strong> <?php esc_html_e( 'failure(s) in last 60 s', 'cloudscale-cyber-devtools' ); ?>
                        <?php if ( $ssh_last_alert > 0 ) : ?>
                        &nbsp;|&nbsp; <strong><?php esc_html_e( 'Last alert:', 'cloudscale-cyber-devtools' ); ?></strong>
							<?php echo esc_html( human_time_diff( $ssh_last_alert ) . ' ago' ); ?>
                        <?php endif; ?>
                    </div>
                </div>
					<?php if ( ! empty( $ssh_last_check['lines'] ) ) : ?>
                <div style="margin-top:10px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:10px 14px;font-size:11px;color:#475569;font-family:monospace;max-height:140px;overflow-y:auto;">
						<?php foreach ( array_reverse( $ssh_last_check['lines'] ) as $line ) : ?>
                    <div><?php echo esc_html( $line ); ?></div>
                    <?php endforeach; ?>
                </div>
                <?php endif; ?>
                <?php endif; ?>
                <div style="margin-top:18px;display:flex;align-items:center;gap:10px">
                    <button type="button" class="cs-btn-primary" id="cs-ssh-mon-save">💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                    <span class="cs-settings-saved" id="cs-ssh-mon-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                </div>

                <?php if ( ! empty( $ssh_alert_log ) ) : ?>
                <div style="margin-top:22px;">
                    <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;">
                        <span style="font-weight:600;font-size:13px;">🚨 <?php esc_html_e( 'Alert History', 'cloudscale-cyber-devtools' ); ?></span>
                        <button type="button" id="cs-ssh-log-clear" style="background:none;border:none;color:#94a3b8;font-size:11px;cursor:pointer;padding:0;"><?php esc_html_e( 'Clear log', 'cloudscale-cyber-devtools' ); ?></button>
                    </div>
                    <table style="width:100%;border-collapse:collapse;font-size:12px;">
                        <thead>
                            <tr style="background:#f8fafc;">
                                <th style="text-align:left;padding:6px 10px;border-bottom:1px solid #e2e8f0;color:#64748b;font-weight:600;"><?php esc_html_e( 'Time', 'cloudscale-cyber-devtools' ); ?></th>
                                <th style="text-align:center;padding:6px 10px;border-bottom:1px solid #e2e8f0;color:#64748b;font-weight:600;"><?php esc_html_e( 'Attempts', 'cloudscale-cyber-devtools' ); ?></th>
                                <th style="text-align:left;padding:6px 10px;border-bottom:1px solid #e2e8f0;color:#64748b;font-weight:600;"><?php esc_html_e( 'Targeted accounts', 'cloudscale-cyber-devtools' ); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                            foreach ( array_reverse( $ssh_alert_log ) as $entry ) :
                                $users = $entry['users'] ?? array();
                                arsort( $users );
                                $user_parts = array();
                                foreach ( array_slice( $users, 0, 5, true ) as $u => $c ) {
                                    $user_parts[] = esc_html( $u ) . ( $c > 1 ? ' &times;' . $c : '' );
                                }
								?>
                            <tr style="border-bottom:1px solid #f1f5f9;">
                                <td style="padding:6px 10px;white-space:nowrap;color:#475569;"><?php echo esc_html( human_time_diff( $entry['ts'] ) . ' ago' ); ?></td>
                                <td style="padding:6px 10px;text-align:center;font-weight:700;color:#dc2626;"><?php echo (int) $entry['count']; ?></td>
                                <td style="padding:6px 10px;color:#334155;"><?php echo $user_parts ? implode( ', ', $user_parts ) : '&middot;'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- each $user_parts entry is esc_html()-escaped above ?></td>
                            </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
                <?php endif; ?>
            </div>
        </div>

        <!-- ── Your 2FA Setup (current user) ─────────── -->
        <div class="cs-panel">
            <div class="cs-section-header cs-section-header-green">
                <span>👤 YOUR 2FA SETUP</span>
                <span class="cs-header-hint"><?php echo esc_html( wp_get_current_user()->user_login ); ?></span>
                <?php
                self::render_explain_btn(
                    '2fa-setup',
                    'Your 2FA Setup',
                    array(
						array(
							'name' => 'Authenticator App (TOTP)',
							'rec'  => 'Recommended',
							'html' => 'Generates a <strong>6-digit code every 30 seconds</strong> using a TOTP app. Works offline and is the most secure 2FA method.<br><br><ul><li><strong>Google Authenticator</strong>, iOS / Android</li><li><strong>Authy</strong>, iOS / Android / Desktop</li><li><strong>1Password</strong>, built-in TOTP support</li><li><strong>Apple Passwords</strong>, iOS 18+ / macOS 15+</li></ul>',
						),
						array(
							'name' => 'Email Code',
							'rec'  => 'Optional',
							'html' => 'Sends a one-time code to your account email on each login. Simpler to set up but depends on email deliverability, if your site\'s outgoing email is unreliable, use an authenticator app instead.',
						),
						array(
							'name' => 'Passkey',
							'rec'  => 'Recommended',
							'html' => 'Uses <strong>Face ID</strong>, <strong>Touch ID</strong>, <strong>Windows Hello</strong>, or a hardware security key (YubiKey, etc.) as your second factor. Register a passkey in the <strong>Passkeys</strong> panel, then select this method here.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 16px;"><?php esc_html_e( 'Add a second verification step to your account. Even if your password is stolen, an attacker still can\'t get in without this second factor. Authenticator app is the most secure option; email code requires no app but depends on reliable outgoing mail.', 'cloudscale-cyber-devtools' ); ?></p>

                <!-- Email 2FA status -->
                <?php
                // Check if a verification email is already pending for this user.
                $email_pending = (bool) get_user_meta( $user_id, 'csdt_devtools_email_verify_pending', true );
                ?>
                <div class="cs-2fa-row" id="cs-email-row">
                    <div class="cs-2fa-row-icon">📧</div>
                    <div class="cs-2fa-row-body">
                        <div class="cs-2fa-row-title"><?php esc_html_e( 'Email Code', 'cloudscale-cyber-devtools' ); ?></div>
                        <div class="cs-2fa-row-desc"><?php esc_html_e( 'A 6-digit code is emailed to you after your password is accepted.', 'cloudscale-cyber-devtools' ); ?></div>
                        <div class="cs-email-pending-msg" id="cs-email-pending-msg" style="<?php echo $email_pending ? '' : 'display:none'; ?>">
                            <span class="cs-pending-notice">📬 <?php esc_html_e( 'Verification email sent, click the link in the email to activate.', 'cloudscale-cyber-devtools' ); ?></span>
                        </div>
                    </div>
                    <div class="cs-2fa-row-action">
                        <?php if ( $email_active ) : ?>
                            <span class="cs-2fa-badge cs-2fa-badge-on"><?php esc_html_e( 'Active', 'cloudscale-cyber-devtools' ); ?></span>
                            <button type="button" class="cs-btn-pink cs-2fa-disable" data-method="email" style="margin-left:10px">
                                <?php esc_html_e( 'Disable', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                        <?php elseif ( $email_pending ) : ?>
                            <span class="cs-2fa-badge cs-2fa-badge-pending" id="cs-email-badge"><?php esc_html_e( 'Awaiting verification', 'cloudscale-cyber-devtools' ); ?></span>
                            <button type="button" class="cs-btn-orange cs-email-enable" id="cs-email-enable-btn" style="margin-left:10px">
                                <?php esc_html_e( 'Resend', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                        <?php else : ?>
                            <span class="cs-2fa-badge cs-2fa-badge-off" id="cs-email-badge"><?php esc_html_e( 'Off', 'cloudscale-cyber-devtools' ); ?></span>
                            <button type="button" class="cs-btn-orange cs-email-enable" id="cs-email-enable-btn" style="margin-left:10px">
                                <?php esc_html_e( 'Enable', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                        <?php endif; ?>
                    </div>
                </div>

                <div class="cs-2fa-divider"></div>

                <!-- TOTP status + setup wizard -->
                <div class="cs-2fa-row" id="cs-totp-row">
                    <div class="cs-2fa-row-icon">📱</div>
                    <div class="cs-2fa-row-body">
                        <div class="cs-2fa-row-title"><?php esc_html_e( 'Authenticator App (TOTP)', 'cloudscale-cyber-devtools' ); ?></div>
                        <div class="cs-2fa-row-desc"><?php esc_html_e( 'Google Authenticator, Authy, 1Password, or any TOTP app. Generates a fresh 6-digit code every 30 seconds.', 'cloudscale-cyber-devtools' ); ?></div>
                    </div>
                    <div class="cs-2fa-row-action">
                        <?php if ( $totp_active ) : ?>
                            <span class="cs-2fa-badge cs-2fa-badge-on" id="cs-totp-badge"><?php esc_html_e( 'Active', 'cloudscale-cyber-devtools' ); ?></span>
                            <button type="button" class="cs-btn-pink cs-2fa-disable" data-method="totp" style="margin-left:10px" id="cs-totp-disable-btn">
                                <?php esc_html_e( 'Disable', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                        <?php else : ?>
                            <span class="cs-2fa-badge cs-2fa-badge-off" id="cs-totp-badge"><?php esc_html_e( 'Not set up', 'cloudscale-cyber-devtools' ); ?></span>
                            <button type="button" class="cs-btn-primary" id="cs-totp-setup-btn" style="margin-left:10px">
                                <?php esc_html_e( 'Set Up', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- TOTP Setup Wizard (hidden until triggered) -->
                <div id="cs-totp-wizard" class="cs-totp-wizard" style="display:none">
                    <div class="cs-totp-wizard-inner">
                        <h3 class="cs-totp-wizard-title">📱 <?php esc_html_e( 'Set Up Authenticator App', 'cloudscale-cyber-devtools' ); ?></h3>

                        <div class="cs-totp-steps">
                            <div class="cs-totp-step">
                                <span class="cs-totp-step-num">1</span>
                                <?php esc_html_e( 'Open your authenticator app (Google Authenticator, Authy, 1Password, etc.) and scan this QR code:', 'cloudscale-cyber-devtools' ); ?>
                            </div>
                            <div class="cs-totp-qr-wrap">
                                <div id="cs-totp-qr-loading" class="cs-totp-qr-loading">
                                    <span class="spinner is-active" style="float:none;margin:0"></span>
                                    <?php esc_html_e( 'Generating…', 'cloudscale-cyber-devtools' ); ?>
                                </div>
                                <div id="cs-totp-qr-canvas" class="cs-totp-qr-img" style="display:none"></div>
                            </div>
                            <div class="cs-totp-manual-wrap" style="display:none" id="cs-totp-manual">
                                <span class="cs-label" style="font-size:12px"><?php esc_html_e( "Can't scan? Enter this key manually:", 'cloudscale-cyber-devtools' ); ?></span>
                                <div class="cs-totp-secret-row">
                                    <code id="cs-totp-secret-display" class="cs-totp-secret"></code>
                                    <button type="button" id="cs-totp-copy-btn" class="cs-totp-copy-btn" title="<?php esc_attr_e( 'Copy key', 'cloudscale-cyber-devtools' ); ?>">
                                        <?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?>
                                    </button>
                                </div>
                            </div>

                            <div class="cs-totp-step" style="margin-top:16px">
                                <span class="cs-totp-step-num">2</span>
                                <?php esc_html_e( 'Enter the 6-digit code from your app to confirm setup:', 'cloudscale-cyber-devtools' ); ?>
                            </div>
                            <div class="cs-totp-verify-row">
                                <input type="text" id="cs-totp-verify-code" class="cs-input cs-totp-code-input"
                                        placeholder="000000" maxlength="6" inputmode="numeric" autocomplete="one-time-code">
                                <button type="button" class="cs-btn-primary" id="cs-totp-verify-btn">
                                    ✓ <?php esc_html_e( 'Verify & Activate', 'cloudscale-cyber-devtools' ); ?>
                                </button>
                            </div>
                            <div id="cs-totp-verify-msg" class="cs-totp-verify-msg" style="display:none"></div>
                        </div>

                        <div style="margin-top:12px">
                            <button type="button" class="cs-btn-pink" id="cs-totp-cancel-btn" style="font-size:11px;padding:5px 12px">
                                <?php esc_html_e( 'Cancel', 'cloudscale-cyber-devtools' ); ?>
                            </button>
                        </div>
                    </div>
                </div>

            </div>
        </div>

        <!-- ── Two-Factor Authentication ─────────────── -->
        <div class="cs-panel" id="cs-panel-2fa">
            <div class="cs-section-header cs-section-header-orange">
                <span>🔑 TWO-FACTOR AUTHENTICATION</span>
                <span class="cs-header-hint"><?php esc_html_e( 'Email code or Authenticator app', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    '2fa',
                    'Two-Factor Authentication',
                    array(
						array(
							'name' => 'Off',
							'rec'  => 'Not Recommended',
							'html' => 'Disables 2FA site-wide. Passwords alone are vulnerable to phishing and brute-force attacks, not recommended for any public site.',
						),
						array(
							'name' => 'Email Code',
							'rec'  => 'Optional',
							'html' => 'Requires users to enter a code sent to their email after each password login. Works out of the box with no app required, but depends on your site\'s outgoing email working reliably.',
						),
						array(
							'name' => 'Authenticator App (TOTP)',
							'rec'  => 'Recommended',
							'html' => 'Each user configures their own TOTP app (<strong>Google Authenticator</strong>, <strong>Authy</strong>, <strong>1Password</strong>). Most secure option, works offline, no email dependency.',
						),
						array(
							'name' => 'Force 2FA for Admins',
							'rec'  => 'Recommended',
							'html' => 'Blocks <code>administrator</code>-role users from accessing the dashboard until they have set up 2FA. Strongly recommended on any multi-user site.',
						),
						array(
							'name' => 'Grace Logins',
							'rec'  => 'Advanced',
							'html' => 'Allows a user to log in up to <em>N</em> times before 2FA is enforced. The counter is per-user and never resets automatically. Default is <code>0</code> (2FA required from the first login).<br><br><strong>Tip for automated test accounts:</strong> set to <code>1</code>. Tools like Playwright cannot complete a real 2FA challenge, one grace login lets a test account authenticate for setup steps without disabling 2FA site-wide.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p class="cs-login-desc"><?php esc_html_e( 'Require a second verification step after password login. Email sends a one-time code; Authenticator uses Google Authenticator, Authy, or any TOTP app.', 'cloudscale-cyber-devtools' ); ?></p>

                <!-- Site-wide default -->
                <?php
                $has_passkeys = ! empty( CSDT_DevTools_Passkey::get_passkeys( $user_id ) );
                ?>
                <div class="cs-field-row">
                    <div class="cs-field">
                        <label class="cs-label"><?php esc_html_e( 'Site-wide Default Method:', 'cloudscale-cyber-devtools' ); ?></label>
                        <div class="cs-2fa-method-group">
                            <label class="cs-radio-label <?php echo $method === 'off' ? 'active' : ''; ?>">
                                <input type="radio" name="csdt_devtools_2fa_method" value="off" <?php checked( $method, 'off' ); ?>>
                                <span class="cs-radio-icon">🚫</span> <?php esc_html_e( 'Off', 'cloudscale-cyber-devtools' ); ?>
                            </label>
                            <label class="cs-radio-label <?php echo $method === 'email' ? 'active' : ''; ?> <?php echo ! $email_active ? 'cs-radio-disabled' : ''; ?>"
                                    <?php echo ! $email_active ? 'title="' . esc_attr__( 'Enable Email Code for your account first', 'cloudscale-cyber-devtools' ) . '"' : ''; ?>>
                                <input type="radio" name="csdt_devtools_2fa_method" value="email" <?php checked( $method, 'email' ); ?> <?php disabled( ! $email_active ); ?>>
                                <span class="cs-radio-icon">📧</span> <?php esc_html_e( 'Email Code', 'cloudscale-cyber-devtools' ); ?>
                            </label>
                            <label class="cs-radio-label <?php echo $method === 'totp' ? 'active' : ''; ?> <?php echo ! $totp_active ? 'cs-radio-disabled' : ''; ?>"
                                    <?php echo ! $totp_active ? 'title="' . esc_attr__( 'Set up Authenticator App for your account first', 'cloudscale-cyber-devtools' ) . '"' : ''; ?>>
                                <input type="radio" name="csdt_devtools_2fa_method" value="totp" <?php checked( $method, 'totp' ); ?> <?php disabled( ! $totp_active ); ?>>
                                <span class="cs-radio-icon">📱</span> <?php esc_html_e( 'Authenticator App', 'cloudscale-cyber-devtools' ); ?>
                            </label>
                            <label class="cs-radio-label <?php echo $method === 'passkey' ? 'active' : ''; ?> <?php echo ! $has_passkeys ? 'cs-radio-disabled' : ''; ?>"
                                    <?php echo ! $has_passkeys ? 'title="' . esc_attr__( 'Register a passkey for your account first', 'cloudscale-cyber-devtools' ) . '"' : ''; ?>>
                                <input type="radio" name="csdt_devtools_2fa_method" value="passkey" <?php checked( $method, 'passkey' ); ?> <?php disabled( ! $has_passkeys ); ?>>
                                <span class="cs-radio-icon">🔑</span> <?php esc_html_e( 'Passkey', 'cloudscale-cyber-devtools' ); ?>
                            </label>
                        </div>
                        <span class="cs-hint"><?php esc_html_e( 'Sets the default method. Individual users can override if force is not enabled.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                    <div class="cs-field">
                        <label class="cs-label"><?php esc_html_e( 'Enforcement:', 'cloudscale-cyber-devtools' ); ?></label>
                        <label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin-top:2px">
                            <input type="checkbox" id="cs-2fa-force" <?php checked( $force ); ?>>
                            <span style="font-size:13px;color:#555"><?php esc_html_e( 'Force 2FA for all administrators', 'cloudscale-cyber-devtools' ); ?></span>
                        </label>
                        <span class="cs-hint"><?php esc_html_e( 'Admins without 2FA set up will be blocked from the dashboard until they configure it.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <?php $grace_logins = (int) get_option( 'csdt_devtools_2fa_grace_logins', '0' ); ?>
                <div class="cs-field-row" style="margin-top:16px">
                    <div class="cs-field">
                        <label class="cs-label" for="cs-2fa-grace-logins"><?php esc_html_e( 'Grace logins before 2FA is required:', 'cloudscale-cyber-devtools' ); ?></label>
                        <input type="number" id="cs-2fa-grace-logins" class="cs-input" min="0" max="10"
                                value="<?php echo esc_attr( $grace_logins ); ?>" style="max-width:100px">
                        <span class="cs-hint"><?php esc_html_e( 'Allow N logins without 2FA per user. 0 = 2FA required from first login. For automated test accounts use 1.', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>

                <div style="margin-top:16px;display:flex;align-items:center;gap:10px">
                    <button type="button" class="cs-btn-primary" id="cs-2fa-save">💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                    <span class="cs-settings-saved" id="cs-2fa-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
            </div>
        </div>

        <!-- ── Passkeys (WebAuthn) ────────────────────── -->
        <div class="cs-panel" id="cs-panel-passkeys">
            <div class="cs-section-header" style="background:linear-gradient(135deg,#1e1b4b,#3730a3)">
                <span>🔑 PASSKEYS (WEBAUTHN)</span>
                <span class="cs-header-hint"><?php esc_html_e( 'Face ID · Touch ID · Windows Hello · Security Keys', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'passkeys',
                    'Passkeys (WebAuthn)',
                    array(
						array(
							'name' => 'What is a passkey?',
							'rec'  => 'Informational',
							'html' => 'A passkey is a cryptographic credential stored on your device. It replaces passwords with biometrics (<strong>Face ID</strong>, <strong>Touch ID</strong>, <strong>Windows Hello</strong>) or hardware keys (YubiKey, etc.). No secret is ever sent over the network, the private key never leaves your device.',
						),
						array(
							'name' => 'Registering a passkey',
							'rec'  => 'Recommended',
							'html' => 'Click <strong>+ Add Passkey</strong>, give it a name (e.g. <code>iPhone 16</code> or <code>MacBook Touch ID</code>), then follow your device\'s biometric prompt.<br><br>Register multiple passkeys for different devices so you always have a backup.',
						),
						array(
							'name' => 'Test',
							'rec'  => 'Optional',
							'html' => 'Verifies a passkey is working correctly <em>without</em> logging out. Use this after registering a new passkey to confirm the credential round-trips successfully.',
						),
						array(
							'name' => 'Remove',
							'rec'  => 'Optional',
							'html' => 'Deletes the passkey from your account. You can re-register it at any time, the device credential itself is not affected.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 16px;"><?php esc_html_e( 'Passkeys replace your password entirely, or act as your 2FA step, using Face ID, Touch ID, Windows Hello, or a hardware key. Once registered, your device\'s biometric sensor is the only thing needed to log in. Register one passkey per device and keep at least two so you always have a backup.', 'cloudscale-cyber-devtools' ); ?></p>
                <?php CSDT_DevTools_Passkey::render_section( $user_id ); ?>
            </div>
        </div>

        <?php
        /* ── Test Account Manager ─────────────────────────────────────── */
        $test_secret   = CSDT_Test_Accounts::get_or_create_secret();
        $path_token    = CSDT_Test_Accounts::get_or_create_path_token();
        $masked_secret = str_repeat( '•', min( 20, strlen( $test_secret ) ) ) . substr( $test_secret, -4 );
        $session_url   = rest_url( 'csdt/v1/test-session-' . $path_token );
        $logout_url    = rest_url( 'csdt/v1/test-logout-' . $path_token );
        $test_users    = CSDT_Test_Accounts::get_test_users_with_sessions();
        ?>
        <div class="cs-panel" id="cs-panel-test-accounts">
            <div class="cs-section-header" style="background:linear-gradient(135deg,#1e3a8a,#1d4ed8)">
                <span>🧪 <?php esc_html_e( 'TEST ACCOUNT MANAGER', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Persistent test users for Playwright / CI, bypasses 2FA via server-side cookies', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'test-accounts',
                    'Test Account Manager',
                    array(
						array(
							'name' => 'How it works',
							'rec'  => 'Overview',
							'html' => 'Create a persistent WordPress user for Playwright. Each test run calls the Session URL to get short-lived auth cookies, this happens server-side and never triggers wp-login.php or 2FA hooks. When the test run finishes, call the Logout URL to destroy the session.',
						),
						array(
							'name' => 'Session URL',
							'rec'  => 'Required',
							'html' => '<code>POST {session_url}</code><br>Body: <code>{ "secret": "...", "role": "your_name", "ttl": 1200 }</code><br>Returns auth cookies to inject into Playwright context. TTL max is 3600 seconds.',
						),
						array(
							'name' => 'Logout URL',
							'rec'  => 'Recommended',
							'html' => '<code>POST {logout_url}</code><br>Body: <code>{ "secret": "...", "role": "your_name" }</code> (or add <code>"session_token": "..."</code> to kill only that session). Call this in afterAll to clean up.',
						),
						array(
							'name' => 'Security',
							'rec'  => 'Info',
							'html' => 'Both the URL path token (32 random chars) and the shared secret are required to obtain a session. After 5 bad secret attempts the API locks for 10 minutes and sends a Telegram alert. Never commit the secret to git.<br><br><strong>Password login is hard-blocked.</strong> Playwright role accounts are created with a random 24-character password that is immediately discarded, it is never shown to anyone. The plugin also registers an <code>authenticate</code> filter (priority 30) that rejects any password-based login attempt for these accounts at the WordPress level, regardless of whether someone could somehow obtain the password hash. The only way to authenticate as a test account is through the Session URL above.',
						),
						array(
							'name' => 'Block Basic Auth',
							'rec'  => 'Recommended',
							'html' => '<strong>What are app passwords?</strong><br>WordPress lets any user generate an "Application Password" under their profile. External tools then send <code>Authorization: Basic username:app_password</code> with every REST API request, no login form, no 2FA.<br><br><strong>The security risk</strong><br>App passwords completely bypass 2FA. An attacker who leaks one gets full REST API access as that user, forever, with no second factor to stop them.<br><br><strong>Enable the block if:</strong><ul style="margin:.4em 0 .4em 1.2em;line-height:1.8"><li>You have 2FA enabled and want it to actually protect the account</li><li>You do not use the WordPress mobile app to post</li><li>No external service (Zapier, IFTTT, a headless CMS, a backup tool) authenticates with an app password</li></ul><strong>Leave it off if:</strong><ul style="margin:.4em 0 .4em 1.2em;line-height:1.8"><li>You publish from the WordPress iPhone/Android app</li><li>A third-party service is connected via REST API + app password</li></ul><strong>Check "App passwords in use" first.</strong> If the counter shows 0, you can safely enable the block with zero impact. If it shows &gt;0, identify which integration uses them before blocking.<br><br><em>Test accounts are unaffected, they use server-side session cookies, not Basic Auth.</em>',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <div class="cs-sec-settings">

                    <!-- Step 1: Create test user -->
                    <div class="cs-sec-row" style="align-items:flex-start;">
                        <span class="cs-sec-label" style="padding-top:4px;"><?php esc_html_e( 'Create test user:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end;">
                                <div style="display:flex;flex-direction:column;gap:4px;">
                                    <input type="text" id="cs-pwr-name" placeholder="e.g. my_playwright" maxlength="40" style="width:200px;" class="cs-sec-select">
                                    <span style="font-size:11px;color:#9ca3af;"><?php esc_html_e( 'Name, use as CSDT_TEST_ROLE in .env', 'cloudscale-cyber-devtools' ); ?></span>
                                </div>
                                <div style="display:flex;flex-direction:column;gap:4px;">
                                    <select id="cs-pwr-wp-role" class="cs-sec-select" style="width:auto;">
                                        <option value="administrator"><?php esc_html_e( 'Administrator', 'cloudscale-cyber-devtools' ); ?></option>
                                        <option value="editor"><?php esc_html_e( 'Editor', 'cloudscale-cyber-devtools' ); ?></option>
                                        <option value="author"><?php esc_html_e( 'Author', 'cloudscale-cyber-devtools' ); ?></option>
                                        <option value="contributor"><?php esc_html_e( 'Contributor', 'cloudscale-cyber-devtools' ); ?></option>
                                        <option value="subscriber"><?php esc_html_e( 'Subscriber', 'cloudscale-cyber-devtools' ); ?></option>
                                    </select>
                                    <span style="font-size:11px;color:#9ca3af;"><?php esc_html_e( 'WordPress role', 'cloudscale-cyber-devtools' ); ?></span>
                                </div>
                                <button type="button" id="cs-pwr-create" class="cs-btn-primary" style="align-self:flex-start;">
                                    + <?php esc_html_e( 'Create User', 'cloudscale-cyber-devtools' ); ?>
                                </button>
                            </div>
                            <span class="cs-hint" style="margin-top:6px;"><?php esc_html_e( 'Creates a persistent WP user. Reuse across all test runs, sessions are short-lived, the user account is not.', 'cloudscale-cyber-devtools' ); ?></span>
                            <div id="cs-pwr-msg" style="display:none;margin-top:8px;font-size:13px;"></div>
                        </div>
                    </div>

                    <hr class="cs-sec-divider" style="margin:8px 0;">

                    <!-- Step 2: Shared secret -->
                    <div class="cs-sec-row" style="align-items:center;">
                        <span class="cs-sec-label"><?php esc_html_e( 'Shared secret:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
                                <?php $full_mask = str_repeat( '•', strlen( $test_secret ) ); ?>
                                <code id="cs-pwr-secret-display" style="background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;padding:4px 10px;font-size:12px;letter-spacing:0.05em;"><?php echo esc_html( $full_mask ); ?></code>
                                <button type="button" id="cs-pwr-secret-show" class="cs-btn-secondary cs-btn-sm">👁 <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                                <button type="button" id="cs-pwr-secret-copy" class="cs-btn-secondary cs-btn-sm">⎘ <?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></button>
                                <button type="button" id="cs-pwr-secret-regen" class="cs-btn-secondary cs-btn-sm">↺ <?php esc_html_e( 'Regenerate', 'cloudscale-cyber-devtools' ); ?></button>
                            </div>
                            <span class="cs-hint" style="margin-top:6px;"><?php esc_html_e( 'Store in .env (never commit). Regenerating invalidates all existing .env files.', 'cloudscale-cyber-devtools' ); ?></span>
                        </div>
                    </div>

                    <!-- Session URL -->
                    <?php
                    $session_base  = rest_url( 'csdt/v1/test-session-' );
                    $logout_base   = rest_url( 'csdt/v1/test-logout-' );
                    $masked_sess   = str_repeat( '•', 32 );
                    $masked_logout = str_repeat( '•', 32 );
                    ?>
                    <div class="cs-sec-row" style="margin-top:8px;">
                        <span class="cs-sec-label"><?php esc_html_e( 'Session URL:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
                                <code id="cs-ta-session-url-display" style="background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;padding:4px 10px;font-size:11px;word-break:break-all;" data-real="<?php echo esc_attr( $session_url ); ?>" data-masked="<?php echo esc_attr( $masked_sess ); ?>"><?php echo esc_html( $masked_sess ); ?></code>
                                <button type="button" class="cs-btn-secondary cs-btn-sm" id="cs-ta-session-url-show">👁 <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                                <button type="button" class="cs-btn-secondary cs-btn-sm cs-copy-url" data-url="<?php echo esc_attr( $session_url ); ?>">⎘ <?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></button>
                            </div>
                        </div>
                    </div>

                    <!-- Logout URL -->
                    <div class="cs-sec-row" style="margin-top:8px;">
                        <span class="cs-sec-label"><?php esc_html_e( 'Logout URL:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
                                <code id="cs-ta-logout-url-display" style="background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;padding:4px 10px;font-size:11px;word-break:break-all;" data-real="<?php echo esc_attr( $logout_url ); ?>" data-masked="<?php echo esc_attr( $masked_logout ); ?>"><?php echo esc_html( $masked_logout ); ?></code>
                                <button type="button" class="cs-btn-secondary cs-btn-sm" id="cs-ta-logout-url-show">👁 <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                                <button type="button" class="cs-btn-secondary cs-btn-sm cs-copy-url" data-url="<?php echo esc_attr( $logout_url ); ?>">⎘ <?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></button>
                            </div>
                        </div>
                    </div>

                    <!-- Block Basic Auth toggle -->
                    <?php
                    $app_pw_usage = CSDT_Test_Accounts::get_app_password_usage();
                    $app_pw_users = $app_pw_usage['users'];
                    $app_pw_total = $app_pw_usage['passwords'];
                    if ( $app_pw_total === 0 ) {
                        $usage_badge_bg     = '#f0fdf4';
						$usage_badge_border = '#86efac';
						$usage_badge_color  = '#166534';
                        $usage_badge_text   = '✓ No app passwords in use, safe to enable block';
                    } else {
                        $usage_badge_bg     = '#fef2f2';
						$usage_badge_border = '#fca5a5';
						$usage_badge_color  = '#991b1b';
                        $usage_badge_text   = sprintf(
                            /* translators: 1: number of app passwords, 2: number of users */
                            _n( '%1$d app password across %2$d user, identify the integration before blocking', '%1$d app passwords across %2$d users, identify the integrations before blocking', $app_pw_total, 'cloudscale-cyber-devtools' ),
                            $app_pw_total,
                            $app_pw_users
                        );
                    }
                    ?>
                    <div class="cs-sec-row" style="margin-top:8px;">
                        <span class="cs-sec-label"><?php esc_html_e( 'Block Basic Auth:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <div id="cs-app-pw-usage-badge" style="display:inline-block;background:<?php echo esc_attr( $usage_badge_bg ); ?>;border:1px solid <?php echo esc_attr( $usage_badge_border ); ?>;border-radius:6px;padding:4px 10px;font-size:12px;font-weight:600;color:<?php echo esc_attr( $usage_badge_color ); ?>;margin-bottom:8px;">
                                <?php echo esc_html( $usage_badge_text ); ?>
                            </div>
                            <?php
                            if ( $app_pw_total > 0 ) :
                                $app_pw_details = CSDT_Test_Accounts::get_app_password_details();
								?>
                            <div style="margin-bottom:8px;">
                                <button type="button" id="cs-app-pw-list-toggle" class="cs-btn-secondary cs-btn-sm" onclick="var d=document.getElementById('cs-app-pw-list');var open=d.style.display==='none';d.style.display=open?'block':'none';this.textContent=open?'▼ Hide app passwords':'▶ Show app passwords';">▶ Show app passwords</button>
                                <div id="cs-app-pw-list" style="display:none;margin-top:8px;border:1px solid #e5e7eb;border-radius:6px;overflow:hidden;">
                                    <table style="width:100%;border-collapse:collapse;font-size:11px;">
                                        <thead>
                                            <tr style="background:#f8fafc;border-bottom:1px solid #e5e7eb;">
                                                <th style="padding:6px 10px;width:28px;"><input type="checkbox" id="cs-app-pw-select-all" title="Select all" style="cursor:pointer;"></th>
                                                <th style="padding:6px 10px;text-align:left;color:#374151;font-weight:600;">App name</th>
                                                <th style="padding:6px 10px;text-align:left;color:#374151;font-weight:600;">User</th>
                                                <th style="padding:6px 10px;text-align:left;color:#374151;font-weight:600;">Created</th>
                                                <th style="padding:6px 10px;text-align:left;color:#374151;font-weight:600;">Last used</th>
                                                <th style="padding:6px 10px;text-align:left;color:#374151;font-weight:600;">Last IP</th>
                                                <th style="padding:6px 2px;"></th>
                                            </tr>
                                        </thead>
                                        <tbody id="cs-app-pw-tbody">
                                        <?php
                                        foreach ( $app_pw_details as $u_entry ) :
                                            foreach ( $u_entry['passwords'] as $pw ) :
                                                $created_str   = $pw['created'] ? gmdate( 'j M Y', $pw['created'] ) : '·';
                                                $last_used_str = $pw['last_used'] ? human_time_diff( $pw['last_used'] ) . ' ago' : 'Never';
                                                $last_ip_str   = $pw['last_ip'] ?: '·';
                                                $never         = ! $pw['last_used'];
												?>
                                            <tr data-uuid="<?php echo esc_attr( $pw['uuid'] ); ?>" data-user="<?php echo esc_attr( (string) $u_entry['user_id'] ); ?>" style="border-bottom:1px solid #f1f5f9;">
                                                <td style="padding:5px 10px;"><input type="checkbox" class="cs-app-pw-row-cb" style="cursor:pointer;"></td>
                                                <td style="padding:5px 10px;color:#111827;font-weight:600;"><?php echo esc_html( $pw['name'] ?: '(unnamed)' ); ?></td>
                                                <td style="padding:5px 10px;color:#6b7280;"><?php echo esc_html( $u_entry['display_name'] ); ?></td>
                                                <td style="padding:5px 10px;color:#6b7280;"><?php echo esc_html( $created_str ); ?></td>
                                                <td style="padding:5px 10px;color:<?php echo $never ? '#9ca3af' : '#374151'; ?>;"><?php echo esc_html( $last_used_str ); ?></td>
                                                <td style="padding:5px 10px;color:#6b7280;font-family:monospace;"><?php echo esc_html( $last_ip_str ); ?></td>
                                                <td style="padding:5px 8px;text-align:right;">
                                                    <button type="button" class="cs-btn-secondary cs-btn-sm cs-app-pw-delete-btn" style="color:#dc2626;border-color:#fca5a5;font-size:10px;padding:2px 7px;">✕ Delete</button>
                                                </td>
                                            </tr>
												<?php
                                        endforeach;
endforeach;
										?>
                                        </tbody>
                                    </table>
                                    <div style="padding:6px 10px;background:#f8fafc;border-top:1px solid #e5e7eb;display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                                        <span id="cs-app-pw-sel-count" style="display:none;font-size:11px;color:#374151;flex:1;"></span>
                                        <button type="button" id="cs-app-pw-delete-selected" class="cs-btn-secondary cs-btn-sm" style="display:none;color:#dc2626;border-color:#fca5a5;font-size:10px;padding:2px 7px;">✕ Delete selected</button>
                                        <button type="button" id="cs-app-pw-delete-all" class="cs-btn-secondary cs-btn-sm" style="color:#dc2626;border-color:#fca5a5;font-size:10px;padding:2px 7px;">✕ Delete all</button>
                                    </div>
                                    <div id="cs-app-pw-delete-msg" style="display:none;padding:6px 10px;font-size:11px;"></div>
                                </div>
                            </div>
                            <?php endif; ?>
                            <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
                                <label class="cs-toggle-label" style="margin:0;">
                                    <input type="checkbox" id="cs-block-basic-auth-toggle" <?php checked( get_option( 'csdt_block_basic_auth', '0' ), '1' ); ?>>
                                    <span class="cs-toggle-switch"></span>
                                    <span class="cs-toggle-text"><?php esc_html_e( 'Block all app passwords / Basic Auth', 'cloudscale-cyber-devtools' ); ?></span>
                                </label>
                                <button type="button" id="cs-block-basic-auth-save" class="cs-btn-secondary cs-btn-sm"><?php esc_html_e( 'Save', 'cloudscale-cyber-devtools' ); ?></button>
                                <span id="cs-block-basic-auth-hint" style="font-size:12px;color:#6b7280;"></span>
                            </div>
                            <span class="cs-hint" style="margin-top:4px;"><?php esc_html_e( 'Enable if you use 2FA and don\'t publish via the WP mobile app or any external REST API integration. Test account sessions are unaffected.', 'cloudscale-cyber-devtools' ); ?></span>
                        </div>
                    </div>
                    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- inline auth block button handler; wp_add_inline_script not available at this render point ?>
                    <script>
                    (function() {
                        var btn = document.getElementById('cs-block-basic-auth-save');
                        if (!btn) { return; }
                        btn.addEventListener('click', function() {
                            var toggle  = document.getElementById('cs-block-basic-auth-toggle');
                            var hint    = document.getElementById('cs-block-basic-auth-hint');
                            var enabled = toggle && toggle.checked ? '1' : '0';
                            var ajaxUrl = '<?php echo esc_js( admin_url( 'admin-ajax.php' ) ); ?>';
                            var nonce   = (typeof csdtTestAccounts !== 'undefined' ? csdtTestAccounts.nonce : '<?php echo esc_js( wp_create_nonce( 'csdt_devtools_login_nonce' ) ); ?>');
                            btn.disabled = true;
                            btn.textContent = '...';
                            var fd = new FormData();
                            fd.append('action',  'csdt_toggle_block_basic_auth');
                            fd.append('nonce',   nonce);
                            fd.append('enabled', enabled);
                            fetch(ajaxUrl, { method: 'POST', body: fd })
                                .then(function(r) { return r.text().then(function(t) { return { status: r.status, txt: t }; }); })
                                .then(function(res) {
                                    btn.disabled = false;
                                    btn.textContent = '<?php esc_html_e( 'Save', 'cloudscale-cyber-devtools' ); ?>';
                                    var resp = null;
                                    try { resp = JSON.parse(res.txt); } catch(e) {}
                                    if (resp && resp.success) {
                                        if (hint) { hint.textContent = '✓ Saved'; hint.style.color = '#166534'; setTimeout(function(){ hint.textContent = ''; }, 2000); }
                                        var usage = resp.data && resp.data.usage;
                                        var badge = document.getElementById('cs-app-pw-usage-badge');
                                        if (badge && usage) {
                                            var n = usage.passwords, u = usage.users;
                                            if (n === 0) {
                                                badge.style.background = '#f0fdf4'; badge.style.borderColor = '#86efac'; badge.style.color = '#166534';
                                                badge.textContent = '✓ No app passwords in use, safe to enable block';
                                            } else {
                                                badge.style.background = '#fef2f2'; badge.style.borderColor = '#fca5a5'; badge.style.color = '#991b1b';
                                                badge.textContent = n + ' app password' + (n !== 1 ? 's' : '') + ' across ' + u + ' user' + (u !== 1 ? 's' : '') + ', identify the integration' + (n !== 1 ? 's' : '') + ' before blocking';
                                            }
                                        }
                                    } else {
                                        var msg = resp ? (resp.data || 'Unknown error') : 'Server error (HTTP ' + res.status + ')';
                                        if (hint) { hint.textContent = '✗ ' + msg; hint.style.color = '#dc2626'; }
                                    }
                                })
                                .catch(function(e) {
                                    btn.disabled = false;
                                    btn.textContent = '<?php esc_html_e( 'Save', 'cloudscale-cyber-devtools' ); ?>';
                                    if (hint) { hint.textContent = '✗ ' + (e && e.message ? e.message : 'network error'); hint.style.color = '#dc2626'; }
                                });
                        });
                    }());
                    </script>
                    <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- inline delete handler ?>
                    <script>
                    (function() {
                        var tbody     = document.getElementById('cs-app-pw-tbody');
                        var msgEl     = document.getElementById('cs-app-pw-delete-msg');
                        var selectAll = document.getElementById('cs-app-pw-select-all');
                        var selCount  = document.getElementById('cs-app-pw-sel-count');
                        var delSelBtn = document.getElementById('cs-app-pw-delete-selected');
                        var delAllBtn = document.getElementById('cs-app-pw-delete-all');
                        if (!tbody) { return; }
                        var ajaxUrl = '<?php echo esc_js( admin_url( 'admin-ajax.php' ) ); ?>';
                        var nonce   = (typeof csdtTestAccounts !== 'undefined' ? csdtTestAccounts.nonce : '<?php echo esc_js( wp_create_nonce( 'csdt_devtools_login_nonce' ) ); ?>');

                        function updateBadgeAndPanel(usage) {
                            var badge = document.getElementById('cs-app-pw-usage-badge');
                            if (!badge || !usage) { return; }
                            var n = usage.passwords, u = usage.users;
                            if (n === 0) {
                                badge.style.background = '#f0fdf4'; badge.style.borderColor = '#86efac'; badge.style.color = '#166534';
                                badge.textContent = '✓ No app passwords in use, safe to enable block';
                                var panel = document.getElementById('cs-app-pw-list');
                                if (panel) { panel.closest('div').style.display = 'none'; }
                            } else {
                                badge.style.background = '#fef2f2'; badge.style.borderColor = '#fca5a5'; badge.style.color = '#991b1b';
                                badge.textContent = n + ' app password' + (n !== 1 ? 's' : '') + ' across ' + u + ' user' + (u !== 1 ? 's' : '') + ', identify the integration' + (n !== 1 ? 's' : '') + ' before blocking';
                            }
                        }

                        function syncSelectionUI() {
                            var all     = tbody.querySelectorAll('.cs-app-pw-row-cb');
                            var checked = tbody.querySelectorAll('.cs-app-pw-row-cb:checked');
                            var n = checked.length;
                            if (selCount) { selCount.style.display = n > 0 ? 'inline' : 'none'; selCount.textContent = n + ' selected'; }
                            if (delSelBtn) { delSelBtn.style.display = n > 0 ? 'inline-block' : 'none'; }
                            if (selectAll) {
                                selectAll.checked       = all.length > 0 && n === all.length;
                                selectAll.indeterminate = n > 0 && n < all.length;
                            }
                        }

                        if (selectAll) {
                            selectAll.addEventListener('change', function() {
                                tbody.querySelectorAll('.cs-app-pw-row-cb').forEach(function(cb) { cb.checked = selectAll.checked; });
                                syncSelectionUI();
                            });
                        }

                        tbody.addEventListener('change', function(e) {
                            if (e.target.classList.contains('cs-app-pw-row-cb')) { syncSelectionUI(); }
                        });

                        function bulkDelete(rows) {
                            if (!rows.length) { return; }
                            var label = rows.length + ' app password' + (rows.length !== 1 ? 's' : '');
                            if (!confirm('Delete ' + label + '?\n\nAny integration using them will stop working immediately.')) { return; }
                            var items = rows.map(function(r) { return { user_id: r.dataset.user, uuid: r.dataset.uuid }; });
                            if (delSelBtn) { delSelBtn.disabled = true; }
                            if (delAllBtn) { delAllBtn.disabled = true; }
                            var fd = new FormData();
                            fd.append('action', 'csdt_bulk_delete_app_passwords');
                            fd.append('nonce',  nonce);
                            fd.append('items',  JSON.stringify(items));
                            fetch(ajaxUrl, { method: 'POST', body: fd })
                                .then(function(r) { return r.json(); })
                                .then(function(resp) {
                                    if (delSelBtn) { delSelBtn.disabled = false; }
                                    if (delAllBtn) { delAllBtn.disabled = false; }
                                    if (resp && resp.success) {
                                        rows.forEach(function(r) { r.style.transition = 'opacity .3s'; r.style.opacity = '0'; setTimeout(function() { r.remove(); }, 300); });
                                        setTimeout(function() { syncSelectionUI(); }, 350);
                                        updateBadgeAndPanel(resp.data && resp.data.usage);
                                        var n = (resp.data && resp.data.deleted) || rows.length;
                                        if (msgEl) { msgEl.style.display = 'block'; msgEl.style.color = '#166534'; msgEl.textContent = '✓ Deleted ' + n + ' password' + (n !== 1 ? 's' : ''); setTimeout(function(){ msgEl.style.display = 'none'; }, 3000); }
                                    } else {
                                        if (msgEl) { msgEl.style.display = 'block'; msgEl.style.color = '#dc2626'; msgEl.textContent = '✗ ' + (resp && resp.data ? resp.data : 'Error'); }
                                    }
                                })
                                .catch(function(e) {
                                    if (delSelBtn) { delSelBtn.disabled = false; }
                                    if (delAllBtn) { delAllBtn.disabled = false; }
                                    if (msgEl) { msgEl.style.display = 'block'; msgEl.style.color = '#dc2626'; msgEl.textContent = '✗ ' + (e && e.message ? e.message : 'network error'); }
                                });
                        }

                        if (delSelBtn) {
                            delSelBtn.addEventListener('click', function() {
                                var rows = Array.from(tbody.querySelectorAll('.cs-app-pw-row-cb:checked')).map(function(cb) { return cb.closest('tr'); });
                                bulkDelete(rows);
                            });
                        }

                        if (delAllBtn) {
                            delAllBtn.addEventListener('click', function() {
                                bulkDelete(Array.from(tbody.querySelectorAll('tr')));
                            });
                        }

                        tbody.addEventListener('click', function(e) {
                            var btn = e.target.closest('.cs-app-pw-delete-btn');
                            if (!btn) { return; }
                            var row    = btn.closest('tr');
                            var uuid   = row ? row.dataset.uuid : '';
                            var userId = row ? row.dataset.user : '';
                            if (!uuid || !userId) { return; }
                            var appName = row.cells[1] ? row.cells[1].textContent.trim() : uuid;
                            if (!confirm('Delete app password "' + appName + '"?\n\nAny integration using it will stop working immediately.')) { return; }
                            btn.disabled = true;
                            btn.textContent = '…';
                            var fd = new FormData();
                            fd.append('action',  'csdt_delete_app_password');
                            fd.append('nonce',   nonce);
                            fd.append('user_id', userId);
                            fd.append('uuid',    uuid);
                            fetch(ajaxUrl, { method: 'POST', body: fd })
                                .then(function(r) { return r.json(); })
                                .then(function(resp) {
                                    if (resp && resp.success) {
                                        row.style.transition = 'opacity .3s';
                                        row.style.opacity = '0';
                                        setTimeout(function() { row.remove(); syncSelectionUI(); }, 300);
                                        updateBadgeAndPanel(resp.data && resp.data.usage);
                                        if (msgEl) { msgEl.style.display = 'block'; msgEl.style.color = '#166534'; msgEl.textContent = '✓ Deleted "' + appName + '"'; setTimeout(function(){ msgEl.style.display = 'none'; }, 3000); }
                                    } else {
                                        btn.disabled = false; btn.textContent = '✕ Delete';
                                        if (msgEl) { msgEl.style.display = 'block'; msgEl.style.color = '#dc2626'; msgEl.textContent = '✗ ' + (resp && resp.data ? resp.data : 'Error'); }
                                    }
                                })
                                .catch(function(e) {
                                    btn.disabled = false; btn.textContent = '✕ Delete';
                                    if (msgEl) { msgEl.style.display = 'block'; msgEl.style.color = '#dc2626'; msgEl.textContent = '✗ ' + (e && e.message ? e.message : 'network error'); }
                                });
                        });
                    }());
                    </script>

                    <hr class="cs-sec-divider" style="margin:8px 0;">

                    <!-- Step 4: Active test users + sessions -->
                    <div class="cs-sec-row" style="align-items:flex-start;">
                        <span class="cs-sec-label" style="padding-top:4px;"><?php esc_html_e( 'Test users:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control" style="flex:1;">
                            <div id="cs-pwr-users-list">
                            <?php if ( empty( $test_users ) ) : ?>
                                <p style="color:#9ca3af;font-size:13px;margin:0;"><?php esc_html_e( 'No test users yet - create one above.', 'cloudscale-cyber-devtools' ); ?></p>
                            <?php else : ?>
                                <?php
                                foreach ( $test_users as $u ) :
                                    $last_login_str = '';
                                    if ( ! empty( $u['last_login'] ) ) {
                                        $diff = time() - (int) $u['last_login'];
                                        if ( $diff < 60 ) {
											$last_login_str = __( 'just now', 'cloudscale-cyber-devtools' ); }
                                        /* translators: %d is the number of minutes */
                                        elseif ( $diff < 3600 ) {
											$last_login_str = sprintf( __( '%dm ago', 'cloudscale-cyber-devtools' ), (int) floor( $diff / 60 ) ); /* phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment */ }
                                        /* translators: %d is the number of hours */
                                        elseif ( $diff < 86400 ) {
											$last_login_str = sprintf( __( '%dh ago', 'cloudscale-cyber-devtools' ), (int) floor( $diff / 3600 ) ); /* phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment */ } else {
											$last_login_str = wp_date( 'M j', $u['last_login'] ); }
                                    }
									?>
                                <div class="cs-pwr-user-row" style="display:flex;align-items:center;gap:10px;padding:8px 12px;margin-bottom:4px;background:#f9fafb;border-radius:6px;border:1px solid #e5e7eb;flex-wrap:wrap;">
                                    <code style="font-size:12px;min-width:120px;"><?php echo esc_html( $u['name'] ); ?></code>
                                    <span style="font-size:12px;color:#6b7280;"><?php echo esc_html( ucfirst( $u['wp_role'] ?: 'administrator' ) ); ?></span>
                                    <span style="font-size:12px;color:#9ca3af;font-family:monospace;"><?php echo esc_html( $u['username'] ); ?></span>
                                    <span class="cs-pwr-sess-count" style="font-size:12px;color:<?php echo $u['session_count'] > 0 ? '#d97706' : '#9ca3af'; ?>">
                                        <?php echo esc_html( $u['session_count'] ); ?> <?php echo $u['session_count'] === 1 ? esc_html__( 'session', 'cloudscale-cyber-devtools' ) : esc_html__( 'sessions', 'cloudscale-cyber-devtools' ); ?>
                                    </span>
                                    <?php if ( $last_login_str ) : ?>
                                    <span style="font-size:11px;color:#9ca3af;"><?php echo esc_html__( 'Last login:', 'cloudscale-cyber-devtools' ); ?> <?php echo esc_html( $last_login_str ); ?></span>
                                    <?php endif; ?>
                                    <div style="margin-left:auto;display:flex;gap:6px;flex-shrink:0;flex-wrap:wrap;align-items:center;">
                                        <button type="button" class="cs-btn-secondary cs-btn-sm cs-pwr-rename"
                                                data-name="<?php echo esc_attr( $u['name'] ); ?>"
                                                data-user-id="<?php echo (int) $u['user_id']; ?>"
                                                data-current-login="<?php echo esc_attr( $u['username'] ); ?>"
                                                title="<?php esc_attr_e( 'Change the WP username for this test account', 'cloudscale-cyber-devtools' ); ?>">
                                            ✏️ <?php esc_html_e( 'Rename', 'cloudscale-cyber-devtools' ); ?>
                                        </button>
                                        <button type="button" class="cs-btn-secondary cs-btn-sm cs-pwr-kill-sessions" data-name="<?php echo esc_attr( $u['name'] ); ?>" <?php echo $u['session_count'] === 0 ? 'disabled' : ''; ?>>
                                            <?php esc_html_e( 'Kill Sessions', 'cloudscale-cyber-devtools' ); ?>
                                        </button>
                                        <button type="button" class="cs-btn-secondary cs-btn-sm cs-pwr-delete" data-name="<?php echo esc_attr( $u['name'] ); ?>" style="color:#dc2626;border-color:#fca5a5;">
                                            <?php esc_html_e( 'Delete User', 'cloudscale-cyber-devtools' ); ?>
                                        </button>
                                    </div>
                                </div>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </div>

                            <!-- API attack chart + table shown in the Brute-Force panel above, no duplicate here -->

                            <!-- .env.test snippet -->
                            <details style="margin-top:16px;" id="cs-pwr-snippet-details">
                                <summary style="cursor:pointer;font-size:12px;font-weight:600;color:#1e40af;user-select:none;"><?php esc_html_e( 'Show .env.test snippet', 'cloudscale-cyber-devtools' ); ?></summary>
                                <div style="margin-top:8px;">
                                    <pre id="cs-pwr-snippet" style="margin:0 0 8px;padding:12px;background:#0f172a;color:#e2e8f0;border-radius:6px;font-size:11px;line-height:1.7;overflow-x:auto;white-space:pre;"></pre>
                                    <button type="button" id="cs-pwr-copy-snippet" class="cs-btn-secondary cs-btn-sm">⎘ <?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></button>
                                </div>
                            </details>
                        </div>
                    </div>

                </div>
            </div>
        </div>

        <?php
    }

    /*
    ==================================================================
        6. SQL COMMAND: Query validation + AJAX
        ================================================================== */

    /**
     * Returns true when the SQL string begins with a read-only keyword and contains no semicolons.
     *
     * @since  1.6.0
     * @param  string $sql Raw SQL string to validate.
     * @return bool
     */
    private static function is_safe_query( string $sql ): bool {
        $clean = trim( $sql );
        // Strip all block comments (including mid-query MySQL /*!...*/ optimizer hints),
        // line comments (-- and #), and surrounding whitespace before keyword check.
        $clean = preg_replace( '/\/\*.*?\*\//s', '', $clean );
        $clean = preg_replace( '/(--|#)[^\n]*/m', '', $clean );
        $clean = trim( $clean );
        // Strip a single trailing semicolon, a statement terminator is harmless on its own.
        $clean = rtrim( rtrim( $clean ), ';' );
        // Reject any semicolon remaining mid-query, prevents statement stacking
        // (e.g. SELECT 1; DROP TABLE wp_users).
        if ( strpos( $clean, ';' ) !== false ) {
            return false;
        }
        // Reject file-system abuse clauses regardless of SELECT keyword.
        if ( preg_match( '/\b(INTO\s+OUTFILE|INTO\s+DUMPFILE|LOAD_FILE)\b/i', $clean ) ) {
            return false;
        }
        if ( preg_match( '/^(\w+)/i', $clean, $m ) ) {
            $first = strtoupper( $m[1] );
            return in_array( $first, array( 'SELECT', 'SHOW', 'DESCRIBE', 'DESC', 'EXPLAIN' ), true );
        }
        return false;
    }

    /**
     * AJAX handler: executes a validated read-only SQL query and returns results as JSON.
     *
     * @since  1.6.0
     * @return void Sends JSON response and exits.
     */
    public static function ajax_sql_run(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Forbidden', 403 );
        }
        if ( ! check_ajax_referer( self::SQL_NONCE, 'nonce', false ) ) {
            wp_send_json_error( 'Bad nonce', 403 );
        }

        try {
            $raw = isset( $_POST['sql'] ) ? $_POST['sql'] : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- raw SQL for admin tool; unslashed on next line, validated via is_safe_query()
            $sql = trim( wp_unslash( $raw ) );
            if ( ! $sql ) {
                wp_send_json_error( 'Empty query' );
            }

            if ( ! self::is_safe_query( $sql ) ) {
                wp_send_json_error( 'Only SELECT, SHOW, DESCRIBE, and EXPLAIN queries are allowed. Do not include shell commands like sudo or mysql.' );
            }

            global $wpdb;
            $wpdb->suppress_errors( true );
            $start = microtime( true );
            // prepare() cannot be applied to a free-form admin SQL tool, the entire
            // query is the user's input, leaving no placeholders to bind. Safety is
            // provided by is_safe_query() (read-only keywords + no semicolons),
            // manage_options capability gate, and nonce verification above.
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- free-form read-only SQL tool; safety enforced by is_safe_query() allowlist, manage_options cap, and nonce
            $results = $wpdb->get_results( $sql, ARRAY_A );
            $elapsed = round( ( microtime( true ) - $start ) * 1000, 2 );
            $error   = $wpdb->last_error;
            $wpdb->suppress_errors( false );

            if ( $error ) {
                wp_send_json_error( $error );
            }

            wp_send_json_success(
                array(
					'rows'    => $results,
					'count'   => count( $results ),
					'elapsed' => $elapsed,
                )
            );
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }

    public static function ajax_sql_http_fix(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Forbidden', 403 );
        }
        if ( ! check_ajax_referer( self::SQL_NONCE, 'nonce', false ) ) {
            wp_send_json_error( 'Bad nonce', 403 );
        }

        $dry_run = isset( $_POST['dry_run'] ) && '1' === $_POST['dry_run'];
        $host    = wp_parse_url( home_url(), PHP_URL_HOST );
        $from    = 'http://' . $host;
        $to      = 'https://' . $host;

        global $wpdb;

        $tables      = $wpdb->get_col( 'SHOW TABLES' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $total_cells = 0;
        $lines       = array();

        foreach ( $tables as $table ) {
            if ( strpos( $table, '_trash_' ) !== false ) {
                continue;
            }
            // Get primary key and all text-like columns.
            $columns   = $wpdb->get_results( $wpdb->prepare( 'DESCRIBE %i', $table ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- introspecting table structure; no user input
            $pk        = null;
            $text_cols = array();
            foreach ( $columns as $col ) {
                if ( strtolower( $col['Key'] ) === 'pri' && ! $pk ) {
                    $pk = $col['Field'];
                }
                $type = strtolower( $col['Type'] );
                if ( strpos( $type, 'char' ) !== false || strpos( $type, 'text' ) !== false
                    || strpos( $type, 'blob' ) !== false || strpos( $type, 'json' ) !== false ) {
                    // Skip guid column.
                    if ( $col['Field'] !== 'guid' ) {
                        $text_cols[] = $col['Field'];
                    }
                }
            }
            if ( empty( $text_cols ) || ! $pk ) {
                continue;
            }

            $col_identifiers = array_merge( array( $pk ), $text_cols );
            $id_placeholders = implode( ', ', array_fill( 0, count( $col_identifiers ), '%i' ) );
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- dynamic column list from information_schema; no user input
            $rows    = $wpdb->get_results( $wpdb->prepare( "SELECT {$id_placeholders} FROM %i", ...array_merge( $col_identifiers, array( $table ) ) ), ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- $id_placeholders contains only %i tokens built from information_schema; no user input
            $changed = 0;
            foreach ( (array) $rows as $row ) {
                $pk_val  = $row[ $pk ];
                $updates = array();
                foreach ( $text_cols as $col ) {
                    $orig = $row[ $col ];
                    if ( $orig === null || strpos( $orig, $from ) === false ) {
                        continue;
                    }
                    $new = CSDT_Code_Migrator::recursive_str_replace( $from, $to, $orig );
                    if ( $new !== $orig ) {
                        $updates[ $col ] = $new;
                    }
                }
                if ( $updates ) {
                    ++$changed;
                    ++$total_cells;
                    if ( ! $dry_run ) {
                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
                        $wpdb->update( $table, $updates, array( $pk => $pk_val ) );
                    }
                }
            }
            if ( $changed ) {
                $lines[] = sprintf( '%s: %d row(s) updated', $table, $changed );
            }
        }

        $lines[] = sprintf( '--- Total: %d cell(s) %s', $total_cells, $dry_run ? 'would be updated (dry run)' : 'updated' );

        wp_send_json_success(
            array(
				'output'  => implode( "\n", $lines ),
				'dry_run' => $dry_run,
				'from'    => $from,
				'to'      => $to,
				'total'   => $total_cells,
            )
        );
    }

    /*
    ==================================================================
        8. LOGIN SECURITY, Hook registrations (class extracted to class-login.php)
        ================================================================== */


    public static function register_dashboard_widget(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
			return; }
        wp_add_dashboard_widget(
            'csdt_security_summary',
            '🔐 Cyber Devtools',
            array( __CLASS__, 'render_dashboard_widget' )
        );
    }

    public static function render_dashboard_widget(): void {
        $ai_cfg       = CSDT_AI_Dispatcher::get_config();
        $ai_provider  = $ai_cfg['provider'];
        $has_key      = ! empty( $ai_cfg['key'] );
        $provider_lbl = $ai_provider === 'gemini' ? 'Google Gemini' : 'Anthropic Claude';

        $combined_cc    = method_exists( 'CSDT_Site_Audit', 'get_combined_counts' )
            ? CSDT_Site_Audit::get_combined_counts()
            : array(
				'scan_critical'  => 0,
				'scan_high'      => 0,
				'audit_critical' => null,
				'audit_high'     => null,
				'audit_run_at'   => 0,
				'either_run'     => false,
			);
        $scan_crit_n    = $combined_cc['scan_critical'];
        $scan_high_n    = $combined_cc['scan_high'];
        $audit_critical = $combined_cc['audit_critical'];
        $audit_high     = $combined_cc['audit_high'];
        $audit_run_at   = $combined_cc['audit_run_at'];

        $history   = get_option( 'csdt_scan_history', array() );
        $last_scan = ! empty( $history ) ? $history[0] : null;
        $score_cls = '#888';
        if ( $last_scan ) {
            $s         = (int) ( $last_scan['score'] ?? 0 );
            $score_cls = $s >= 75 ? '#16a34a' : ( $s >= 55 ? '#d97706' : '#dc2626' );
        }

        $bf_on               = get_option( 'csdt_devtools_brute_force_enabled', '1' ) === '1';
        $login_slug          = get_option( 'csdt_devtools_login_slug', '' );
        $force_2fa           = get_option( 'csdt_devtools_2fa_force_admins', '0' ) === '1';
        $email_2fa           = get_option( 'csdt_devtools_2fa_method', 'off' ) === 'email';
        $playwright_user_ids = array_column( (array) get_option( 'csdt_playwright_roles', array() ), 'user_id' );
        $admins              = get_users( array( 'role' => 'administrator' ) );
        $admins              = array_filter( $admins, static fn( $u ) => ! in_array( $u->ID, $playwright_user_ids, true ) );
        $adm_tot             = count( $admins );
        $adm_2fa             = 0;
        $admins_missing_2fa  = array();
        foreach ( $admins as $u ) {
            if ( get_user_meta( $u->ID, 'csdt_devtools_totp_enabled', true ) === '1'
                || ! empty( get_user_meta( $u->ID, 'csdt_devtools_passkeys', true ) )
                || $email_2fa ) {
                ++$adm_2fa;
            } else {
                $admins_missing_2fa[] = $u->user_login;
            }
        }

        // Extra login security stats.
        $bf_log       = get_option( 'csdt_devtools_bf_log', array() );
        $today_str    = gmdate( 'Y-m-d' );
        $failed_today = 0;
        $failed_7d    = 0;
        $cutoff_7d    = time() - 7 * DAY_IN_SECONDS;
        foreach ( $bf_log as $entry ) {
            $ts = (int) ( $entry[0] ?? $entry['time'] ?? 0 );
            if ( gmdate( 'Y-m-d', $ts ) === $today_str ) {
				++$failed_today; }
            if ( $ts >= $cutoff_7d ) {
				++$failed_7d; }
        }

        // Deduplicated failed-login entries for the widget: one row per IP (most recent),
        // with today's count per IP. Sorted newest first, capped at 10 unique IPs.
        $bf_log_reversed = array_reverse( $bf_log );
        $bf_seen_ips     = array();
        $bf_ip_today     = array();
        $bf_widget_rows  = array();
        foreach ( $bf_log_reversed as $entry ) {
            $ts  = (int) ( $entry[0] ?? $entry['time'] ?? 0 );
            $usr = (string) ( $entry[1] ?? $entry['username'] ?? '' );
            $ip  = (string) ( $entry[2] ?? $entry['ip'] ?? '' );
            $cc  = (string) ( $entry[3] ?? $entry['country'] ?? '' );
            if ( gmdate( 'Y-m-d', $ts ) === $today_str ) {
                $bf_ip_today[ $ip ] = ( $bf_ip_today[ $ip ] ?? 0 ) + 1;
            }
            if ( ! isset( $bf_seen_ips[ $ip ] ) ) {
                $bf_seen_ips[ $ip ] = true;
                $bf_widget_rows[]   = array(
					'time' => $ts,
					'user' => $usr,
					'ip'   => $ip,
					'cc'   => $cc,
				);
            }
            if ( count( $bf_widget_rows ) >= 10 ) {
				break;
            }
        }

        // Three groups:
        // 1. valid, username currently exists as a WP account
        // 2. deleted, was recorded as a real-account attack (type='attack' in security events log)
        // but no longer exists in WP
        // 3. invalid, never existed; just bots guessing common names (shown as count only)
        $attack_usernames = array();
        $sec_evts         = get_option( 'csdt_security_events', array() );
        if ( is_array( $sec_evts ) ) {
            foreach ( $sec_evts as $se ) {
                if ( ( $se['type'] ?? '' ) === 'attack' ) {
                    // title format: "Login attack on 'username'"
                    if ( preg_match( "/Login attack on '(.+)'/", $se['title'] ?? '', $m ) ) {
                        $attack_usernames[ $m[1] ] = true;
                    }
                }
            }
        }

        // Pre-fetch all users in two batch queries instead of one per unique username.
        $all_bf_names = array();
        foreach ( $bf_log as $e ) {
            $n = (string) ( $e[1] ?? $e['username'] ?? '' );
            if ( $n ) {
				$all_bf_names[ $n ] = true; }
        }
        $all_bf_names = array_keys( $all_bf_names );

        $bf_user_map = array(); // username => WP_User|false
        if ( $all_bf_names ) {
            foreach ( get_users(
                array(
					'login__in' => $all_bf_names,
					'number'    => -1,
                )
            ) as $found_u ) {
                $bf_user_map[ $found_u->user_login ] = $found_u;
            }
            $need_email = array_values( array_filter( $all_bf_names, fn( $n ) => ! isset( $bf_user_map[ $n ] ) ) );
            if ( $need_email ) {
                global $wpdb;
                $fmt = implode( ',', array_fill( 0, count( $need_email ), '%s' ) );
                // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- variadic spread counted incorrectly by PHPCS
                foreach ( (array) $wpdb->get_results( $wpdb->prepare( "SELECT ID, user_email FROM {$wpdb->users} WHERE user_email IN ({$fmt})", ...$need_email ) ) as $row ) {
                    $bf_user_map[ $row->user_email ] = get_userdata( $row->ID );
                }
            }
            foreach ( $all_bf_names as $n ) {
                if ( ! isset( $bf_user_map[ $n ] ) ) {
					$bf_user_map[ $n ] = false; }
            }
        }

        $valid_users   = array();
        $deleted_users = array();
        $seen_users    = array();
        foreach ( array_reverse( $bf_log ) as $entry ) {
            $usr = (string) ( $entry[1] ?? $entry['username'] ?? '' );
            if ( ! $usr || isset( $seen_users[ $usr ] ) ) {
				continue; }
            $seen_users[ $usr ] = true;
            $u                  = $bf_user_map[ $usr ] ?? false;
            if ( $u ) {
                $valid_users[ $usr ] = array(
                    'login'      => $u->user_login,
                    'email_mask' => preg_replace( '/(?<=.).(?=.*@)/u', '•', $u->user_email ),
                );
            } elseif ( isset( $attack_usernames[ $usr ] ) ) {
                // Was a real account when the attack fired the alert.
                $deleted_users[ $usr ] = array( 'login' => $usr );
            }
            // else: invalid, counts toward the summary only
        }

        $invalid_count     = count( $seen_users ) - count( $valid_users ) - count( $deleted_users );
        $invalid_count     = max( 0, $invalid_count );
        $compromised_count = count( $valid_users ) + count( $deleted_users );

        // Build ordered display list: valid accounts first, deleted second.
        $targeted_users = array();
        foreach ( $valid_users   as $u ) {
			$targeted_users[] = $u + array( 'deleted' => false ); }
        foreach ( $deleted_users as $u ) {
			$targeted_users[] = array(
				'login'      => $u['login'],
				'email_mask' => '',
				'deleted'    => true,
			); }

        $blocklist     = get_option( 'csdt_ip_blocklist', array() );
        $blocked_count = is_array( $blocklist ) ? count( $blocklist ) : 0;
        $probe_stats   = get_option( 'csdt_wplogin_blocked_stats', array() );
        $probes_today  = (int) ( $probe_stats['daily'][ $today_str ] ?? 0 );
        $session_dur   = get_option( 'csdt_devtools_session_duration', 'default' );

        // API attack count (today, from security events log).
        $all_sec_events    = get_option( 'csdt_security_events', array() );
        $all_sec_events    = is_array( $all_sec_events ) ? $all_sec_events : array();
        $api_attacks_today = 0;
        foreach ( $all_sec_events as $ev ) {
            if ( in_array( $ev['type'] ?? '', array( 'api_attack', 'rest_fail' ), true )
                && gmdate( 'Y-m-d', (int) ( $ev['time'] ?? 0 ) ) === $today_str ) {
                ++$api_attacks_today;
            }
        }

        $base_url = admin_url( 'tools.php?page=cyber-devtools' );
        ?>

        <?php
        $either_run   = $combined_cc['either_run'];
        $audit_crit_n = (int) ( $audit_critical ?? 0 );
        $audit_high_n = (int) ( $audit_high ?? 0 );

        // Score ring colours
        if ( $last_scan ) {
            $s = (int) ( $last_scan['score'] ?? 0 );
            if ( $s >= 75 ) {
                $ring_color = '#16a34a';
				$ring_bg    = '#f0fdf4';
            } elseif ( $s >= 55 ) {
                $ring_color = '#d97706';
				$ring_bg    = '#fffbeb';
            } else {
                $ring_color = '#dc2626';
				$ring_bg    = '#fef2f2';
            }
        } else {
            $ring_color = '#94a3b8';
			$ring_bg    = '#f8fafc';
        }

        // 2FA chip colour
        $tfa_all  = ( $adm_tot > 0 && $adm_2fa === $adm_tot );
        $tfa_some = ( $adm_2fa > 0 && ! $tfa_all );
        $tfa_dot  = $tfa_all ? '#16a34a' : ( $tfa_some ? '#d97706' : '#dc2626' );
        ?>

        <!-- ── API path attack urgent warning ───────────────────────── -->
        <?php if ( $api_attacks_today > 0 ) : ?>
        <a href="<?php echo esc_url( admin_url( 'tools.php?page=cyber-devtools&tab=credentials' ) ); ?>"
            style="display:flex;align-items:center;gap:8px;background:#7f1d1d;border-bottom:2px solid #ef4444;padding:8px 12px;margin:-1px -12px 10px;text-decoration:none;cursor:pointer;">
            <span style="font-size:14px;flex-shrink:0;">🚨</span>
            <div style="flex:1;min-width:0;">
                <div style="font-size:11px;font-weight:800;color:#fca5a5;letter-spacing:.02em;"><?php esc_html_e( 'API PATH UNDER ATTACK, ROTATE IMMEDIATELY', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:10px;color:#fca5a5;opacity:.8;">
                <?php
                /* translators: %d: number of login attempts today */
                echo esc_html( sprintf( _n( '%d attempt today', '%d attempts today', $api_attacks_today, 'cloudscale-cyber-devtools' ), $api_attacks_today ) );
                ?>
                &middot; <?php esc_html_e( 'Click to rotate path token', 'cloudscale-cyber-devtools' ); ?></div>
            </div>
            <span style="font-size:10px;font-weight:700;color:#fca5a5;white-space:nowrap;">↺ Rotate →</span>
        </a>
        <?php endif; ?>

        <!-- ── Dark header band ───────────────────────────────────────── -->
        <div class="cs-dw-header">
            <div class="cs-dw-header-left">
                <span style="font-size:17px;line-height:1;flex-shrink:0">🔐</span>
                <div>
                    <div class="cs-dw-header-title"><?php esc_html_e( 'Cyber Devtools', 'cloudscale-cyber-devtools' ); ?></div>
                    <div class="cs-dw-header-sub"><?php esc_html_e( 'AI security · 2FA · Login protection', 'cloudscale-cyber-devtools' ); ?></div>
                </div>
            </div>
            <div class="cs-dw-header-right">
                <?php if ( $last_scan ) : ?>
                <div class="cs-dw-hpill" title="<?php echo esc_attr( __( 'Security scan', 'cloudscale-cyber-devtools' ) ); ?>" style="color:<?php echo $scan_crit_n > 0 ? '#fca5a5' : '#86efac'; ?>">
                    <span class="cs-dw-hpill-num"><?php echo esc_html( $scan_crit_n ); ?></span>
                    <span class="cs-dw-hpill-lbl"><?php esc_html_e( 'Scan Crit', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <div class="cs-dw-hpill" title="<?php echo esc_attr( __( 'Security scan', 'cloudscale-cyber-devtools' ) ); ?>" style="color:<?php echo $scan_high_n > 0 ? '#fcd34d' : '#86efac'; ?>">
                    <span class="cs-dw-hpill-num"><?php echo esc_html( $scan_high_n ); ?></span>
                    <span class="cs-dw-hpill-lbl"><?php esc_html_e( 'Scan High', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <?php endif; ?>
                <?php if ( null !== $audit_critical ) : ?>
                <div class="cs-dw-hpill" title="<?php echo esc_attr( __( 'Site audit', 'cloudscale-cyber-devtools' ) ); ?>" style="color:<?php echo $audit_crit_n > 0 ? '#fca5a5' : '#86efac'; ?>">
                    <span class="cs-dw-hpill-num"><?php echo esc_html( $audit_crit_n ); ?></span>
                    <span class="cs-dw-hpill-lbl"><?php esc_html_e( 'Audit Crit', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <div class="cs-dw-hpill" title="<?php echo esc_attr( __( 'Site audit', 'cloudscale-cyber-devtools' ) ); ?>" style="color:<?php echo $audit_high_n > 0 ? '#fcd34d' : '#86efac'; ?>">
                    <span class="cs-dw-hpill-num"><?php echo esc_html( $audit_high_n ); ?></span>
                    <span class="cs-dw-hpill-lbl"><?php esc_html_e( 'Audit High', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <?php endif; ?>
                <div class="cs-dw-hpill" style="color:<?php echo $failed_today > 0 ? '#fca5a5' : '#86efac'; ?>">
                    <span class="cs-dw-hpill-num"><?php echo esc_html( $failed_today ); ?></span>
                    <span class="cs-dw-hpill-lbl"><?php esc_html_e( 'Failed', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <div class="cs-dw-hpill" style="color:<?php echo $blocked_count > 0 ? '#fcd34d' : '#86efac'; ?>">
                    <span class="cs-dw-hpill-num"><?php echo esc_html( $blocked_count ); ?></span>
                    <span class="cs-dw-hpill-lbl"><?php esc_html_e( 'Blocked', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <div class="cs-dw-hpill" style="color:<?php echo $api_attacks_today > 0 ? '#fca5a5' : '#86efac'; ?>">
                    <span class="cs-dw-hpill-num"><?php echo esc_html( $api_attacks_today ); ?></span>
                    <span class="cs-dw-hpill-lbl"><?php esc_html_e( 'API', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <span style="background:rgba(15,184,224,0.15);border:1px solid rgba(15,184,224,0.4);color:#67e8f9;font-size:10px;font-weight:700;padding:3px 8px;border-radius:20px;letter-spacing:0.04em">v<?php echo esc_html( self::VERSION ); ?></span>
            </div>
        </div>

        <!-- ── Hero: score + scan times ──────────────────────────────── -->
        <div class="cs-dw-hero">
            <div class="cs-dw-score-ring" style="color:<?php echo esc_attr( $ring_color ); ?>;background:<?php echo esc_attr( $ring_bg ); ?>;">
                <?php echo $either_run ? esc_html( $last_scan['score'] ?? '·' ) : '·'; ?>
            </div>
            <div class="cs-dw-hero-meta">
                <div class="cs-dw-hero-title">
                    <?php
                    if ( $either_run && $last_scan ) {
                        echo esc_html( $last_scan['score_label'] ?? __( 'Scan complete', 'cloudscale-cyber-devtools' ) );
                    } elseif ( $has_key ) {
                        esc_html_e( 'No scan yet', 'cloudscale-cyber-devtools' );
                    } else {
                        esc_html_e( 'AI key not configured', 'cloudscale-cyber-devtools' );
                    }
                    ?>
                </div>
                <div class="cs-dw-hero-sub">
                    <?php
                    if ( $either_run ) {
                        if ( $last_scan ) {
                            /* translators: %s time diff */
                            printf( esc_html__( 'Scanned %s ago', 'cloudscale-cyber-devtools' ), esc_html( human_time_diff( (int) ( $last_scan['scanned_at'] ?? 0 ) ) ) );
                        }
                        if ( null !== $audit_critical ) {
                            echo $last_scan ? ' &middot; ' : '';
                            /* translators: %s time diff */
                            printf( esc_html__( 'Audited %s ago', 'cloudscale-cyber-devtools' ), esc_html( human_time_diff( $audit_run_at ) ) );
                        }
                    } else {
                        echo esc_html( $has_key ? $provider_lbl : __( 'Add a free Gemini key to start', 'cloudscale-cyber-devtools' ) );
                    }
                    ?>
                </div>
                <?php if ( $last_scan ) : ?>
                <div style="margin-top:6px"><a href="<?php echo esc_url( $base_url . '&tab=security' ); ?>" style="font-size:11px;font-weight:600;color:#0369a1;text-decoration:none"><?php esc_html_e( 'View full report →', 'cloudscale-cyber-devtools' ); ?></a></div>
                <?php endif; ?>
            </div>
        </div>

        <!-- ── Login Security ─────────────────────────────────────────── -->
        <div class="cs-dw-section">🔒 <?php esc_html_e( 'Login Security', 'cloudscale-cyber-devtools' ); ?></div>
        <div class="cs-dw-grid">
            <div class="cs-dw-chip">
                <span class="cs-dw-dot" style="background:<?php echo $bf_on ? '#16a34a' : '#dc2626'; ?>;"></span>
                <span>
                    <span class="cs-dw-chip-label"><?php esc_html_e( 'Brute Force', 'cloudscale-cyber-devtools' ); ?></span><br>
                    <?php echo $bf_on ? esc_html__( 'Protected', 'cloudscale-cyber-devtools' ) : esc_html__( 'Off', 'cloudscale-cyber-devtools' ); ?>
                </span>
            </div>
            <div class="cs-dw-chip" style="flex-direction:column;align-items:flex-start;">
                <div style="display:flex;align-items:center;gap:6px;">
                    <span class="cs-dw-dot" style="background:<?php echo esc_attr( $tfa_dot ); ?>;flex-shrink:0;"></span>
                    <span>
                        <span class="cs-dw-chip-label"><?php esc_html_e( '2FA Admins', 'cloudscale-cyber-devtools' ); ?></span><br>
                        <?php echo esc_html( $adm_2fa . ' / ' . $adm_tot ); ?>
                    </span>
                </div>
                <?php if ( ! empty( $admins_missing_2fa ) ) : ?>
                <details style="margin-top:6px;width:100%;">
                    <summary style="font-size:11px;color:#6366f1;cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:3px;user-select:none;">
                        <span class="csdt-2fa-chevron" style="font-size:9px;">&#9654;</span>
                        <?php esc_html_e( 'Details', 'cloudscale-cyber-devtools' ); ?>
                    </summary>
                    <ul style="margin:5px 0 0 2px;padding:0;list-style:none;font-size:11px;color:#374151;">
                        <?php foreach ( $admins_missing_2fa as $login ) : ?>
                        <li style="padding:2px 0;display:flex;align-items:center;gap:4px;">
                            <span style="color:#dc2626;font-size:9px;">&#x26A0;</span>
                            <span><?php echo esc_html( $login ); ?></span>
                            <span style="color:#9ca3af;font-size:10px;"><?php esc_html_e( ',  no 2FA', 'cloudscale-cyber-devtools' ); ?></span>
                        </li>
                        <?php endforeach; ?>
                    </ul>
                </details>
                <?php elseif ( $adm_tot > 0 ) : ?>
                <div style="margin-top:4px;font-size:11px;color:#16a34a;">&#x2713; <?php esc_html_e( 'All covered', 'cloudscale-cyber-devtools' ); ?></div>
                <?php endif; ?>
            </div>
            <div class="cs-dw-chip">
                <span class="cs-dw-dot" style="background:<?php echo ! empty( $login_slug ) ? '#16a34a' : '#dc2626'; ?>;"></span>
                <span>
                    <span class="cs-dw-chip-label"><?php esc_html_e( 'Hide Login', 'cloudscale-cyber-devtools' ); ?></span><br>
                    <?php echo ! empty( $login_slug ) ? esc_html__( 'Active', 'cloudscale-cyber-devtools' ) : esc_html__( 'Off', 'cloudscale-cyber-devtools' ); ?>
                </span>
            </div>
            <div class="cs-dw-chip">
                <span class="cs-dw-dot" style="background:<?php echo $force_2fa ? '#16a34a' : '#dc2626'; ?>;"></span>
                <span>
                    <span class="cs-dw-chip-label"><?php esc_html_e( 'Force 2FA', 'cloudscale-cyber-devtools' ); ?></span><br>
                    <?php echo $force_2fa ? esc_html__( 'On', 'cloudscale-cyber-devtools' ) : esc_html__( 'Off', 'cloudscale-cyber-devtools' ); ?>
                </span>
            </div>
            <div class="cs-dw-chip">
                <span class="cs-dw-dot" style="background:<?php echo $failed_today > 0 ? '#dc2626' : '#16a34a'; ?>;"></span>
                <span>
                    <span class="cs-dw-chip-label"><?php esc_html_e( 'Failed Logins Today', 'cloudscale-cyber-devtools' ); ?></span><br>
                    <?php echo esc_html( $failed_today ); ?>
                    <?php if ( $failed_7d > $failed_today ) : ?>
                        <span style="font-size:10px;color:#94a3b8">&nbsp;(<?php echo esc_html( $failed_7d ); ?> / 7d)</span>
                    <?php endif; ?>
                </span>
            </div>
            <div class="cs-dw-chip">
                <span class="cs-dw-dot" style="background:<?php echo $probes_today > 0 ? '#d97706' : '#16a34a'; ?>;"></span>
                <span>
                    <span class="cs-dw-chip-label"><?php esc_html_e( 'wp-login Probes Today', 'cloudscale-cyber-devtools' ); ?></span><br>
                    <?php echo esc_html( $probes_today ); ?>
                </span>
            </div>
            <div class="cs-dw-chip">
                <span class="cs-dw-dot" style="background:<?php echo $blocked_count > 0 ? '#d97706' : '#16a34a'; ?>;"></span>
                <span>
                    <span class="cs-dw-chip-label"><?php esc_html_e( 'Blocked IPs', 'cloudscale-cyber-devtools' ); ?></span><br>
                    <?php echo esc_html( $blocked_count ); ?>
                </span>
            </div>
            <div class="cs-dw-chip">
                <span class="cs-dw-dot" style="background:#16a34a;"></span>
                <span>
                    <span class="cs-dw-chip-label"><?php esc_html_e( 'Session Length', 'cloudscale-cyber-devtools' ); ?></span><br>
                    <?php
                    $dur_labels = array(
                        'default' => __( 'WP default', 'cloudscale-cyber-devtools' ),
                        '1h'      => '1 hour',
                        '4h'      => '4 hours',
                        '8h'      => '8 hours',
                        '24h'     => '24 hours',
                        '48h'     => '2 days',
                        '7d'      => '7 days',
                        '30d'     => '30 days',
                    );
                    echo esc_html( $dur_labels[ $session_dur ] ?? $session_dur );
                    ?>
                </span>
            </div>
        </div>

        <!-- ── Failed Login Attempts ─────────────────────────────────── -->
        <?php
        if ( ! empty( $bf_widget_rows ) ) :
            $latest       = $bf_widget_rows[0];
            $latest_age   = $latest['time'] ? human_time_diff( $latest['time'] ) . ' ago' : '';
            $latest_ip    = $latest['ip'];
            $latest_cc    = $latest['cc'];
            $latest_usr   = $latest['user'] ?: '';
            $latest_today = (int) ( $bf_ip_today[ $latest_ip ] ?? 0 );
            $is_blocked   = isset( $blocklist[ $latest_ip ] );
			?>
        <div class="cs-dw-section">🎯 <?php esc_html_e( 'Failed Login Attempts', 'cloudscale-cyber-devtools' ); ?></div>
        <div style="display:flex;flex-direction:column;gap:5px;margin-bottom:12px;">
            <a href="<?php echo esc_url( $base_url . '&tab=login' ); ?>" style="display:flex;align-items:flex-start;gap:7px;flex-shrink:0;background:#fff7ed;border:1px solid #fdba74;border-radius:6px;padding:6px 9px;text-decoration:none;cursor:pointer;" title="<?php esc_attr_e( 'Go to Login Security', 'cloudscale-cyber-devtools' ); ?>">
                <span style="font-size:13px;flex-shrink:0;line-height:1.4">🎯</span>
                <div style="flex:1;min-width:0">
                    <div style="font-size:11px;font-weight:700;color:#9a3412;line-height:1.3;display:flex;align-items:center;gap:5px;">
                        <?php esc_html_e( 'Failed login attempt', 'cloudscale-cyber-devtools' ); ?>
                        <?php if ( $failed_today > 0 ) : ?>
                        <span style="font-size:9px;font-weight:700;padding:1px 5px;border-radius:8px;background:#fdba74;color:#9a3412;white-space:nowrap;"><?php echo esc_html( $failed_today ); ?> today</span>
                        <?php endif; ?>
                        <?php if ( $is_blocked ) : ?>
                        <span style="font-size:9px;font-weight:700;padding:1px 5px;border-radius:8px;background:#dcfce7;color:#15803d;white-space:nowrap;">blocked</span>
                        <?php endif; ?>
                        <?php if ( $compromised_count > 0 ) : ?>
                        <span style="font-size:9px;font-weight:700;padding:1px 5px;border-radius:8px;background:#fef2f2;color:#991b1b;border:1px solid #fca5a5;white-space:nowrap;">⚠ <?php echo esc_html( $compromised_count ); ?> account<?php echo $compromised_count > 1 ? 's' : ''; ?> targeted</span>
                        <?php endif; ?>
                    </div>
                    <div style="font-size:10px;color:#64748b;margin-top:1px;">
                        <?php
                        if ( $latest_usr ) {
                            $ulen       = mb_strlen( $latest_usr );
                            $show_start = min( 2, $ulen );
                            $show_end   = $ulen > 4 ? 2 : 0;
                            $mask_len   = max( 0, $ulen - $show_start - $show_end );
                            $masked_usr = mb_substr( $latest_usr, 0, $show_start )
                                        . str_repeat( '•', $mask_len )
                                        . ( $show_end ? mb_substr( $latest_usr, -$show_end ) : '' );
                        } else {
                            $masked_usr = '·';
                        }
                        ?>
                        <?php esc_html_e( 'Username:', 'cloudscale-cyber-devtools' ); ?> <?php echo esc_html( $masked_usr ); ?> &nbsp;&middot;&nbsp;
                        IP: <?php echo esc_html( $latest_ip ); ?><?php echo $latest_cc ? ' · ' . esc_html( $latest_cc ) : ''; ?>
                    </div>
                </div>
                <?php if ( $latest_age ) : ?>
                <span style="font-size:9px;color:#94a3b8;white-space:nowrap;flex-shrink:0;margin-top:2px"><?php echo esc_html( $latest_age ); ?></span>
                <?php endif; ?>
            </a>
            <?php if ( $compromised_count > 0 ) : ?>
            <div style="margin-top:6px;">
                <button type="button"
                        onclick="var d=this.nextElementSibling;d.style.display=d.style.display==='none'?'block':'none';this.textContent=d.style.display==='none'?'👁 Show '+<?php echo (int) $compromised_count; ?>+' targeted account<?php echo $compromised_count > 1 ? 's' : ''; ?>':'🔒 Hide accounts';"
                        style="font-size:10px;font-weight:600;padding:3px 10px;background:#fef2f2;border:1px solid #fca5a5;border-radius:5px;color:#991b1b;cursor:pointer;">
                    👁 <?php echo esc_html( sprintf( /* translators: %d is the number of targeted user accounts */ _n( 'Show %d targeted account', 'Show %d targeted accounts', $compromised_count, 'cloudscale-cyber-devtools' ), $compromised_count ) ); ?>
                </button>
                <div style="display:none;margin-top:6px;background:#fef2f2;border:1px solid #fca5a5;border-radius:6px;overflow:hidden;">
                    <?php foreach ( $targeted_users as $tuser ) : ?>
                    <div style="display:flex;align-items:center;gap:8px;padding:7px 10px;border-bottom:1px solid rgba(252,165,165,.4);">
                        <?php if ( $tuser['deleted'] ) : ?>
                        <span style="font-size:14px;flex-shrink:0;">⚫</span>
                        <span style="font-size:12px;font-weight:700;color:#6b7280;flex:1;"><?php echo esc_html( $tuser['login'] ); ?></span>
                        <span style="font-size:10px;padding:2px 6px;border-radius:6px;background:#f1f5f9;color:#94a3b8;border:1px solid #e2e8f0;white-space:nowrap;"><?php esc_html_e( 'deleted', 'cloudscale-cyber-devtools' ); ?></span>
                        <?php else : ?>
                        <span style="font-size:14px;flex-shrink:0;">🔴</span>
                        <span style="font-size:12px;font-weight:700;color:#991b1b;flex:1;"><?php echo esc_html( $tuser['login'] ); ?></span>
                        <span style="font-size:10px;color:#94a3b8;white-space:nowrap;"><?php echo esc_html( $tuser['email_mask'] ); ?></span>
                        <?php endif; ?>
                    </div>
                    <?php endforeach; ?>
                    <?php if ( $invalid_count > 0 ) : ?>
                    <div style="padding:6px 10px;font-size:11px;color:#6b7280;background:#fff8f8;">
                        + <?php echo esc_html( $invalid_count ); ?> <?php esc_html_e( 'invalid username attempts (bots guessing)', 'cloudscale-cyber-devtools' ); ?>
                    </div>
                    <?php endif; ?>
                    <div style="padding:5px 10px;font-size:10px;color:#94a3b8;background:#fff8f8;"><?php esc_html_e( 'Real accounts targeted, consider changing their passwords.', 'cloudscale-cyber-devtools' ); ?></div>
                </div>
            </div>
            <?php endif; ?>
        </div>
        <?php endif; ?>

        <!-- ── Recent Security Events ────────────────────────────────── -->
        <?php
        $raw_events = get_option( 'csdt_security_events', array() );
        $raw_events = is_array( $raw_events ) ? array_reverse( $raw_events ) : array();

        // Deduplicate: keep only the latest event per type, track today's count per type.
        $today_str    = gmdate( 'Y-m-d' );
        $seen_types   = array();
        $today_counts = array();
        $deduped      = array();
        foreach ( $raw_events as $ev ) {
            $etype = (string) ( $ev['type'] ?? 'other' );
            // Normalise: treat rest_fail as api_attack for grouping/display.
            $bucket = ( $etype === 'rest_fail' ) ? 'api_attack' : $etype;
            // Count today's occurrences (keyed by bucket).
            if ( gmdate( 'Y-m-d', (int) ( $ev['time'] ?? 0 ) ) === $today_str ) {
                $today_counts[ $bucket ] = ( $today_counts[ $bucket ] ?? 0 ) + 1;
            }
            // Keep only first (most recent) of each bucket type.
            if ( ! isset( $seen_types[ $bucket ] ) ) {
                $seen_types[ $bucket ] = true;
                $deduped[]             = $ev;
            }
        }
        // Cap at 10 unique types.
        $deduped = array_slice( $deduped, 0, 10 );

        if ( ! empty( $deduped ) ) :
			?>
        <div class="cs-dw-section">⚠️ <?php esc_html_e( 'Recent Security Events', 'cloudscale-cyber-devtools' ); ?></div>
        <div style="display:flex;flex-direction:column;gap:5px;margin-bottom:12px;max-height:260px;overflow-y:auto;padding-right:2px;">
            <?php
            foreach ( $deduped as $ev ) :
                $ev_time       = (int) ( $ev['time'] ?? 0 );
                $ev_type       = (string) ( $ev['type'] ?? '' );
                $ev_title      = (string) ( $ev['title'] ?? '' );
                $ev_detail     = (string) ( $ev['detail'] ?? '' );
                $age           = $ev_time ? human_time_diff( $ev_time ) . ' ago' : '';
                $ev_title_safe = (string) preg_replace( "/[:\s]*'[^']*'\s*→\s*'[^']*'/u", '', $ev_title );
                $ev_title_safe = trim( rtrim( trim( $ev_title_safe ), ':' ) );
                if ( $ev_title_safe === '' ) {
					$ev_title_safe = $ev_title; }
                $ev_detail_show = ( $ev_title_safe !== $ev_title )
                    ? $ev_title . ( $ev_detail ? ' · ' . $ev_detail : '' )
                    : $ev_detail;
                $today_bucket   = ( $ev_type === 'rest_fail' ) ? 'api_attack' : $ev_type;
                $today_n        = (int) ( $today_counts[ $today_bucket ] ?? 0 );
                if ( $ev_type === 'downgrade' ) {
                    $icon    = '🔓';
					$bg      = '#fef2f2';
					$border  = '#fca5a5';
					$tx      = '#991b1b';
                    $ev_href = $base_url . '&tab=login';
                    $ev_hint = __( 'Go to Login Security', 'cloudscale-cyber-devtools' );
                } elseif ( $ev_type === 'attack' ) {
                    $icon    = '🎯';
					$bg      = '#fff7ed';
					$border  = '#fdba74';
					$tx      = '#9a3412';
                    $ev_href = $base_url . '&tab=login';
                    $ev_hint = __( 'Go to Login Security', 'cloudscale-cyber-devtools' );
                } elseif ( $ev_type === 'api_attack' || $ev_type === 'rest_fail' ) {
                    $icon    = '🔌';
					$bg      = '#fef2f2';
					$border  = '#fca5a5';
					$tx      = '#991b1b';
                    $ev_href = $base_url . '&tab=login#cs-panel-test-accounts';
                    $ev_hint = __( 'Go to Test Account Manager', 'cloudscale-cyber-devtools' );
                } else {
                    $icon    = '⚠️';
					$bg      = '#f9fafb';
					$border  = '#e5e7eb';
					$tx      = '#374151';
                    $ev_href = $base_url . '&tab=login';
                    $ev_hint = __( 'Go to Login Security', 'cloudscale-cyber-devtools' );
                }
				?>
            <a href="<?php echo esc_url( $ev_href ); ?>" title="<?php echo esc_attr( $ev_hint ); ?>" style="display:flex;align-items:flex-start;gap:7px;flex-shrink:0;background:<?php echo esc_attr( $bg ); ?>;border:1px solid <?php echo esc_attr( $border ); ?>;border-radius:6px;padding:6px 9px;text-decoration:none;cursor:pointer;">
                <span style="font-size:13px;flex-shrink:0;line-height:1.4"><?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
                <div style="flex:1;min-width:0">
                    <div style="font-size:11px;font-weight:700;color:<?php echo esc_attr( $tx ); ?>;line-height:1.3;display:flex;align-items:center;gap:5px;">
                        <?php echo esc_html( $ev_title_safe ); ?>
                        <?php if ( $today_n > 0 ) : ?>
                        <span style="font-size:9px;font-weight:700;padding:1px 5px;border-radius:8px;background:<?php echo esc_attr( $border ); ?>;color:<?php echo esc_attr( $tx ); ?>;white-space:nowrap;"><?php echo esc_html( $today_n ); ?> today</span>
                        <?php endif; ?>
                    </div>
                    <?php if ( $ev_detail_show ) : ?>
                    <span onclick="event.preventDefault();event.stopPropagation();var d=this.nextElementSibling;d.style.display=d.style.display==='none'?'block':'none';this.textContent=d.style.display==='none'?'▶ Details':'▼ Hide details';" style="font-size:10px;color:#94a3b8;margin-top:2px;cursor:pointer;display:block;">▶ Details</span>
                    <div style="font-size:10px;color:#64748b;margin-top:2px;display:none;"><?php echo esc_html( $ev_detail_show ); ?></div>
                    <?php endif; ?>
                </div>
                <?php if ( $age ) : ?>
                <span style="font-size:9px;color:#94a3b8;white-space:nowrap;flex-shrink:0;margin-top:2px"><?php echo esc_html( $age ); ?></span>
                <?php endif; ?>
            </a>
            <?php endforeach; ?>
        </div>
        <?php endif; ?>

        <!-- ── CTA ────────────────────────────────────────────────────── -->
        <div class="cs-dw-cta">
            <a href="<?php echo esc_url( $base_url ); ?>">
                <span style="font-size:15px;">🛡️</span>
                <?php esc_html_e( 'Open Cyber and Devtools', 'cloudscale-cyber-devtools' ); ?>
            </a>
        </div>
        <?php
    }

    // ─── AJAX: save notification alert toggles ───────────────────────────

    public static function ajax_save_alerts(): void {
        check_ajax_referer( 'csdt_devtools_thumbnails', 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        try {
            $allowed = array(
                'csdt_telegram_login_valid_user',
                'csdt_telegram_login_invalid_user',
                'csdt_telegram_rest_auth_fail',
                'csdt_telegram_scan_result',
                'csdt_telegram_threat_monitor',
                'csdt_telegram_php_errors',
            );
            foreach ( $allowed as $opt ) {
                $val = isset( $_POST[ $opt ] ) && '1' === sanitize_text_field( wp_unslash( $_POST[ $opt ] ) ) ? '1' : '0';
                update_option( $opt, $val );
            }
            wp_send_json_success();
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }

    // ─── AJAX: rename any WP user login ──────────────────────────────────

    public static function ajax_rename_wp_user(): void {
        check_ajax_referer( self::LOGIN_NONCE, 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        $current_login = isset( $_POST['current_login'] ) ? sanitize_user( wp_unslash( $_POST['current_login'] ), true ) : '';
        $new_login     = isset( $_POST['new_login'] ) ? sanitize_user( wp_unslash( $_POST['new_login'] ), true ) : '';

        if ( ! $current_login || ! $new_login ) {
            wp_send_json_error( __( 'Both fields are required.', 'cloudscale-cyber-devtools' ) );
        }
        if ( strlen( $new_login ) < 3 ) {
            wp_send_json_error( __( 'New username must be at least 3 characters.', 'cloudscale-cyber-devtools' ) );
        }
        $user = get_user_by( 'login', $current_login );
        if ( ! $user ) {
            /* translators: %s is the username that was not found */
            wp_send_json_error( sprintf( __( 'No user found with username "%s".', 'cloudscale-cyber-devtools' ), $current_login ) );
        }
        if ( username_exists( $new_login ) ) {
            wp_send_json_error( __( 'That username is already taken.', 'cloudscale-cyber-devtools' ) );
        }

        global $wpdb;
        $updated = $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            $wpdb->users,
            array( 'user_login' => $new_login ),
            array( 'ID' => $user->ID ),
            array( '%s' ),
            array( '%d' )
        );
        if ( $updated === false ) {
            wp_send_json_error( __( 'Database error, could not rename user.', 'cloudscale-cyber-devtools' ) );
        }

        wp_update_user(
            array(
				'ID'            => $user->ID,
				'display_name'  => $new_login,
				'user_nicename' => sanitize_title( $new_login ),
            )
        );

        // Terminate all sessions so they must log in again with the new username.
        WP_Session_Tokens::get_instance( $user->ID )->destroy_all();

        CSDT_Login::record_security_event(
            'downgrade',
            'Username renamed',
            "'{$current_login}' → '{$new_login}' · By: " . wp_get_current_user()->user_login
        );

        wp_send_json_success(
            array(
				'old_login' => $current_login,
				'new_login' => $new_login,
            )
        );
    }

    // ─── Credentials panel ───────────────────────────────────────────────

    private static function render_credentials_panel(): void {
        $base = admin_url( 'tools.php?page=' . self::TOOLS_SLUG );

        // Collect all secrets.
        $creds = array(
            array(
                'group'  => '🔌 Test Session API',
                'hint'   => __( 'Rotate the path token and secret whenever you suspect compromise.', 'cloudscale-cyber-devtools' ),
                'edit'   => $base . '&tab=login#cs-panel-test-accounts',
                'fields' => array(
                    array(
                        'label'        => __( 'Session URL', 'cloudscale-cyber-devtools' ),
                        'value'        => rest_url( 'csdt/v1/test-session-' . CSDT_Test_Accounts::get_or_create_path_token() ),
                        'type'         => 'url',
                        'regen_action' => 'csdt_regen_path_token',
                        'regen_note'   => __( 'Updates both Session URL and Logout URL. Update .env.test immediately.', 'cloudscale-cyber-devtools' ),
                    ),
                    array(
                        'label' => __( 'Logout URL', 'cloudscale-cyber-devtools' ),
                        'value' => rest_url( 'csdt/v1/test-logout-' . CSDT_Test_Accounts::get_or_create_path_token() ),
                        'type'  => 'url',
                    ),
                    array(
                        'label'        => __( 'Shared Secret', 'cloudscale-cyber-devtools' ),
                        'value'        => CSDT_Test_Accounts::get_or_create_secret(),
                        'type'         => 'secret',
                        'regen_action' => 'csdt_regen_test_secret',
                    ),
                ),
            ),
            array(
                'group'      => '&#9992; Telegram',
                'hint'       => __( 'Shared across all CloudScale plugins. Change the bot token or chat ID to switch channels.', 'cloudscale-cyber-devtools' ),
                'edit'       => $base . '&tab=security#cs-notifications-settings',
                'test_nonce' => wp_create_nonce( 'cloudscale_telegram_test' ),
                'fields'     => array(
                    array(
                        'label' => __( 'Bot Token', 'cloudscale-cyber-devtools' ),
                        'value' => (string) get_option( CloudScale_Telegram::OPTION_TOKEN, '' ),
                        'type'  => 'secret',
                    ),
                    array(
                        'label' => __( 'Chat ID', 'cloudscale-cyber-devtools' ),
                        'value' => (string) get_option( CloudScale_Telegram::OPTION_CHAT_ID, '' ),
                        'type'  => 'secret',
                    ),
                ),
            ),
            array(
                'group'  => '☁️ Cloudflare',
                'hint'   => __( 'Rotate the API Token if it is compromised, the Zone ID is not a secret but is listed here for convenience.', 'cloudscale-cyber-devtools' ),
                'edit'   => $base . '&tab=debug#cs-panel-uptime-monitor',
                'fields' => array(
                    array(
                        'label' => __( 'Zone ID', 'cloudscale-cyber-devtools' ),
                        'value' => (string) get_option( 'csdt_devtools_cf_zone_id', '' ),
                        'type'  => 'secret',
                    ),
                    array(
                        'label' => __( 'API Token', 'cloudscale-cyber-devtools' ),
                        'value' => (string) get_option( 'csdt_devtools_cf_api_token', '' ),
                        'type'  => 'secret',
                    ),
                ),
            ),
            array(
                'group'  => '🤖 AI API Keys',
                'hint'   => __( 'Used for security scans, image generation, and prompt writing.', 'cloudscale-cyber-devtools' ),
                'edit'   => $base . '&tab=security#cs-sec-api-key',
                'fields' => array(
                    array(
                        'label' => 'OpenAI',
                        'value' => (string) get_option( 'csdt_devtools_openai_key', '' ),
                        'type'  => 'secret',
                    ),
                    array(
                        'label' => 'Anthropic',
                        'value' => (string) get_option( 'csdt_devtools_anthropic_key', '' ),
                        'type'  => 'secret',
                    ),
                    array(
                        'label' => 'Gemini',
                        'value' => (string) get_option( 'csdt_devtools_gemini_key', '' ),
                        'type'  => 'secret',
                    ),
                ),
            ),
            array(
                'group'  => '📧 SMTP',
                'hint'   => __( 'Update these whenever you rotate your email account password.', 'cloudscale-cyber-devtools' ),
                'edit'   => $base . '&tab=mail',
                'fields' => array(
                    array(
                        'label' => __( 'Host', 'cloudscale-cyber-devtools' ),
                        'value' => (string) get_option( 'csdt_devtools_smtp_host', '' ),
                        'type'  => 'plain',
                    ),
                    array(
                        'label' => __( 'Username', 'cloudscale-cyber-devtools' ),
                        'value' => (string) get_option( 'csdt_devtools_smtp_user', '' ),
                        'type'  => 'plain',
                    ),
                    array(
                        'label' => __( 'Password', 'cloudscale-cyber-devtools' ),
                        'value' => (string) get_option( 'csdt_devtools_smtp_pass', '' ),
                        'type'  => 'secret',
                    ),
                ),
            ),
        );

        static $cred_idx = 0;
        ?>
        <div class="cs-panel" style="max-width:860px">
            <div class="cs-section-header" style="background:linear-gradient(135deg,#1e3a5f 0%,#0f2942 100%)">
                <span>🔑 <?php esc_html_e( 'CREDENTIALS', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'All rotatable secrets in one place, masked by default', 'cloudscale-cyber-devtools' ); ?></span>
            </div>
            <div class="cs-panel-body" style="display:flex;flex-direction:column;gap:20px;">

            <?php foreach ( $creds as $group ) : ?>
            <div style="border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;">
                <div style="background:#f8fafc;border-bottom:1px solid #e2e8f0;padding:8px 14px;display:flex;align-items:center;justify-content:space-between;gap:8px;">
                    <span style="font-size:12px;font-weight:700;color:#1e293b;"><?php echo esc_html( $group['group'] ); ?></span>
                    <a href="<?php echo esc_url( $group['edit'] ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;text-decoration:none;" onclick="setTimeout(function(){var h=location.hash;if(h){var el=document.querySelector(h);if(el)el.scrollIntoView({behavior:'smooth',block:'start'});}},400);"><?php esc_html_e( 'Edit settings →', 'cloudscale-cyber-devtools' ); ?></a>
                </div>
                <?php if ( ! empty( $group['hint'] ) ) : ?>
                <div style="padding:6px 14px 0;font-size:11px;color:#94a3b8;"><?php echo esc_html( $group['hint'] ); ?></div>
                <?php endif; ?>
                <table style="width:100%;border-collapse:collapse;">
                <?php
                foreach ( $group['fields'] as $f ) :
                    ++$cred_idx;
                    $val    = (string) ( $f['value'] ?? '' );
                    $type   = $f['type'] ?? 'secret';
                    $fid    = 'cs-cred-val-' . $cred_idx;
                    $is_set = $val !== '';
                    if ( ! $is_set ) {
                        $display = '';
                        $masked  = '';
                    } elseif ( $type === 'plain' ) {
                        $display = $val;
                        $masked  = $val;
                    } elseif ( $type === 'url' ) {
                        $display = $val;
                        $masked  = str_repeat( '•', 32 );
                    } else {
                        $display = $val;
                        $masked  = str_repeat( '•', min( 24, max( 8, strlen( $val ) - 4 ) ) ) . ( strlen( $val ) > 4 ? substr( $val, -4 ) : '' );
                    }
                    $regen_action = $f['regen_action'] ?? '';
					?>
                <tr style="border-top:1px solid #f1f5f9;">
                    <td style="padding:8px 14px;font-size:12px;color:#64748b;font-weight:600;white-space:nowrap;width:140px;"><?php echo esc_html( $f['label'] ); ?></td>
                    <td style="padding:8px 14px;">
                        <?php if ( ! $is_set ) : ?>
                        <span style="font-size:12px;color:#94a3b8;font-style:italic;"><?php esc_html_e( 'Not set', 'cloudscale-cyber-devtools' ); ?></span>
                        <?php else : ?>
                        <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                            <code id="<?php echo esc_attr( $fid ); ?>"
                                    data-real="<?php echo esc_attr( $display ); ?>"
                                    data-masked="<?php echo esc_attr( $masked ); ?>"
                                    style="background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;padding:3px 10px;font-size:11px;word-break:break-all;max-width:420px;"><?php echo esc_html( $type === 'plain' ? $display : $masked ); ?></code>
                            <?php if ( $type !== 'plain' ) : ?>
                            <button type="button" class="cs-btn-secondary cs-btn-sm cs-cred-show" data-target="<?php echo esc_attr( $fid ); ?>">👁 <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                            <?php endif; ?>
                            <button type="button" class="cs-btn-secondary cs-btn-sm cs-cred-copy" data-target="<?php echo esc_attr( $fid ); ?>">⎘ <?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></button>
                            <?php if ( $regen_action ) : ?>
                            <button type="button" class="cs-btn-secondary cs-btn-sm cs-cred-regen" data-action="<?php echo esc_attr( $regen_action ); ?>" data-target="<?php echo esc_attr( $fid ); ?>" data-type="<?php echo esc_attr( $type ); ?>" style="color:#b45309;border-color:#fbbf24;">↺ <?php esc_html_e( 'Rotate', 'cloudscale-cyber-devtools' ); ?></button>
                            <?php endif; ?>
                        </div>
                        <?php endif; ?>
                    </td>
                </tr>
                <?php endforeach; ?>
                </table>
                <?php if ( ! empty( $group['test_nonce'] ) ) : ?>
                <div style="padding:8px 14px;border-top:1px solid #f1f5f9;display:flex;align-items:center;gap:8px;">
                    <button type="button" class="cs-btn-sm cs-btn-primary cs-tg-test-btn"
                            data-nonce="<?php echo esc_attr( $group['test_nonce'] ); ?>">
                        &#9992; <?php esc_html_e( 'Send Test', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <span class="cs-tg-test-msg" style="font-size:11px;"></span>
                </div>
                <?php endif; ?>
            </div>
            <?php endforeach; ?>

            <p style="font-size:11px;color:#94a3b8;margin:0;"><?php esc_html_e( 'Secrets are stored in wp_options and never exposed in source code. Rotation buttons regenerate and save the new value server-side, update your .env files immediately after rotating.', 'cloudscale-cyber-devtools' ); ?></p>
            <?php // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- inline handlers for credentials panel buttons ?>
            <script>
            (function() {
                var AJAX  = <?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>;
                var NONCE_LOGIN = <?php echo wp_json_encode( wp_create_nonce( self::LOGIN_NONCE ) ); ?>;
                var NONCE_THUMBS = <?php echo wp_json_encode( wp_create_nonce( 'csdt_devtools_thumbnails' ) ); ?>;

                document.addEventListener('click', function(e) {
                    // Show/Hide
                    var showBtn = e.target.closest('.cs-cred-show');
                    if (showBtn) {
                        var code = document.getElementById(showBtn.getAttribute('data-target'));
                        if (!code) return;
                        var shown = showBtn.textContent.includes('Hide') || showBtn.textContent.includes('🔒');
                        code.textContent = shown ? (code.dataset.masked || '') : (code.dataset.real || '');
                        showBtn.textContent = shown ? '👁 Show' : '🔒 Hide';
                        return;
                    }
                    // Copy
                    var copyBtn = e.target.closest('.cs-cred-copy');
                    if (copyBtn) {
                        var code2 = document.getElementById(copyBtn.getAttribute('data-target'));
                        if (!code2) return;
                        navigator.clipboard.writeText(code2.dataset.real || '').then(function() {
                            var orig = copyBtn.textContent;
                            copyBtn.textContent = '✓ Copied';
                            setTimeout(function() { copyBtn.textContent = orig; }, 1500);
                        });
                        return;
                    }
                    // Rotate
                    var regenBtn = e.target.closest('.cs-cred-regen');
                    if (regenBtn) {
                        if (!confirm('Rotate this credential? You must update all .env files and scripts that use it.')) return;
                        var action = regenBtn.getAttribute('data-action');
                        var target = regenBtn.getAttribute('data-target');
                        var ftype  = regenBtn.getAttribute('data-type');
                        var code3  = document.getElementById(target);
                        regenBtn.disabled = true;
                        regenBtn.textContent = '⏳';
                        var fd = new FormData();
                        fd.append('action', action);
                        fd.append('nonce',  NONCE_LOGIN);
                        fetch(AJAX, { method: 'POST', body: fd })
                            .then(function(r) { return r.json(); })
                            .then(function(res) {
                                regenBtn.disabled = false;
                                regenBtn.textContent = '↺ Rotate';
                                if (res.success && code3) {
                                    var newVal = res.data.secret || res.data.session_url || '';
                                    if (newVal) {
                                        var masked = newVal.length > 8
                                            ? '•'.repeat(Math.min(24, newVal.length - 4)) + newVal.slice(-4)
                                            : newVal;
                                        code3.dataset.real   = newVal;
                                        code3.dataset.masked = masked;
                                        code3.textContent    = masked;
                                        // flash row green
                                        var tr = code3.closest('tr');
                                        if (tr) { tr.style.background = '#f0fdf4'; setTimeout(function() { tr.style.background = ''; }, 2000); }
                                    }
                                }
                            })
                            .catch(function() { regenBtn.disabled = false; regenBtn.textContent = '↺ Rotate'; });
                        return;
                    }
                    // Save alert settings
                    var alertSave = e.target.closest('#cs-alerts-save');
                    if (alertSave) {
                        alertSave.disabled = true;
                        var data = { action: 'csdt_devtools_save_alerts', nonce: NONCE_THUMBS };
                        document.querySelectorAll('.cs-alert-toggle').forEach(function(cb) { data[cb.dataset.opt] = cb.checked ? '1' : '0'; });
                        var fd2 = new FormData();
                        Object.keys(data).forEach(function(k) { fd2.append(k, data[k]); });
                        fetch(AJAX, { method: 'POST', body: fd2 })
                            .then(function(r) { return r.json(); })
                            .then(function() {
                                alertSave.disabled = false;
                                var saved = document.getElementById('cs-alerts-saved');
                                if (saved) { saved.style.display = ''; setTimeout(function(){ saved.style.display = 'none'; }, 2000); }
                            })
                            .catch(function() { alertSave.disabled = false; });
                    }
                });

                // Send Test (credentials panel Telegram button)
                document.addEventListener('click', function(e) {
                    var testBtn = e.target.closest('.cs-tg-test-btn');
                    if (!testBtn) return;
                    var nonce = testBtn.getAttribute('data-nonce');
                    var msg   = testBtn.closest('div').querySelector('.cs-tg-test-msg');
                    testBtn.disabled = true;
                    if (msg) { msg.textContent = 'Sending...'; msg.style.color = '#666'; }
                    var fd = new FormData();
                    fd.append('action',       'cloudscale_telegram_test');
                    fd.append('nonce',        nonce);
                    fd.append('test_source',  testBtn.getAttribute('data-source') || '');
                    fetch(AJAX, { method: 'POST', body: fd })
                        .then(function(r) { return r.json(); })
                        .then(function(d) {
                            if (msg) {
                                msg.textContent = d.success ? 'Sent.' : (d.data || 'Failed.');
                                msg.style.color = d.success ? '#0a5' : '#c00';
                            }
                        })
                        .catch(function() { if (msg) { msg.textContent = 'Request error.'; msg.style.color = '#c00'; } })
                        .finally(function() { testBtn.disabled = false; });
                });
            }());
            </script>

            <!-- ── Notification Alerts ──────────────────────────────────── -->
            <?php
            $tg_set = CloudScale_Telegram::is_configured();
            $alerts = array(
                array(
                    'group' => '🔐 Security Controls',
                    'hint'  => __( 'These always fire, they cannot be silenced because a disabled security control is a critical event.', 'cloudscale-cyber-devtools' ),
                    'items' => array(
                        array(
							'label'  => __( 'Security control downgraded (hide login off, brute force off, 2FA off)', 'cloudscale-cyber-devtools' ),
							'always' => true,
						),
                    ),
                ),
                array(
                    'group' => '🎯 Login Attacks',
                    'hint'  => '',
                    'items' => array(
                        array(
							'label'   => __( 'Failed login, valid (known) username targeted', 'cloudscale-cyber-devtools' ),
							'opt'     => 'csdt_telegram_login_valid_user',
							'default' => '0',
							'id'      => 'ca-tg-valid',
						),
                        array(
							'label'   => __( 'Failed login, unknown username attempted', 'cloudscale-cyber-devtools' ),
							'opt'     => 'csdt_telegram_login_invalid_user',
							'default' => '0',
							'id'      => 'ca-tg-invalid',
						),
                        array(
							'label'  => __( 'Account locked after N failed attempts', 'cloudscale-cyber-devtools' ),
							'always' => true,
						),
                        array(
							'label'  => __( 'IP auto-blocked after N failures / hour', 'cloudscale-cyber-devtools' ),
							'always' => true,
						),
                    ),
                ),
                array(
                    'group' => '🔌 API Attacks',
                    'hint'  => '',
                    'items' => array(
                        array(
							'label'   => __( 'REST API auth failure (application password brute-force)', 'cloudscale-cyber-devtools' ),
							'opt'     => 'csdt_telegram_rest_auth_fail',
							'default' => '1',
							'id'      => 'ca-tg-rest-fail',
						),
                        array(
							'label'  => __( 'Test Session API locked after 5 bad attempts', 'cloudscale-cyber-devtools' ),
							'always' => true,
						),
                    ),
                ),
                array(
                    'group' => '🔍 Scans & Monitoring',
                    'hint'  => '',
                    'items' => array(
                        array(
							'label'   => __( 'Scheduled AI security scan result', 'cloudscale-cyber-devtools' ),
							'opt'     => 'csdt_telegram_scan_result',
							'default' => '1',
							'id'      => 'ca-tg-scan',
						),
                        array(
							'label'   => __( 'Threat monitor, file change / new admin / probe spike', 'cloudscale-cyber-devtools' ),
							'opt'     => 'csdt_telegram_threat_monitor',
							'default' => '1',
							'id'      => 'ca-tg-threat',
						),
                        array(
							'label'   => __( 'PHP error monitor, new fatal errors detected', 'cloudscale-cyber-devtools' ),
							'opt'     => 'csdt_telegram_php_errors',
							'default' => '1',
							'id'      => 'ca-tg-php-err',
						),
                    ),
                ),
            );
            ?>
            <div style="border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;margin-top:20px;">
                <div style="background:#f8fafc;border-bottom:1px solid #e2e8f0;padding:10px 16px;display:flex;align-items:center;justify-content:space-between;gap:8px;">
                    <span style="font-size:12px;font-weight:700;color:#1e293b;">🔔 <?php esc_html_e( 'Notification Alerts', 'cloudscale-cyber-devtools' ); ?></span>
                    <?php if ( ! $tg_set ) : ?>
                    <span style="font-size:10px;font-weight:600;padding:2px 8px;border-radius:8px;background:#fef3c7;color:#92400e;border:1px solid #fcd34d;">
                        &#9888; <?php esc_html_e( 'Telegram not configured, alerts will not fire', 'cloudscale-cyber-devtools' ); ?>
                    </span>
                    <?php else : ?>
                    <span style="font-size:10px;color:#16a34a;font-weight:600;">&#10003; Telegram configured</span>
                    <?php endif; ?>
                </div>
                <div style="background:#fff;padding:12px 16px;display:flex;flex-direction:column;gap:16px;">
                    <?php foreach ( $alerts as $agroup ) : ?>
                    <div>
                        <div style="font-size:11px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;"><?php echo esc_html( $agroup['group'] ); ?></div>
                        <?php if ( $agroup['hint'] ) : ?>
                        <p style="font-size:11px;color:#94a3b8;margin:0 0 6px;"><?php echo esc_html( $agroup['hint'] ); ?></p>
                        <?php endif; ?>
                        <div style="display:flex;flex-direction:column;gap:5px;">
                        <?php
                        foreach ( $agroup['items'] as $item ) :
                            if ( ! empty( $item['always'] ) ) :
								?>
                            <div style="display:flex;align-items:center;gap:8px;font-size:12px;color:#64748b;">
                                <span style="width:16px;height:16px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;background:#f1f5f9;border:1px solid #e2e8f0;border-radius:3px;font-size:9px;font-weight:700;color:#94a3b8;">🔒</span>
                                <?php echo esc_html( $item['label'] ); ?>
                                <span style="font-size:9px;padding:1px 5px;border-radius:6px;background:#f1f5f9;color:#94a3b8;border:1px solid #e2e8f0;white-space:nowrap;"><?php esc_html_e( 'always on', 'cloudscale-cyber-devtools' ); ?></span>
                            </div>
								<?php
                            else :
                                $val = get_option( $item['opt'], $item['default'] ) === '1';
								?>
                            <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:12px;color:#374151;">
                                <input type="checkbox" class="cs-alert-toggle" data-opt="<?php echo esc_attr( $item['opt'] ); ?>" id="<?php echo esc_attr( $item['id'] ); ?>" <?php checked( $val ); ?> style="width:14px;height:14px;">
                                <?php echo esc_html( $item['label'] ); ?>
                            </label>
                            <?php endif; ?>
                        <?php endforeach; ?>
                        </div>
                    </div>
                    <?php endforeach; ?>
                    <div style="display:flex;align-items:center;gap:8px;padding-top:8px;border-top:1px solid #f1f5f9;">
                        <button type="button" class="cs-btn-primary cs-btn-sm" id="cs-alerts-save">💾 <?php esc_html_e( 'Save Alert Settings', 'cloudscale-cyber-devtools' ); ?></button>
                        <span id="cs-alerts-saved" style="display:none;font-size:12px;font-weight:600;color:#16a34a;">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                    </div>
                </div>
            </div>

            </div>
        </div>
        <?php
    }

    public static function add_crossorigin_to_external_scripts( string $tag, string $handle, string $src ): string {
        if ( ! preg_match( '#^https?://#i', $src ) ) {
            return $tag;
        }
        $site_host = wp_parse_url( home_url(), PHP_URL_HOST );
        $src_host  = wp_parse_url( $src, PHP_URL_HOST );
        if ( $site_host && $src_host && $site_host === $src_host ) {
            return $tag;
        }
        if ( strpos( $tag, 'crossorigin' ) !== false ) {
            return $tag;
        }
        return str_replace( ' src=', ' crossorigin="anonymous" src=', $tag );
    }

    public static function ads_dedup_ob_start(): void {
        if ( is_admin() ) {
            return;
        }
        ob_start( array( __CLASS__, 'ads_dedup_ob_html' ) );
        add_action( 'shutdown', static function () { ob_end_flush(); }, 0 );
    }

    public static function ads_dedup_ob_html( string $html ): string {
        // Matches push({ ... enable_page_level_ads ... }) allowing one level of nested {}.
        $push_re = '/\.push\s*\(\s*\{(?:[^{}]|\{[^{}]*\})*\benable_page_level_ads\b(?:[^{}]|\{[^{}]*\})*\}\s*\)\s*;?/is';
        // adsbygoogle.js loaded with ?client= auto-enables page-level ads internally;
        // any explicit enable_page_level_ads push on the same page is a duplicate → strip all.
        if ( preg_match( '/adsbygoogle\.js[^"\'<>\s]*[?&]client=/i', $html ) ) {
            return preg_replace( $push_re, '', $html ) ?? $html;
        }
        // Old-style (no ?client= URL): keep only the first push, drop duplicates.
        $found = false;
        return preg_replace_callback(
            $push_re,
            static function ( array $m ) use ( &$found ): string {
                if ( $found ) {
                    return '';
                }
                $found = true;
                return $m[0];
            },
            $html
        ) ?? $html;
    }

    public static function output_ads_dedup_script(): void {
        // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- must fire at wp_head priority 0 before any ad script loads; wp_add_inline_script() does not support priority-0 head injection
        ?>
<script>
(function(){
    if(window.__adsDedupFinal)return;
    window.__adsDedupFinal=true;
    var _done=false;
    function autoAdsUrl(){try{return !!document.querySelector('script[src*="adsbygoogle.js"][src*="client="]');}catch(e){return false;}}
    function wrap(arr){
        if(!arr)return;
        var _p=function(o){return Array.prototype.push.call(arr,o);};
        try{var cp=arr.push;if(cp&&cp!==Array.prototype.push){_p=function(o){return cp(o);};}}catch(e){}
        var guard=function(o){
            if(o&&o.enable_page_level_ads){if(_done||autoAdsUrl()){_done=true;return;}_done=true;}
            return _p(o);
        };
        try{
            Object.defineProperty(arr,'push',{
                configurable:true,enumerable:false,
                get:function(){return guard;},
                set:function(v){_p=function(o){return v.call(arr,o);};}
            });
        }catch(e){arr.push=guard;}
        arr.__adsDedupWrapped=true;
    }
    var _cur=window.adsbygoogle=window.adsbygoogle||[];
    wrap(_cur);
    try{Object.defineProperty(window,'adsbygoogle',{configurable:true,get:function(){return _cur;},set:function(v){_cur=v;wrap(_cur);}});}catch(e){}
    window.addEventListener('pageshow',function(e){if(e.persisted){_done=true;}});
}());
</script>
        <?php
    }

    private static function render_home_panel(): void {
        $ai_cfg        = CSDT_AI_Dispatcher::get_config();
        $ai_provider   = $ai_cfg['provider'];
        $has_key       = ! empty( $ai_cfg['key'] );
        $next_run      = wp_next_scheduled( 'csdt_scheduled_scan' );
        $sec_url       = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=security' );
        $rollback_info = get_option( 'csdt_db_prefix_rollback' );
        // Status dashboard data
        $history          = get_option( 'csdt_scan_history', array() );
        $last_scan        = ! empty( $history ) ? $history[0] : null;
        $tm_enabled       = get_option( 'csdt_threat_monitor_enabled', '1' ) === '1';
        $smtp_enabled     = get_option( 'csdt_devtools_smtp_enabled', '0' ) === '1';
        $smtp_host        = trim( (string) get_option( 'csdt_devtools_smtp_host', '' ) );
        $login_slug       = get_option( 'csdt_devtools_login_slug', '' );
        $bf_on            = get_option( 'csdt_devtools_brute_force_enabled', '1' ) === '1';
        $force_2fa        = get_option( 'csdt_devtools_2fa_force_admins', '0' ) === '1';
        $uptime_enabled   = get_option( 'csdt_uptime_enabled', '0' ) === '1';
        $uptime_url       = trim( (string) get_option( 'csdt_uptime_worker_url', '' ) );
        $tm_last_run      = (int) get_option( 'csdt_threat_last_run', 0 );
        $uptime_last_ping = get_option( 'csdt_uptime_last_ping', null );
        // URLs for status cards
        $login_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=login' );
        $mail_url  = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=mail' );
        $debug_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=debug' );
        ?>
        <div id="cs-panel-home" class="cs-panel" style="margin-bottom:0;">

        <!-- ── Status Dashboard ─────────────────────────────────────────── -->
        <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px;padding:12px;">

            <?php
            // ── Card helper: reuse to keep markup DRY
            // Card 1: AI Cyber Scan
            if ( $has_key ) :
                $provider_label = $ai_provider === 'gemini' ? 'Google Gemini' : 'Anthropic Claude';
                $scan_detail    = $last_scan
                    // translators: %s is a human-readable time difference e.g. "5 minutes ago"
                    ? sprintf( __( 'Last scan: %s', 'cloudscale-cyber-devtools' ), human_time_diff( (int) ( $last_scan['scanned_at'] ?? 0 ) ) . ' ' . __( 'ago', 'cloudscale-cyber-devtools' ) )
                    : $provider_label;
				?>
            <div style="background:#f0fdf4;border:1px solid #86efac;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'AI Cyber Scan', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#15803d;">&#x2705; <?php esc_html_e( 'Configured', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;"><?php echo esc_html( $scan_detail ); ?></div>
                <a href="<?php echo esc_url( $sec_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Run Scan', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php else : ?>
            <div style="background:#fef2f2;border:1px solid #fca5a5;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#dc2626;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'AI Cyber Scan', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#dc2626;">&#x274C; <?php esc_html_e( 'Not configured', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#dc2626;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'Free Gemini tier available, no card needed.', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $sec_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Configure', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php endif; ?>

            <!-- Card 1b: Managed AI (commercial pay-monthly tier) -->
            <?php CSDT_Managed_AI::render_home_card(); ?>

            <!-- Card 1c: Telegram Alerts -->
            <?php
            $tg_ok  = CloudScale_Telegram::is_configured();
            $tg_bg  = $tg_ok ? '#f0fdf4' : '#fef2f2';
            $tg_bd  = $tg_ok ? '#86efac' : '#fca5a5';
            $tg_tx  = $tg_ok ? '#15803d' : '#dc2626';
            $tg_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=security#cs-notifications-settings' );
            ?>
            <div style="background:<?php echo esc_attr( $tg_bg ); ?>;border:1px solid <?php echo esc_attr( $tg_bd ); ?>;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Telegram Alerts', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:<?php echo esc_attr( $tg_tx ); ?>;">
                    <?php echo $tg_ok ? '&#x2705; ' . esc_html__( 'Configured', 'cloudscale-cyber-devtools' ) : '&#x274C; ' . esc_html__( 'Not configured', 'cloudscale-cyber-devtools' ); ?>
                </div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;">
                    <?php echo $tg_ok ? esc_html__( 'All plugins share this channel.', 'cloudscale-cyber-devtools' ) : esc_html__( 'Shared alerts for all CloudScale plugins.', 'cloudscale-cyber-devtools' ); ?>
                </div>
                <?php if ( $tg_ok ) : ?>
                <button type="button" class="cs-tg-test-btn"
                        data-nonce="<?php echo esc_attr( wp_create_nonce( 'cloudscale_telegram_test' ) ); ?>"
                        data-source="Cyber DevTools"
                        style="margin-top:6px;font-size:11px;color:#6366f1;font-weight:600;background:none;border:none;padding:0;cursor:pointer;text-decoration:underline;">
                    <?php esc_html_e( 'Send Test', 'cloudscale-cyber-devtools' ); ?>
                </button>
                <span class="cs-tg-test-msg" style="font-size:11px;display:block;"></span>
                <?php else : ?>
                <a href="<?php echo esc_url( $tg_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Configure', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
                <?php endif; ?>
            </div>

            <!-- Card 2: Threat Monitor -->
            <div style="background:<?php echo $tm_enabled ? '#f0fdf4' : '#fef2f2'; ?>;border:1px solid <?php echo $tm_enabled ? '#86efac' : '#fca5a5'; ?>;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Threat Monitor', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:<?php echo $tm_enabled ? '#15803d' : '#dc2626'; ?>;">
                    <?php echo $tm_enabled ? '&#x2705; ' . esc_html__( 'Active', 'cloudscale-cyber-devtools' ) : '&#x274C; ' . esc_html__( 'Disabled', 'cloudscale-cyber-devtools' ); ?>
                </div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;">
                    <?php if ( $tm_last_run ) : ?>
                        <?php /* translators: %s is a human-readable time difference e.g. "5 minutes" */ printf( esc_html__( 'Last check: %s ago', 'cloudscale-cyber-devtools' ), esc_html( human_time_diff( $tm_last_run ) ) ); ?>
                    <?php else : ?>
                        <?php esc_html_e( 'File integrity, new-admin and probe detection.', 'cloudscale-cyber-devtools' ); ?>
                    <?php endif; ?>
                </div>
                <a href="<?php echo esc_url( $sec_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Security', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>

            <!-- Card 3: SMTP Mail -->
            <?php if ( $smtp_enabled && $smtp_host ) : ?>
            <div style="background:#f0fdf4;border:1px solid #86efac;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'SMTP Mail', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#15803d;">&#x2705; <?php esc_html_e( 'Configured', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;"><?php echo esc_html( $smtp_host ); ?></div>
                <a href="<?php echo esc_url( $mail_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Alerts', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php else : ?>
            <div style="background:#fef2f2;border:1px solid #fca5a5;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#dc2626;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'SMTP Mail', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#dc2626;">&#x274C; <?php esc_html_e( 'Not configured', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#dc2626;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'WordPress default mail (unreliable).', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $mail_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Configure', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php endif; ?>

            <!-- Card 4: Login Security -->
            <?php
            $login_ok      = $login_slug && $bf_on && $force_2fa;
            $login_bg      = $login_ok ? '#f0fdf4' : ( $login_slug || $bf_on ? '#fffbeb' : '#fef2f2' );
            $login_bd      = $login_ok ? '#86efac' : ( $login_slug || $bf_on ? '#fcd34d' : '#fca5a5' );
            $login_tx      = $login_ok ? '#15803d' : ( $login_slug || $bf_on ? '#92400e' : '#dc2626' );
            $login_details = array();
            if ( $login_slug ) {
				$login_details[] = __( 'Hidden login', 'cloudscale-cyber-devtools' );
            }
            if ( $bf_on ) {
				$login_details[] = __( 'Brute-force lock', 'cloudscale-cyber-devtools' );
            }
            if ( $force_2fa ) {
				$login_details[] = __( 'Forced 2FA', 'cloudscale-cyber-devtools' );
            }
            ?>
            <div style="background:<?php echo esc_attr( $login_bg ); ?>;border:1px solid <?php echo esc_attr( $login_bd ); ?>;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Login Security', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:<?php echo esc_attr( $login_tx ); ?>;">
                    <?php echo $login_ok ? '&#x2705; ' . esc_html__( 'Hardened', 'cloudscale-cyber-devtools' ) : ( $login_details ? '&#x26A0;&#xFE0F; ' . esc_html__( 'Partial', 'cloudscale-cyber-devtools' ) : '&#x274C; ' . esc_html__( 'Default settings', 'cloudscale-cyber-devtools' ) ); ?>
                </div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;">
                    <?php echo $login_details ? esc_html( implode( ', ', $login_details ) ) : esc_html__( 'No protections active.', 'cloudscale-cyber-devtools' ); ?>
                </div>
                <a href="<?php echo esc_url( $login_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Login Security', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>

            <!-- Card 5: Uptime Monitor -->
            <?php if ( $uptime_enabled && $uptime_url ) : ?>
            <div style="background:#f0fdf4;border:1px solid #86efac;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Uptime Monitor', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#15803d;">&#x2705; <?php esc_html_e( 'Active', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;">
                    <?php if ( $uptime_last_ping && isset( $uptime_last_ping['time'] ) ) : ?>
                        <?php /* translators: %s is a human-readable time difference e.g. "5 minutes" */ printf( esc_html__( 'Last ping: %s ago', 'cloudscale-cyber-devtools' ), esc_html( human_time_diff( (int) $uptime_last_ping['time'] ) ) ); ?>
                        <?php
                        if ( isset( $uptime_last_ping['ms'] ) ) :
							?>
                            &nbsp;&middot;&nbsp;<?php echo esc_html( $uptime_last_ping['ms'] ); ?>ms<?php endif; ?>
                    <?php else : ?>
                        <?php esc_html_e( 'Cloudflare Worker heartbeat enabled.', 'cloudscale-cyber-devtools' ); ?>
                    <?php endif; ?>
                </div>
                <a href="<?php echo esc_url( $debug_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Diagnostics', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php else : ?>
            <div style="background:#fffbeb;border:1px solid #fcd34d;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#92400e;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Uptime Monitor', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#92400e;">&#x26A0;&#xFE0F; <?php esc_html_e( 'Not configured', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#92400e;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'Ping your site from Cloudflare edge every minute.', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $debug_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Set up', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php endif; ?>

            <!-- Card 6: Scheduled Scan -->
            <?php
            if ( $has_key && $next_run ) :
                $next_run_str = wp_date( 'D j M, g:ia', $next_run );
				?>
            <div style="background:#f0f9ff;border:1px solid #7dd3fc;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Scheduled Scan', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#0284c7;">&#x1F551; <?php esc_html_e( 'Scheduled', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;"><?php echo esc_html( $next_run_str ); ?></div>
                <a href="<?php echo esc_url( $sec_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Settings', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php elseif ( $has_key ) : ?>
            <div style="background:#fffbeb;border:1px solid #fcd34d;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#92400e;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Scheduled Scan', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#92400e;">&#x26A0;&#xFE0F; <?php esc_html_e( 'Not scheduled', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#92400e;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'Auto-run a scan weekly or monthly.', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $sec_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Schedule', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php else : ?>
            <div style="background:#fffbeb;border:1px solid #fcd34d;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#92400e;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Scheduled Scan', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#92400e;">&#x26A0;&#xFE0F; <?php esc_html_e( 'No AI key', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#92400e;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'Configure AI to enable scheduled scans.', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $sec_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Configure', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php endif; ?>

            <!-- Card 7: AI Image Generator -->
            <?php
            $openai_key = get_option( 'csdt_devtools_openai_key', '' );
            $thumbs_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=thumbnails' );
            if ( $openai_key ) :
                $missing_images = (int) ( new WP_Query(
                    array(
						'post_type'      => 'post',
						'post_status'    => 'publish',
						'posts_per_page' => -1,
						'fields'         => 'ids',
						'meta_query'     => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- meta_query required for this lookup
							array(
								'key'     => '_thumbnail_id',
								'compare' => 'NOT EXISTS',
							),
						),
                    )
                ) )->found_posts;
				?>
            <div style="background:<?php echo $missing_images ? '#fffbeb' : '#f0fdf4'; ?>;border:1px solid <?php echo $missing_images ? '#fcd34d' : '#86efac'; ?>;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'AI Image Generator', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:<?php echo $missing_images ? '#92400e' : '#15803d'; ?>;">
                    <?php
                    echo $missing_images
                        // translators: %d is the number of posts missing featured images
                        ? '&#x26A0;&#xFE0F; ' . sprintf( esc_html__( '%d posts need images', 'cloudscale-cyber-devtools' ), (int) $missing_images )
                        : '&#x2705; ' . esc_html__( 'All posts have images', 'cloudscale-cyber-devtools' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- integer cast above; esc_html__ for the string part 
					?>
                </div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'gpt-image-2 · OpenAI key configured', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $thumbs_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Generate Images', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php else : ?>
            <div style="background:#fef2f2;border:1px solid #fca5a5;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#dc2626;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'AI Image Generator', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#dc2626;">&#x274C; <?php esc_html_e( 'Not configured', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#dc2626;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'Add an OpenAI key to generate featured images with gpt-image-2.', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $thumbs_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Set up', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php endif; ?>

            <!-- Card 8: Plugin Stack Scanner -->
            <?php
            $optimizer_url     = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=optimizer' );
            $stack_last        = get_transient( 'csdt_plugin_stack_last' );
            $stack_match_count = $stack_last ? (int) $stack_last['match_count'] : -1;
            $stack_at          = $stack_last ? (int) $stack_last['at'] : 0;
            if ( $stack_last ) :
                $stack_bg = $stack_match_count > 0 ? '#fffbeb' : '#f0fdf4';
                $stack_bd = $stack_match_count > 0 ? '#fcd34d' : '#86efac';
                $stack_tx = $stack_match_count > 0 ? '#92400e' : '#15803d';
				?>
            <div style="background:<?php echo esc_attr( $stack_bg ); ?>;border:1px solid <?php echo esc_attr( $stack_bd ); ?>;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Plugin Stack', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:<?php echo esc_attr( $stack_tx ); ?>;">
                    <?php if ( $stack_match_count > 0 ) : ?>
                        &#x26A0;&#xFE0F; <?php /* translators: %d number of redundant plugins found */ printf( esc_html__( '%d replaceable', 'cloudscale-cyber-devtools' ), (int) $stack_match_count ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- (int) cast; printf with esc_html__ template ?>
                    <?php else : ?>
                        &#x2705; <?php esc_html_e( 'Stack optimised', 'cloudscale-cyber-devtools' ); ?>
                    <?php endif; ?>
                </div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;">
                    <?php /* translators: %s time difference e.g. "3 days" */ printf( esc_html__( 'Scanned %s ago', 'cloudscale-cyber-devtools' ), esc_html( human_time_diff( $stack_at ) ) ); ?>
                </div>
                <a href="<?php echo esc_url( $optimizer_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'View Details', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php else : ?>
            <div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:12px 14px;">
                <div style="font-size:10px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;"><?php esc_html_e( 'Plugin Stack', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:13px;font-weight:700;color:#374151;">&#x1F50D; <?php esc_html_e( 'Not yet scanned', 'cloudscale-cyber-devtools' ); ?></div>
                <div style="font-size:11px;color:#6b7280;margin-top:2px;line-height:1.4;"><?php esc_html_e( 'Find plugins CloudScale already replaces.', 'cloudscale-cyber-devtools' ); ?></div>
                <a href="<?php echo esc_url( $optimizer_url ); ?>" style="font-size:11px;color:#6366f1;font-weight:600;display:inline-block;margin-top:6px;text-decoration:none;"><?php esc_html_e( 'Scan Now', 'cloudscale-cyber-devtools' ); ?> &rarr;</a>
            </div>
            <?php endif; ?>

        </div><!-- /status grid -->

        <!-- ── Managed AI subscription panel ──────────────────────────────── -->
        <?php CSDT_Managed_AI::render_panel(); ?>

        <!-- ── Recent Security Events ─────────────────────────────────────── -->
        <?php
        $home_sec_events = get_option( 'csdt_security_events', array() );
        $home_sec_events = is_array( $home_sec_events ) ? array_reverse( $home_sec_events ) : array();
        $home_sec_events = array_slice( $home_sec_events, 0, 8 );
        if ( ! empty( $home_sec_events ) ) :
			$login_tab_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=login' );
			?>
        <div style="margin:0 12px 12px;border:1px solid #fca5a5;border-radius:8px;overflow:hidden;">
            <div style="background:linear-gradient(135deg,#7f1d1d 0%,#b91c1c 100%);padding:8px 14px;display:flex;align-items:center;justify-content:space-between;">
                <span style="color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;">⚠️ Recent Security Events</span>
                <a href="<?php echo esc_url( $login_tab_url ); ?>" style="font-size:10px;color:#fca5a5;text-decoration:none;font-weight:600;">Login Security →</a>
            </div>
            <div style="background:#fff;padding:6px 8px;display:flex;flex-direction:column;gap:4px;">
            <?php
            foreach ( $home_sec_events as $ev ) :
                $ev_time   = (int) ( $ev['time'] ?? 0 );
                $ev_type   = (string) ( $ev['type'] ?? '' );
                $ev_title  = (string) ( $ev['title'] ?? '' );
                $ev_detail = (string) ( $ev['detail'] ?? '' );
                $age       = $ev_time ? human_time_diff( $ev_time ) . ' ago' : '';
                // Strip inline identifiers (usernames) from the summary title, // full details are only revealed when the user clicks Details.
                $ev_title_safe = (string) preg_replace( "/[:\s]*'[^']*'\s*→\s*'[^']*'/u", '', $ev_title );
                $ev_title_safe = trim( rtrim( trim( $ev_title_safe ), ':' ) );
                if ( $ev_title_safe === '' ) {
					$ev_title_safe = $ev_title; }
                // For old stored events where names were in the title, surface the original
                // title in the Details panel so nothing is permanently lost.
                $ev_detail_show = ( $ev_title_safe !== $ev_title )
                    ? $ev_title . ( $ev_detail ? ' · ' . $ev_detail : '' )
                    : $ev_detail;
                if ( $ev_type === 'downgrade' ) {
                    $icon = '🔓';
					$tx   = '#991b1b';
					$bg   = '#fef2f2';
					$bd   = '#fca5a5';
                } elseif ( $ev_type === 'api_attack' || $ev_type === 'rest_fail' ) {
                    $icon = '🔌';
					$tx   = '#7c2d12';
					$bg   = '#fef2f2';
					$bd   = '#fca5a5';
                } elseif ( $ev_type === 'attack' ) {
                    $icon = '🎯';
					$tx   = '#92400e';
					$bg   = '#fffbeb';
					$bd   = '#fcd34d';
                } else {
                    $icon = '⚠️';
					$tx   = '#374151';
					$bg   = '#f9fafb';
					$bd   = '#e5e7eb';
                }
				?>
            <div style="display:flex;align-items:flex-start;gap:7px;background:<?php echo esc_attr( $bg ); ?>;border:1px solid <?php echo esc_attr( $bd ); ?>;border-radius:5px;padding:5px 8px;">
                <span style="font-size:12px;line-height:1.5;flex-shrink:0"><?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
                <div style="flex:1;min-width:0">
                    <span style="font-size:11px;font-weight:700;color:<?php echo esc_attr( $tx ); ?>"><?php echo esc_html( $ev_title_safe ); ?></span>
                    <?php if ( $ev_detail_show ) : ?>
                    <span onclick="var d=this.nextElementSibling;d.style.display=d.style.display==='none'?'block':'none';this.textContent=d.style.display==='none'?'▶ Details':'▼ Hide details';" style="font-size:10px;color:#94a3b8;margin-top:2px;cursor:pointer;display:block;">▶ Details</span>
                    <div style="font-size:10px;color:#6b7280;margin-top:2px;display:none;"><?php echo esc_html( $ev_detail_show ); ?></div>
                    <?php endif; ?>
                </div>
                <?php
                if ( $age ) :
					?>
                    <span style="font-size:9px;color:#94a3b8;white-space:nowrap;flex-shrink:0;margin-top:2px"><?php echo esc_html( $age ); ?></span><?php endif; ?>
            </div>
            <?php endforeach; ?>
            </div>
        </div>
        <?php endif; ?>

        <!-- ── DB Prefix Rollback Banner ──────────────────────────────────── -->
        <?php
        if ( $rollback_info && ! empty( $rollback_info['old_prefix'] ) ) :
            $age_h = round( ( time() - ( $rollback_info['time'] ?? 0 ) ) / 3600, 1 );
			?>
        <div style="background:#fef2f2;border:1px solid #fca5a5;border-radius:6px;padding:12px 16px;margin:12px 24px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
            <div style="flex:1;min-width:0;">
                <span style="font-weight:700;color:#dc2626;font-size:13px;">&#x21A9; DB Prefix Rollback Available</span>
                <span style="font-size:12px;color:#6b7280;margin-left:6px;"><?php echo esc_html( $age_h ); ?>h ago</span>
                <div style="font-size:12px;color:#374151;margin-top:2px;">
                    Tables were renamed from <code><?php echo esc_html( $rollback_info['old_prefix'] ); ?></code> &rarr; <code><?php echo esc_html( $rollback_info['new_prefix'] ); ?></code>
                    (<?php echo count( $rollback_info['tables'] ?? array() ); ?> tables). Rollback restores all tables and wp-config.php.
                </div>
            </div>
            <button type="button" id="csdt-prefix-rollback-persistent-btn" class="cs-btn-secondary cs-btn-sm" style="border-color:#ef4444;color:#dc2626;white-space:nowrap;">&#x21A9; Rollback Now</button>
            <span id="csdt-prefix-rollback-persistent-msg" style="display:none;font-size:12px;"></span>
        </div>
        <?php endif; ?>

        <!-- ── Quick Fixes ─────────────────────────────────────────────────── -->
        <div class="cs-section-header" style="background:linear-gradient(90deg,#78350f 0%,#b45309 100%);border-left:3px solid #fcd34d;margin-bottom:0;">
            <span>&#x26A1; <?php esc_html_e( 'Quick Fixes', 'cloudscale-cyber-devtools' ); ?></span>
            <span class="cs-header-hint"><?php esc_html_e( 'One-click hardening actions for common WordPress security settings', 'cloudscale-cyber-devtools' ); ?></span>
            <?php
            self::render_explain_btn(
                'quick-fixes',
                'Quick Fixes',
                array(
					array(
						'name' => 'How it works',
						'rec'  => 'Overview',
						'html' => 'Each row shows a security hardening item and its current status (&#x2705; fixed / &#x26A0; needs attention). Click the action button to apply the fix in one click, no manual file editing or WP-CLI required. The panel refreshes automatically after each fix.',
					),
					array(
						'name' => 'WP-Cron Health',
						'rec'  => 'Important',
						'html' => 'Checks that WordPress scheduled cleanup events are scheduled and firing on time. If cron is disabled or events are overdue, click <strong>Reschedule &amp; Run Now</strong> to fix immediately.',
					),
					array(
						'name' => 'Expired Transients',
						'rec'  => 'Maintenance',
						'html' => 'Counts expired cache entries left in wp_options. WordPress auto-purges these daily via cron, but they can accumulate if cron has been unreliable. Click <strong>Delete Expired Transients</strong> to clear the backlog immediately.',
					),
					array(
						'name' => 'DB Prefix',
						'rec'  => 'Critical',
						'html' => 'Renames all <code>wp_</code> tables to a unique prefix and updates wp-config.php automatically. Always create a database backup before running this fix.',
					),
					array(
						'name' => 'wp-config.php',
						'rec'  => 'Critical',
						'html' => 'Sets <code>wp-config.php</code> permissions to <code>0400</code> (read-only). This prevents any PHP process, including a compromised plugin, from overwriting your database credentials or secret keys.',
					),
				)
            );
            ?>
        </div>
        <div id="cs-quick-fixes-panel" style="padding:12px 0 4px;">
        <?php
        foreach ( CSDT_Site_Audit::get_quick_fixes() as $fix ) :
            $is_fixed = (bool) $fix['fixed'];
			?>
            <div class="cs-quick-fix-row" data-fix-id="<?php echo esc_attr( $fix['id'] ); ?>" style="display:flex;align-items:flex-start;gap:12px;padding:10px 14px;margin-bottom:6px;background:<?php echo $is_fixed ? 'rgba(0,0,0,0.02)' : '#fff'; ?>;border-radius:6px;border:1px solid <?php echo $is_fixed ? 'rgba(0,0,0,0.07)' : 'rgba(0,0,0,0.12)'; ?>;">
                <div style="flex-shrink:0;font-size:16px;line-height:1.5;padding-top:1px;"><?php echo $is_fixed ? '<span style="color:#16a34a;">✓</span>' : '<span style="color:#d97706;">⚠</span>'; ?></div>
                <div style="flex:1;min-width:0;">
                    <div style="display:flex;justify-content:space-between;align-items:flex-start;gap:8px;">
                        <div style="font-size:13px;font-weight:600;color:<?php echo $is_fixed ? '#6b7280' : '#1d2327'; ?>;"><?php echo esc_html( $fix['title'] ); ?></div>
                        <?php if ( $is_fixed ) : ?>
                        <span style="flex-shrink:0;font-size:12px;color:#16a34a;font-weight:600;white-space:nowrap;">Fixed &#x2713;</span>
                        <?php endif; ?>
                    </div>
                    <div style="font-size:12px;color:#50575e;margin-top:2px;"><?php echo esc_html( $fix['detail'] ); ?></div>
                    <?php
                    if ( ! $is_fixed ) :
                        $risk            = $fix['risk'] ?? 'safe';
                        $confirm_msg     = $fix['confirm_msg'] ?? '';
                        $btn_extra_style = $risk === 'moderate' ? 'background:#d97706;border-color:#b45309;'
                                        : ( $risk === 'destructive' ? 'background:#dc2626;border-color:#b91c1c;' : '' );
						?>
                    <div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;">
						<?php if ( ! empty( $fix['fix_modal'] ) ) : ?>
                        <button type="button" class="cs-btn-primary cs-btn-sm"
                                style="<?php echo esc_attr( $btn_extra_style ); ?>"
                                data-cs-modal-open="<?php echo esc_attr( $fix['fix_modal'] ); ?>">
                            <?php echo esc_html( $fix['fix_label'] ); ?>
                        </button>
                    <?php else : ?>
                        <button type="button" class="cs-btn-primary cs-btn-sm cs-quick-fix-btn"
                                style="<?php echo esc_attr( $btn_extra_style ); ?>"
                                data-fix-id="<?php echo esc_attr( $fix['id'] ); ?>"
                                data-risk="<?php echo esc_attr( $risk ); ?>"
                                <?php
                                if ( $confirm_msg ) :
									?>
                                    data-confirm-msg="<?php echo esc_attr( $confirm_msg ); ?>"<?php endif; ?>>
                            <?php echo esc_html( $fix['fix_label'] ); ?>
                        </button>
                        <?php if ( ! empty( $fix['dismiss_label'] ) && ! empty( $fix['dismiss_id'] ) ) : ?>
                        <button type="button" class="cs-btn-secondary cs-btn-sm cs-quick-fix-btn"
                                data-fix-id="<?php echo esc_attr( $fix['dismiss_id'] ); ?>"
                                style="font-size:11px;">
                            <?php echo esc_html( $fix['dismiss_label'] ); ?>
                        </button>
                        <?php endif; ?>
                    <?php endif; ?>
                    </div>
                    <?php endif; ?>
                </div>
            </div>
        <?php endforeach; ?>
        </div>

        </div><!-- /cs-panel -->
        <?php
    }

    /*
    ==================================================================
        Optimizer tab, Plugin Stack Scanner + Update Risk Scorer
        ================================================================== */

    private static function render_optimizer_panel(): void {
        $has_key      = CSDT_AI_Dispatcher::has_key();
        $security_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=security' );
        ?>
        <!-- ── Plugin Stack Scanner ──────────────────────────────────────── -->
        <div class="cs-panel" id="cs-panel-plugin-stack">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#1a3a8f 0%,#1e6fd9 100%);border-left:3px solid #60a5fa;">
                <span>🔍 <?php esc_html_e( 'Plugin Stack Scanner', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Find plugins CloudScale already replaces, reduce bloat and attack surface', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'plugin-stack',
                    'Plugin Stack Scanner',
                    array(
						array(
							'name' => 'What it scans',
							'rec'  => 'Overview',
							'html' => 'Compares your active plugins against a curated list of functionality that CloudScale already provides. Plugins flagged as redundant can usually be deactivated, reducing page load time, update surface area, and conflict risk.',
						),
						array(
							'name' => 'Inactive plugins',
							'rec'  => 'Important',
							'html' => 'Inactive plugins still execute their autoloaded code on every page load and are still scanned for vulnerabilities. Deactivate <em>and delete</em> plugins you are not actively using, do not just deactivate them.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                    <p style="color:#4b5563;margin:0 0 6px;line-height:1.65;font-size:.95em;">
                        <?php esc_html_e( 'CloudScale replaces entire categories of WordPress plugins, security scanners, 2FA plugins, SMTP mailers, code block plugins, SQL tools, and log viewers. Scan to find out which of your installed plugins you can safely remove.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <p style="color:#9ca3af;margin:0 0 18px;font-size:.88em;">
                        <?php esc_html_e( 'Fewer plugins = smaller attack surface, faster page loads, fewer update conflicts.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
                        <button id="csdt-optimizer-scan-btn" class="cs-btn-primary">
                            🔍 <?php esc_html_e( 'Scan My Plugin Stack', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                        <span id="csdt-optimizer-scanning" style="display:none;color:#6b7280;font-size:13px;">
                            ⏳ <?php esc_html_e( 'Scanning installed plugins...', 'cloudscale-cyber-devtools' ); ?>
                        </span>
                    </div>
                    <div id="csdt-optimizer-results" style="display:none;margin-top:20px;"></div>
            </div>
        </div>

        <!-- ── Update Risk Scorer ────────────────────────────────────────── -->
        <div class="cs-panel" id="cs-panel-update-risk">
            <div class="cs-section-header cs-section-header-teal">
                <span>🔄 <?php esc_html_e( 'Update Risk Scorer', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'AI-rates pending plugin updates: Patch / Minor / Breaking before you apply them', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'update-risk',
                    'Update Risk Scorer',
                    array(
						array(
							'name' => 'Risk ratings',
							'rec'  => 'Recommended',
							'html' => '🟢 <strong>Patch</strong>, safe to apply immediately (security fix or bug fix with no API changes). 🟡 <strong>Minor</strong>, new features, low risk but review changelog. 🔴 <strong>Breaking</strong>, major version or significant API changes, test on staging first.',
						),
						array(
							'name' => 'How it works',
							'rec'  => 'Overview',
							'html' => 'Reads the plugin changelog from WordPress.org and sends it to the configured AI provider to assess change type. Requires an AI API key (configure on the Home tab).',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <div>
                    <p style="color:#4b5563;margin:0 0 6px;line-height:1.65;font-size:.95em;">
                        <?php esc_html_e( 'Before applying plugin updates, get an AI risk rating for each one: Patch (safe now), Minor (new features), or Breaking (review first). Prevents update-caused site breakage.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <p style="color:#9ca3af;margin:0 0 16px;font-size:.88em;">
                        <?php esc_html_e( 'Reads the plugin changelog from WordPress.org and asks the AI to assess whether this is a security patch, a feature release, or a potentially breaking change.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
                        <button id="csdt-update-risk-scan-btn" class="cs-btn-primary">
                            🔍 <?php esc_html_e( 'Scan for Available Updates', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                        <span id="csdt-update-risk-scanning" style="display:none;color:#6b7280;font-size:13px;">
                            ⏳ <?php esc_html_e( 'Loading...', 'cloudscale-cyber-devtools' ); ?>
                        </span>
                    </div>
                    <div id="csdt-update-risk-results" style="display:none;margin-top:20px;"></div>
                </div>
            </div>
        </div>

        <!-- ── Database Intelligence Engine ─────────────────────────────── -->
        <div class="cs-panel">
            <div class="cs-section-header cs-section-header-orange">
                <span>🗄️ <?php esc_html_e( 'Database Intelligence Engine', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Scan for DB bloat, transients, revisions, orphaned meta, then one-click fix', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'db-intelligence',
                    'Database Intelligence Engine',
                    array(
						array(
							'name' => 'What it finds',
							'rec'  => 'Recommended',
							'html' => 'Oversized autoload cache (slows every page load), expired transients (uncleaned remnants from plugins), post revisions (can accumulate thousands of rows), and orphaned post/user metadata left behind by deleted plugins.',
						),
						array(
							'name' => 'One-click fixes',
							'rec'  => 'Recommended',
							'html' => 'Each issue found includes a Fix It button that runs the cleanup directly in the database. The operation is logged. Take a backup first if you want a safety net, CloudScale Backup &amp; Restore can do this in one click.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <div>
                    <p style="color:#4b5563;margin:0 0 6px;line-height:1.65;font-size:.95em;">
                        <?php esc_html_e( 'Scans your WordPress database for hidden bloat, oversized autoload cache, expired transients, post revisions, and orphaned metadata, then gives you one-click cleanup actions for each issue found.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <p style="color:#9ca3af;margin:0 0 16px;font-size:.88em;">
                        <?php esc_html_e( 'All fixes run directly in the database. Take a backup first if you want a safety net.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
                        <button id="csdt-db-intelligence-scan-btn" class="cs-btn-primary">
                            🔍 <?php esc_html_e( 'Analyse Database', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                        <span id="csdt-db-intelligence-scanning" style="display:none;color:#6b7280;font-size:13px;">
                            ⏳ <?php esc_html_e( 'Scanning…', 'cloudscale-cyber-devtools' ); ?>
                        </span>
                    </div>
                    <div id="csdt-db-intelligence-results" style="display:none;margin-top:20px;"></div>
                </div>
            </div>
        </div>

        <!-- ── Orphaned Table Cleanup ────────────────────────────────────── -->
        <div class="cs-panel">
            <div class="cs-section-header cs-section-header-red">
                <span>🗑️ <?php esc_html_e( 'Orphaned Table Cleanup', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Find and safely remove database tables left behind by deleted plugins', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'orphaned-tables',
                    'Orphaned Table Cleanup',
                    array(
						array(
							'name' => 'What gets flagged',
							'rec'  => 'Overview',
							'html' => 'Non-core database tables that have no active plugin claiming them. WordPress core tables are always protected. Tables are identified by comparing your database against a list of known core tables.',
						),
						array(
							'name' => 'Recycle Bin',
							'rec'  => 'Important',
							'html' => 'Tables are never deleted immediately. They are renamed with a <code>_trash_</code> prefix (moved to the Recycle Bin) first. You can restore them to their original name or permanently delete from the Recycle Bin.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <div>
                    <p style="color:#4b5563;margin:0 0 6px;line-height:1.65;font-size:.95em;">
                        <?php esc_html_e( 'Scans for database tables left behind by removed plugins. WordPress core tables are always protected, only non-core tables appear here.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <p style="color:#9ca3af;margin:0 0 16px;font-size:.88em;">
                        <?php esc_html_e( 'Tables are moved to the Recycle Bin first (renamed with a _trash_ prefix). You can then restore or permanently delete them.', 'cloudscale-cyber-devtools' ); ?>
                    </p>
                    <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px;">
                        <button id="csdt-orphan-scan-btn" type="button" class="cs-btn-primary">
                            🔍 <?php esc_html_e( 'Scan for Orphaned Tables', 'cloudscale-cyber-devtools' ); ?>
                        </button>
                    </div>
                    <div id="csdt-orphan-results" style="margin-top:8px;"></div>

                    <!-- Recycle Bin -->
                    <div style="margin-top:28px;border-top:1px solid #fde68a;padding-top:20px;">
                        <div id="csdt-trash-toggle" style="display:flex;align-items:center;gap:10px;margin-bottom:8px;cursor:pointer;user-select:none;">
                            <span id="csdt-trash-chevron" style="font-size:11px;color:#6b7280;min-width:10px;">▶</span>
                            <h3 style="margin:0;font-size:1rem;font-weight:600;color:#374151;">♻️ <?php esc_html_e( 'Recycle Bin', 'cloudscale-cyber-devtools' ); ?></h3>
                            <button id="csdt-trash-refresh-btn" type="button" style="font-size:11px;background:none;border:1px solid #d1d5db;border-radius:4px;padding:2px 8px;cursor:pointer;color:#6b7280;">🔄 <?php esc_html_e( 'Refresh', 'cloudscale-cyber-devtools' ); ?></button>
                        </div>
                        <div id="csdt-trash-body" style="display:none;">
                            <p style="color:#9ca3af;font-size:.88em;margin:0 0 12px;"><?php esc_html_e( 'Archived tables can be restored to their original names or permanently deleted.', 'cloudscale-cyber-devtools' ); ?></p>
                            <div id="csdt-trash-results"></div>
                        </div>
                    </div>
                </div>

            </div>
        </div>

        <!-- ── AI Debugging Assistant ────────────────────────────────────── -->
        <div class="cs-panel" id="cs-panel-ai-debug">
            <div class="cs-section-header" style="background:linear-gradient(90deg,#312e81 0%,#4f46e5 100%);border-left:3px solid #a5b4fc;">
                <span>🤖 <?php esc_html_e( 'AI Debugging Assistant', 'cloudscale-cyber-devtools' ); ?></span>
                <span class="cs-header-hint"><?php esc_html_e( 'Paste an error message or log snippet, get an AI diagnosis and fix steps', 'cloudscale-cyber-devtools' ); ?></span>
                <?php
                self::render_explain_btn(
                    'ai-debug',
                    'AI Debugging Assistant',
                    array(
						array(
							'name' => 'What to paste',
							'rec'  => 'Overview',
							'html' => 'Paste a PHP fatal error, WordPress debug.log entry, browser console error, or a plain-English description of unexpected behaviour. The AI reads context, identifies the root cause, and explains how to fix it step by step.',
						),
						array(
							'name' => 'Privacy note',
							'rec'  => 'Important',
							'html' => 'Text you enter here is sent to the configured AI provider (Anthropic or Gemini). Do not paste stack traces that contain passwords, API keys, or other secrets. Redact sensitive values before submitting.',
						),
					)
                );
                ?>
            </div>
            <div class="cs-panel-body">
                <?php if ( $has_key ) : ?>
                <p style="color:#4b5563;margin:0 0 14px;line-height:1.65;font-size:.95em;">
                    <?php esc_html_e( 'Paste an error message, log entry, or describe the problem below. The AI will identify the root cause and provide fix steps.', 'cloudscale-cyber-devtools' ); ?>
                </p>
                <textarea id="csdt-debug-input"
                    rows="6"
                    style="width:100%;box-sizing:border-box;font-family:monospace;font-size:.85em;padding:10px 12px;border:1px solid #d1d5db;border-radius:6px;resize:vertical;background:#f8fafc;color:#1e293b;line-height:1.6;"
                    placeholder="<?php esc_attr_e( 'Paste error message, debug.log entry, or describe the issue…', 'cloudscale-cyber-devtools' ); ?>"></textarea>
                <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:12px;">
                    <button id="csdt-debug-analyze-btn" type="button" class="cs-btn-primary">
                        🤖 <?php esc_html_e( 'Diagnose with AI', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <span id="csdt-debug-analyzing" style="display:none;color:#6b7280;font-size:13px;">
                        ⏳ <?php esc_html_e( 'Analysing…', 'cloudscale-cyber-devtools' ); ?>
                    </span>
                </div>
                <div id="csdt-debug-result" style="display:none;margin-top:20px;"></div>
                <?php else : ?>
                <p style="color:#6b7280;font-size:.93em;margin:0;">
                    <?php
                    echo wp_kses(
                        __( 'An AI API key is required. Configure <strong>Anthropic Claude</strong> or <strong>Google Gemini</strong> on the <a href="#" data-cs-tab="security">Security tab</a> to use the AI Debugging Assistant.', 'cloudscale-cyber-devtools' ),
                        array(
							'strong' => array(),
							'a'      => array(
								'href'        => array(),
								'data-cs-tab' => array(),
							),
                        )
                    );
                    ?>
                </p>
                <?php endif; ?>
            </div>
        </div>
        <?php
    }

	private static function render_security_panel(): void {
        $ai_cfg              = CSDT_AI_Dispatcher::get_config();
        $ai_provider         = $ai_cfg['provider'];
        $sched_enabled       = get_option( 'csdt_scan_schedule_enabled', '0' ) === '1';
        $sched_freq          = get_option( 'csdt_scan_schedule_freq', 'weekly' );
        $sched_type          = get_option( 'csdt_scan_schedule_type', 'deep' );
        $notify_enabled      = get_option( 'csdt_scan_schedule_email', '1' ) === '1';
        $notify_email        = get_option( 'csdt_notify_email', '' );
        $telegram_configured = CloudScale_Telegram::is_configured();
        $next_run            = wp_next_scheduled( 'csdt_scheduled_scan' );
        ?>
        <?php $site_audit_url = admin_url( 'tools.php?page=' . self::TOOLS_SLUG . '&tab=site-audit' ); ?>
        <div class="cs-tab-intro" style="margin-bottom:20px;">
                    <p><?php echo wp_kses( __( 'The <strong>AI Cyber Audit</strong> uses frontier AI &#8212; Anthropic Claude or Google Gemini &#8212; to analyse your WordPress installation and produce a prioritised, scored security report in under 60 seconds. Configure your provider and API key in the <strong>AI Settings</strong> panel below, then run a scan from the <strong>AI Cyber Audit</strong> panel. A free Gemini tier is available with no credit card required.', 'cloudscale-cyber-devtools' ), array( 'strong' => array() ) ); ?></p>

                <!-- AI Security Scan vs Site Audit comparison -->
                <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px;margin-bottom:16px;font-size:12px;">
                    <div style="background:#f0fdf4;border:1px solid #86efac;border-radius:6px;padding:10px 14px;">
                        <div style="font-weight:700;color:#15803d;margin-bottom:4px;">🛡️ AI Security Scan, this tab</div>
                        <div style="color:#374151;line-height:1.5;"><?php esc_html_e( 'Security misconfigurations, exposed endpoints, headers, brute-force exposure. Finds issues attackers exploit.', 'cloudscale-cyber-devtools' ); ?></div>
                    </div>
                    <div style="background:#eff6ff;border:1px solid #93c5fd;border-radius:6px;padding:10px 14px;">
                        <div style="font-weight:700;color:#1d4ed8;margin-bottom:4px;">🔍 Site Audit</div>
                        <div style="color:#374151;line-height:1.5;"><?php esc_html_e( 'Content quality, SEO, database health, plugin status. Finds issues affecting visitors and search rankings.', 'cloudscale-cyber-devtools' ); ?></div>
                        <a href="<?php echo esc_url( $site_audit_url ); ?>" style="display:inline-block;margin-top:6px;font-size:11px;color:#6366f1;font-weight:600;"><?php esc_html_e( 'Go to Site Audit →', 'cloudscale-cyber-devtools' ); ?></a>
                    </div>
                </div>

                <!-- ── Threat Monitor ──────────────────────────── -->
			<?php
			$tm_enabled       = get_option( 'csdt_threat_monitor_enabled', '1' ) === '1';
			$tm_file_enabled  = get_option( 'csdt_threat_file_integrity_enabled', '1' ) === '1';
			$tm_admin_enabled = get_option( 'csdt_threat_new_admin_enabled', '1' ) === '1';
			$tm_probe_enabled = get_option( 'csdt_threat_probe_enabled', '1' ) === '1';
			$tm_threshold     = get_option( 'csdt_threat_probe_threshold', '25' );
			$tm_last_file     = get_option( 'csdt_threat_last_file_alert', null );
			$tm_last_admin    = get_option( 'csdt_threat_last_admin_alert', null );
			$tm_last_probe    = get_option( 'csdt_threat_last_probe_alert', null );
			$tm_baseline_ver  = get_option( 'csdt_file_integrity_wp_ver', '' );
			$tm_baseline      = get_option( 'csdt_file_integrity_baseline', array() );
			?>
                <div class="cs-panel" id="cs-panel-threat-monitor">
                    <div class="cs-section-header" style="background:linear-gradient(90deg,#7f1d1d 0%,#b91c1c 100%);border-left:3px solid #f87171;">
                        <span>🔎 <?php esc_html_e( 'Threat Monitor', 'cloudscale-cyber-devtools' ); ?></span>
                        <span class="cs-header-hint"><?php esc_html_e( 'File integrity · New admin alert · Probe detection, alerts once per incident, not per event', 'cloudscale-cyber-devtools' ); ?></span>
					<?php
                        self::render_explain_btn(
                            'threat-monitor',
                            'Threat Monitor',
                            array(
								array(
									'name' => 'File Integrity',
									'rec'  => 'Critical',
									'html' => 'Every 5 minutes, scans <code>wp-includes/*.php</code> and <code>wp-admin/*.php</code> and compares modification times against a stored baseline. Any unexpected change triggers an immediate email and push alert.<br><br><strong>Smart baseline:</strong> When WordPress is updated, the baseline is rebuilt silently, core files legitimately change during updates. The same mtime is never alerted twice. Click <strong>Reset File Baseline</strong> after a manual core edit to clear outstanding alerts.',
								),
								array(
									'name' => 'New Admin Alert',
									'rec'  => 'Critical',
									'html' => 'Hooks into <code>user_register</code> and <code>set_user_role</code>. The instant any account is created as, or promoted to, administrator, an alert fires with the username, email, and creation method (Admin UI / REST API / WP-CLI).<br><br>Accounts matching test/automation patterns (playwright, helpdocs, e2e_) are flagged as <code>[TEST]</code> and do not trigger false positives during CI runs.',
								),
								array(
									'name' => 'Probe Detection',
									'rec'  => 'High',
									'html' => 'Reads only new bytes appended to the access log since the last check. Counts hits to sensitive paths: <code>wp-login.php</code>, <code>xmlrpc.php</code>, <code>wp-config.php</code>, <code>.env</code>, <code>.git/</code>, <code>.sql</code>, <code>.bak</code>, and shell-injection patterns (<code>eval(</code>, <code>base64_</code>, <code>cmd=</code>).<br><br>Alerts only when count exceeds threshold (default: 25/5 min) AND at most once per hour, no alert floods.',
								),
								array(
									'name' => 'Alert channels',
									'rec'  => 'Setup',
									'html' => 'Alerts fire via <strong>email</strong> (WordPress admin address) and <strong>Telegram</strong> if credentials are set under Security Scan &rarr; Scheduled Scans. The Threat Monitor shares the same notification infrastructure as the SSH Monitor and PHP Error Alerting, one place to configure, all monitors benefit.',
								),
							)
                        );
					?>
                    </div>
                    <div class="cs-panel-body">

                        <div class="cs-field-row">
                            <div class="cs-field">
                                <label class="cs-label">
                                    <input type="checkbox" id="csdt-tm-enabled" <?php checked( $tm_enabled ); ?>>
								<?php esc_html_e( 'Enable Threat Monitor', 'cloudscale-cyber-devtools' ); ?>
                                </label>
                                <span class="cs-hint"><?php esc_html_e( 'Master switch. Runs checks every 5 minutes. Alerts via email and Telegram.', 'cloudscale-cyber-devtools' ); ?></span>
                            </div>
                        </div>

                        <div id="csdt-tm-options" style="<?php echo $tm_enabled ? '' : 'opacity:.5;pointer-events:none;'; ?>">
                            <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px;">

                                <div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px 16px;">
                                    <label style="display:flex;align-items:flex-start;gap:8px;cursor:pointer;font-size:13px;font-weight:600;color:#1e293b;">
                                        <input type="checkbox" id="csdt-tm-file" <?php checked( $tm_file_enabled ); ?> style="margin-top:2px;flex-shrink:0;">
                                        🗂️ <?php esc_html_e( 'File Integrity', 'cloudscale-cyber-devtools' ); ?>
                                    </label>
                                    <div style="font-size:12px;color:#64748b;margin-top:6px;line-height:1.5;"><?php esc_html_e( 'Alerts if wp-includes/ or wp-admin/ core files are modified. Ignores WP core updates automatically. Alerts once per unique change.', 'cloudscale-cyber-devtools' ); ?></div>
								<?php if ( $tm_last_file ) : ?>
                                    <div style="margin-top:8px;font-size:11px;color:#dc2626;font-weight:600;">
                                        🚨 <?php echo esc_html( human_time_diff( $tm_last_file['ts'] ) . ' ago' ); ?>, <?php echo (int) $tm_last_file['count']; ?> file<?php echo $tm_last_file['count'] === 1 ? '' : 's'; ?>
                                    </div>
                                    <?php elseif ( $tm_baseline ) : ?>
                                    <div style="margin-top:8px;font-size:11px;color:#16a34a;">✓ <?php /* translators: 1: WordPress version string, 2: number of files in baseline */ printf( esc_html__( 'Baseline: WP %1$s (%2$d files)', 'cloudscale-cyber-devtools' ), esc_html( $tm_baseline_ver ), count( $tm_baseline ) ); ?></div>
                                    <?php else : ?>
                                    <div style="margin-top:8px;font-size:11px;color:#64748b;"><?php esc_html_e( 'Baseline will be created on first run.', 'cloudscale-cyber-devtools' ); ?></div>
                                    <?php endif; ?>
                                </div>

                                <div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px 16px;">
                                    <label style="display:flex;align-items:flex-start;gap:8px;cursor:pointer;font-size:13px;font-weight:600;color:#1e293b;">
                                        <input type="checkbox" id="csdt-tm-admin" <?php checked( $tm_admin_enabled ); ?> style="margin-top:2px;flex-shrink:0;">
                                        👤 <?php esc_html_e( 'New Admin Alert', 'cloudscale-cyber-devtools' ); ?>
                                    </label>
                                    <div style="font-size:12px;color:#64748b;margin-top:6px;line-height:1.5;"><?php esc_html_e( 'Instant alert when a new administrator account is created or a user is promoted to admin. Fires once per user.', 'cloudscale-cyber-devtools' ); ?></div>
								<?php if ( $tm_last_admin ) : ?>
                                    <div style="margin-top:8px;font-size:11px;color:#dc2626;font-weight:600;">
                                        🚨 <?php echo esc_html( human_time_diff( $tm_last_admin['ts'] ) . ' ago, ' . $tm_last_admin['login'] ); ?>
                                    </div>
                                    <?php else : ?>
                                    <div style="margin-top:8px;font-size:11px;color:#16a34a;">✓ <?php esc_html_e( 'No new admin accounts detected.', 'cloudscale-cyber-devtools' ); ?></div>
                                    <?php endif; ?>
                                </div>

                                <div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px 16px;">
                                    <label style="display:flex;align-items:flex-start;gap:8px;cursor:pointer;font-size:13px;font-weight:600;color:#1e293b;">
                                        <input type="checkbox" id="csdt-tm-probe" <?php checked( $tm_probe_enabled ); ?> style="margin-top:2px;flex-shrink:0;">
                                        🔍 <?php esc_html_e( 'Probe Detection', 'cloudscale-cyber-devtools' ); ?>
                                    </label>
                                    <div style="font-size:12px;color:#64748b;margin-top:6px;line-height:1.5;"><?php esc_html_e( 'Counts requests to sensitive endpoints (wp-login, xmlrpc, .env, .git) in the access log. Throttled to one alert per hour.', 'cloudscale-cyber-devtools' ); ?></div>
                                    <div style="margin-top:8px;display:flex;align-items:center;gap:6px;">
                                        <label style="font-size:11px;color:#64748b;"><?php esc_html_e( 'Threshold:', 'cloudscale-cyber-devtools' ); ?></label>
                                        <input type="number" id="csdt-tm-probe-threshold" min="5" max="500"
                                                value="<?php echo esc_attr( $tm_threshold ); ?>"
                                                style="width:60px;padding:2px 6px;font-size:12px;border:1px solid #d1d5db;border-radius:4px;">
                                        <span style="font-size:11px;color:#64748b;"><?php esc_html_e( 'requests / 5 min', 'cloudscale-cyber-devtools' ); ?></span>
                                    </div>
								<?php if ( $tm_last_probe ) : ?>
                                    <div style="margin-top:6px;font-size:11px;color:#d97706;font-weight:600;">
                                        ⚠ <?php echo esc_html( human_time_diff( $tm_last_probe['ts'] ) . ' ago, ' . $tm_last_probe['count'] . ' probes' ); ?>
                                    </div>
                                    <?php endif; ?>
                                </div>

                            </div>
                        </div>

                        <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
                            <button type="button" class="cs-btn-primary" id="csdt-tm-save">💾 <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                            <span class="cs-settings-saved" id="csdt-tm-saved">✓ <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                            <?php if ( $tm_baseline ) : ?>
                            <button type="button" class="cs-btn-secondary" id="csdt-tm-reset" style="font-size:12px;margin-left:auto;">↺ <?php esc_html_e( 'Reset File Baseline', 'cloudscale-cyber-devtools' ); ?></button>
                            <span id="csdt-tm-reset-msg" style="font-size:12px;color:#16a34a;display:none;"></span>
                            <?php endif; ?>
                        </div>

                    </div>
                </div>

                <!-- ── AI Settings ────────────────────────────────────────────── -->
                <div class="cs-panel" id="cs-panel-ai-settings">
                <div class="cs-section-header cs-section-header-red">
                    <span>&#x1F916; <?php esc_html_e( 'AI Settings', 'cloudscale-cyber-devtools' ); ?></span>
                    <span class="cs-header-hint"><?php esc_html_e( 'Select a provider and paste your API key to enable AI-powered security scans', 'cloudscale-cyber-devtools' ); ?></span>
                    <?php
                    self::render_explain_btn(
                        'cyber-audit',
                        'AI Cyber Audit',
                        array(
							array(
								'name' => 'AI Providers',
								'rec'  => 'Info',
								'html' => '<p>Two providers supported. Your API key is stored only in <code>wp_options</code> and sent exclusively to that provider\'s API endpoint, never to any third party.</p><p><strong>Anthropic Claude</strong>, best results for security analysis.<br>Get a key: <a href="https://console.anthropic.com/settings/keys" target="_blank" rel="noopener">console.anthropic.com/settings/keys</a><br>Models: <code>claude-sonnet-4-6</code> (fast, recommended) · <code>claude-opus-4-7</code> (most capable)</p><p><strong>Google Gemini</strong>, free tier available.<br>Get a key: <a href="https://aistudio.google.com/app/apikey" target="_blank" rel="noopener">aistudio.google.com/app/apikey</a><br>Models: <code>gemini-2.0-flash</code> (fast, free tier) · <code>gemini-2.5-pro</code> (most capable)</p>',
							),
							array(
								'name' => 'Deep Dive Scan',
								'rec'  => 'Recommended',
								'html' => 'Audits WordPress core settings, plugins, themes, user accounts, file permissions, and wp-config.php. Adds live HTTP probes (SSL/TLS strength, login page exposure, XML-RPC, REST user enumeration, directory listing, server headers), DNS email security checks (SPF, DMARC, DKIM), PHP end-of-life detection, and AI-powered static triage of plugin PHP files for suspicious code patterns.',
							),
							array(
								'name' => 'Scheduled Scans',
								'rec'  => 'Recommended',
								'html' => 'Run automatically on a daily or weekly schedule. Results are stored in Scan History. Configure email and Telegram alerts under Scheduled Scans &rarr; Notifications to receive the AI summary the moment each scan completes.',
							),
						)
                    );
                    ?>
                </div>
                <div class="cs-panel-body">
                <p style="color:#6b7280;font-size:13px;line-height:1.65;margin:0 0 16px;"><?php esc_html_e( 'Choose an AI provider and paste your API key to enable the security scan. A free Google Gemini tier is available with no credit card required, ideal for getting started.', 'cloudscale-cyber-devtools' ); ?></p>
                <div class="cs-sec-settings">

                    <div class="cs-sec-row">
                        <span class="cs-sec-label"><?php esc_html_e( 'AI Provider:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <select id="cs-sec-provider" class="cs-sec-select">
                                <option value="anthropic"><?php esc_html_e( 'Anthropic Claude', 'cloudscale-cyber-devtools' ); ?></option>
                                <option value="gemini"><?php esc_html_e( 'Google Gemini', 'cloudscale-cyber-devtools' ); ?></option>
                            </select>
                        </div>
                    </div>

                    <div class="cs-sec-row" id="cs-row-anthropic-key">
                        <span class="cs-sec-label"><?php esc_html_e( 'API Key:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
                                <input type="password" id="cs-sec-api-key" class="cs-text-input cs-sec-key-input"
                                        autocomplete="off" placeholder="sk-ant-api03-…">
                                <button type="button" class="cs-btn-secondary" id="cs-sec-reveal-key" style="display:none">&#128065; <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                                <button type="button" class="cs-btn-secondary" id="cs-sec-test-key">
								<?php esc_html_e( 'Test Key', 'cloudscale-cyber-devtools' ); ?>
                                </button>
                                <span id="cs-sec-key-status" class="cs-sec-key-status"></span>
                            </div>
                            <span class="cs-hint">
                            <?php
                            echo wp_kses(
                                __( 'Get your key at <a href="https://console.anthropic.com/settings/keys" target="_blank" rel="noopener">console.anthropic.com</a>. Stored in wp_options.', 'cloudscale-cyber-devtools' ),
                                array(
									'a' => array(
										'href'   => array(),
										'target' => array(),
										'rel'    => array(),
									),
                                )
                            );
							?>
                                                    </span>
                        </div>
                    </div>

                    <div class="cs-sec-row" id="cs-row-gemini-key" style="display:none">
                        <span class="cs-sec-label"><?php esc_html_e( 'API Key:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
                                <input type="password" id="cs-sec-gemini-key" class="cs-text-input cs-sec-key-input"
                                        autocomplete="off" placeholder="AIza…">
                                <button type="button" class="cs-btn-secondary" id="cs-sec-reveal-gemini-key" style="display:none">&#128065; <?php esc_html_e( 'Show', 'cloudscale-cyber-devtools' ); ?></button>
                                <button type="button" class="cs-btn-secondary" id="cs-sec-test-gemini-key">
								<?php esc_html_e( 'Test Key', 'cloudscale-cyber-devtools' ); ?>
                                </button>
                                <span id="cs-sec-gemini-key-status" class="cs-sec-key-status"></span>
                            </div>
                            <span class="cs-hint">
                            <?php
                            echo wp_kses(
                                __( 'Get your key at <a href="https://aistudio.google.com/app/apikey" target="_blank" rel="noopener">aistudio.google.com</a>. Stored in wp_options.', 'cloudscale-cyber-devtools' ),
                                array(
									'a' => array(
										'href'   => array(),
										'target' => array(),
										'rel'    => array(),
									),
                                )
                            );
							?>
                                                    </span>
                        </div>
                    </div>

                    <div class="cs-sec-row">
                        <span class="cs-sec-label"><?php esc_html_e( 'AI model:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <select id="cs-sec-deep-model" class="cs-sec-select">
                                <option value="_auto_deep">&#x2728; Auto</option>
                            </select>
                        </div>
                    </div>

                    <div class="cs-sec-row cs-sec-row-prompt">
                        <span class="cs-sec-label"><?php esc_html_e( 'System prompt:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <textarea id="cs-sec-prompt" class="cs-sec-prompt-area" rows="10"></textarea>
                            <div style="display:flex;align-items:center;gap:6px;margin-top:8px;flex-wrap:wrap">
                                <button type="button" class="cs-btn-secondary cs-btn-sm" id="cs-sec-copy-prompt">&#x2398; <?php esc_html_e( 'Copy', 'cloudscale-cyber-devtools' ); ?></button>
                                <button type="button" class="cs-btn-secondary cs-btn-sm" id="cs-sec-reset-prompt"><?php esc_html_e( 'Reset to default', 'cloudscale-cyber-devtools' ); ?></button>
                                <div style="flex:1"></div>
                                <button type="button" class="cs-btn-primary" id="cs-sec-save">&#x1F4BE; <?php esc_html_e( 'Save Settings', 'cloudscale-cyber-devtools' ); ?></button>
                                <span class="cs-settings-saved" id="cs-sec-saved">&#x2713; <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                            </div>
                        </div>
                    </div>

                </div>

                <hr class="cs-sec-divider">

                <!-- Notifications -->
                <div id="cs-notifications-settings" class="cs-sec-settings" style="margin-top:0;padding-top:0;">
                    <div class="cs-sec-row">
                        <span class="cs-sec-label" style="font-weight:600;"><?php esc_html_e( 'Notifications:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <span class="cs-hint"><?php esc_html_e( 'Global alert settings for scheduled scans, threat monitor, and uptime monitor.', 'cloudscale-cyber-devtools' ); ?></span>
                        </div>
                    </div>
                    <div class="cs-sec-row">
                        <span class="cs-sec-label"><?php esc_html_e( 'Send email alerts:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
                                <input type="checkbox" id="cs-notify-email-enabled" <?php checked( $notify_enabled ); ?>>
                                <span><?php /* translators: %s is the admin email address */ printf( esc_html__( 'Send to %s', 'cloudscale-cyber-devtools' ), '<strong>' . esc_html( get_option( 'admin_email' ) ) . '</strong>' ); ?></span>
                            </label>
                        </div>
                    </div>
                    <div class="cs-sec-row">
                        <span class="cs-sec-label"><?php esc_html_e( 'Email override:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <input type="email" id="cs-notify-email" class="cs-text-input"
                                    placeholder="<?php echo esc_attr( get_option( 'admin_email' ) ); ?>"
                                    value="<?php echo esc_attr( $notify_email ); ?>"
                                    style="max-width:280px;">
                            <span class="cs-hint"><?php esc_html_e( 'Leave blank to use the admin email above.', 'cloudscale-cyber-devtools' ); ?></span>
                        </div>
                    </div>
                    <div class="cs-sec-row">
                        <span class="cs-sec-label"><?php esc_html_e( 'Telegram:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <?php CloudScale_Telegram::render_settings_fields( 'cloudscale-cyber-devtools', 'Cyber DevTools' ); ?>
                        </div>
                    </div>
                    <div class="cs-sec-row">
                        <span class="cs-sec-label"></span>
                        <div class="cs-sec-control">
                            <div style="display:flex;align-items:center;gap:8px;">
                                <button type="button" class="cs-btn-primary" id="cs-notify-save">&#x1F4BE; <?php esc_html_e( 'Save Notification Settings', 'cloudscale-cyber-devtools' ); ?></button>
                                <span class="cs-settings-saved" id="cs-notify-saved">&#x2713; <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                            </div>
                        </div>
                    </div>
                </div>

                <hr class="cs-sec-divider">

                <!-- Scheduled scan -->
                <div class="cs-sec-settings" style="margin-top:0;padding-top:0;">
                    <div class="cs-sec-row">
                        <span class="cs-sec-label"><?php esc_html_e( 'Scheduled Scan:', 'cloudscale-cyber-devtools' ); ?></span>
                        <div class="cs-sec-control">
                            <label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
                                <input type="checkbox" id="cs-sched-enabled" <?php checked( $sched_enabled ); ?>>
                                <span><?php esc_html_e( 'Run automatically on a schedule', 'cloudscale-cyber-devtools' ); ?></span>
                            </label>
                            <?php if ( $next_run ) : ?>
                            <span class="cs-hint"><?php /* translators: %s is a formatted date/time string e.g. "Mon 5 May 2025, 3:00pm" */ printf( esc_html__( 'Next run: %s', 'cloudscale-cyber-devtools' ), esc_html( wp_date( 'D j M Y, g:ia', $next_run ) ) ); ?></span>
                            <?php endif; ?>
                        </div>
                    </div>
                    <div id="cs-sched-options" <?php echo $sched_enabled ? 'style="display:flex;flex-direction:column;gap:16px;"' : 'style="display:none"'; ?>>
                        <div class="cs-sec-row">
                            <span class="cs-sec-label"><?php esc_html_e( 'Frequency:', 'cloudscale-cyber-devtools' ); ?></span>
                            <div class="cs-sec-control">
                                <select id="cs-sched-freq" class="cs-sec-select" style="width:auto;max-width:180px;">
                                    <option value="weekly"  <?php selected( $sched_freq, 'weekly' ); ?>><?php esc_html_e( 'Weekly', 'cloudscale-cyber-devtools' ); ?></option>
                                    <option value="monthly" <?php selected( $sched_freq, 'monthly' ); ?>><?php esc_html_e( 'Monthly', 'cloudscale-cyber-devtools' ); ?></option>
                                </select>
                            </div>
                        </div>
                        <div class="cs-sec-row">
                            <span class="cs-sec-label"></span>
                            <div class="cs-sec-control">
                                <div style="display:flex;align-items:center;gap:8px;">
                                    <button type="button" class="cs-btn-primary" id="cs-sched-save">&#x1F4BE; <?php esc_html_e( 'Save Schedule', 'cloudscale-cyber-devtools' ); ?></button>
                                    <span class="cs-settings-saved" id="cs-sched-saved">&#x2713; <?php esc_html_e( 'Saved', 'cloudscale-cyber-devtools' ); ?></span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                </div><!-- /AI settings -->
                </div>

                <div class="cs-panel" id="cs-panel-ai-cyber-audit">
                <div class="cs-section-header" style="background:linear-gradient(90deg,#022c22 0%,#065f46 100%);border-left:3px solid #34d399;">
                    <span>🕵️ <?php esc_html_e( 'AI Cyber Audit', 'cloudscale-cyber-devtools' ); ?></span>
                    <span class="cs-header-hint"><?php esc_html_e( 'AI-powered deep dive security scanning, internal config, plugin code analysis, and external exposure checks', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <div class="cs-panel-body">
                <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:14px;padding:10px 14px;background:#f0fdf4;border-radius:8px;border:1px solid #bbf7d0;">
                    <label for="cs-audit-target-url" style="font-size:12px;font-weight:600;color:#14532d;white-space:nowrap;">
                        🎯 <?php esc_html_e( 'Target site:', 'cloudscale-cyber-devtools' ); ?>
                    </label>
                    <input type="url" id="cs-audit-target-url"
                            value="<?php echo esc_attr( home_url( '/' ) ); ?>"
                            placeholder="https://example.com/"
                            style="flex:1;min-width:200px;max-width:420px;font-size:13px;padding:5px 10px;border:1px solid #86efac;border-radius:6px;color:#0f172a;background:#fff;" />
                    <span id="cs-audit-url-status" style="font-size:12px;font-weight:600;color:#15803d;">&#x2714; <?php esc_html_e( 'This site', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <div class="cs-scan-col-header">
                    <?php
                    self::render_explain_btn(
                        'deep-scan',
                        'AI Deep Dive Cyber Audit',
                        array(
							array(
								'name' => 'What it checks',
								'rec'  => 'Overview',
								'html' => 'Checks your WordPress core settings, active plugins and themes, user accounts, file permissions, and wp-config.php, then adds <strong>live HTTP probes</strong> of your site: SSL/TLS validity and strength, login page exposure, XML-RPC state, REST API user enumeration, author enumeration, directory listing, and server version headers.',
							),
							array(
								'name' => 'Plugin code triage',
								'rec'  => 'AI static scan',
								'html' => 'The AI pre-screens plugin PHP files for suspicious patterns (eval, base64_decode, remote code execution sinks) and classifies each finding as <strong>Confirmed / False Positive / Needs Context</strong> before the main analysis. This reduces noise and focuses the report on real risks.',
							),
							array(
								'name' => 'DNS checks',
								'rec'  => 'Email security',
								'html' => 'SPF, DMARC, and DKIM records are checked only when your domain has an MX record. If you have no email configured, these checks are skipped, no false positives.',
							),
							array(
								'name' => 'Speed',
								'rec'  => '30 to 90s',
								'html' => 'The deep dive makes outbound HTTP and DNS requests so duration depends on your network. Typical completion is 30 to 90 seconds. The browser connection is closed immediately via <code>fastcgi_finish_request()</code>; a progress bar polls every 3 seconds.',
							),
						)
                    );
                    ?>
                </div>
                <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
                    <button id="cs-deep-scan-btn" class="cs-btn-primary cs-btn-deep" disabled>
                        🕵️ <?php esc_html_e( 'Run AI Deep Dive Cyber Audit', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <button id="cs-deep-cancel-btn" class="cs-btn-secondary" style="display:none">
                        ✕ <?php esc_html_e( 'Cancel', 'cloudscale-cyber-devtools' ); ?>
                    </button>
                    <span id="cs-deep-model-badge" class="cs-scan-model-badge"></span>
                </div>
                <span id="cs-deep-scan-status" class="cs-vuln-inline-msg"></span>
                <div id="cs-deep-progress" class="cs-scan-progress">
                    <div class="cs-scan-progress-fill deep"></div>
                </div>
                <div id="cs-deep-results" class="cs-vuln-results" style="display:none;margin-top:6px"></div>
                </div>
                </div><!-- /cs-panel-body -->
                </div><!-- /cs-panel-ai-cyber-audit -->

                <!-- Scan History -->
                <div class="cs-panel" id="cs-panel-scan-history">
                <div class="cs-section-header" style="background:linear-gradient(90deg,#1e1b4b 0%,#4338ca 100%);border-left:3px solid #818cf8;">
                    <span>📈 <?php esc_html_e( 'Scan History', 'cloudscale-cyber-devtools' ); ?></span>
                    <span class="cs-header-hint"><?php esc_html_e( 'Last 50 scans, track your security score over time', 'cloudscale-cyber-devtools' ); ?></span>
                    <?php
                    self::render_explain_btn(
                        'scan-history',
                        'Scan History',
                        array(
							array(
								'name' => 'What is tracked',
								'rec'  => 'Overview',
								'html' => 'Every AI Deep Dive Cyber Audit saves a summary entry: scan date, model used, severity counts (critical / high / medium / low), and the full findings list. The last 50 scans are retained.',
							),
							array(
								'name' => 'Score trend chart',
								'rec'  => 'Info',
								'html' => 'The chart plots your critical + high finding count over time. A downward trend means your security posture is improving. Spikes after a plugin update or site change are worth investigating.',
							),
							array(
								'name' => 'Reload a scan',
								'rec'  => 'Info',
								'html' => 'Click any row in the history table to reload that scan\'s full findings report. Useful for comparing before-and-after states when remediating issues, without needing to re-run the scan.',
							),
						)
                    );
                    ?>
                </div>
                <div class="cs-panel-body" id="cs-scan-history-wrap">
                <?php
                $history = get_option( 'csdt_scan_history', array() );
                // Exclude any adhoc (external-URL) entries that may have been stored here
                // by older code paths — own-site scans never have a target_url field.
                // Preserve original numeric keys so data-idx matches csdt_scan_history for ajax_scan_history_item.
                $own_history = array_filter(
                    $history,
                    static function ( $e ) {
						return empty( $e['target_url'] );
					}
                );
                if ( ! empty( $own_history ) ) :
					?>
                <canvas id="cs-scan-history-chart" height="180"
                    style="width:100%;max-width:100%;display:block;margin-bottom:20px;border-radius:6px;background:#fff;border:1px solid #e2e8f0;"></canvas>
					<?php
                endif; if ( empty( $own_history ) ) :
					?>
                    <div style="text-align:center;padding:32px 20px;background:#f8fafc;border:2px dashed #e2e8f0;border-radius:8px;">
                        <div style="font-size:2rem;margin-bottom:10px;">🛡️</div>
                        <div style="font-weight:700;font-size:15px;color:#0f172a;margin-bottom:6px;"><?php esc_html_e( 'No scans yet', 'cloudscale-cyber-devtools' ); ?></div>
                        <div style="font-size:13px;color:#6b7280;margin:0 auto 18px;max-width:380px;"><?php esc_html_e( 'Run your first AI Cyber Audit above. Each scan is saved here so you can track your security posture over time.', 'cloudscale-cyber-devtools' ); ?></div>
                        <a href="#cs-panel-ai-cyber-audit" onclick="document.getElementById('cs-panel-ai-cyber-audit').scrollIntoView({behavior:'smooth'});return false;" style="display:inline-block;background:#6366f1;color:#fff;font-weight:600;font-size:13px;padding:7px 18px;border-radius:6px;text-decoration:none;">Run First Scan ↑</a>
                    </div>
                <?php else : ?>
                    <style>
                        #cs-scan-history-list > summary::-webkit-details-marker { display:none; }
                        #cs-scan-history-list > summary { transition:background .15s,border-color .15s,box-shadow .15s; }
                        #cs-scan-history-list > summary:hover { background:#eef2ff;border-color:#6366f1;box-shadow:0 1px 3px rgba(99,102,241,.18); }
                        #cs-scan-history-list > summary:focus-visible { outline:2px solid #6366f1;outline-offset:2px; }
                        #cs-scan-history-list > summary .cs-scan-toggle-caret { transition:transform .2s; }
                        #cs-scan-history-list[open] > summary .cs-scan-toggle-caret { transform:rotate(180deg); }
                        #cs-scan-history-list[open] > summary .cs-scan-toggle-label::before { content:"Hide "; }
                        #cs-scan-history-list:not([open]) > summary .cs-scan-toggle-label::before { content:"Show all "; }
                    </style>
                    <details id="cs-scan-history-list">
                    <summary style="cursor:pointer;list-style:none;display:flex;align-items:center;justify-content:center;gap:10px;padding:10px 16px;margin-bottom:12px;user-select:none;background:#f1f5f9;border:1.5px solid #cbd5e1;border-radius:8px;font-size:13px;font-weight:600;color:#1e293b;">
                        <span class="cs-scan-toggle-label"></span>
                        <span style="font-weight:700;color:#4f46e5;"><?php echo esc_html( sprintf( /* translators: %d is the number of security scans */ _n( '%d scan', '%d scans', count( $own_history ), 'cloudscale-cyber-devtools' ), count( $own_history ) ) ); ?></span>
                        <span class="cs-scan-toggle-caret" style="font-size:14px;color:#4f46e5;line-height:1;display:inline-block;">&#x25BC;</span>
                    </summary>
                    <div style="display:flex;flex-direction:column;gap:6px;">
                    <?php
                    foreach ( $own_history as $idx => $entry ) :
                        $score        = (int) ( $entry['score'] ?? 0 );
                        $label        = esc_html( $entry['score_label'] ?? '' );
                        $type_label   = $entry['type'] === 'deep' ? 'Deep Dive' : 'AI Cyber Audit';
                        $date         = $entry['scanned_at'] ? wp_date( 'D j M Y, g:ia', $entry['scanned_at'] ) : '';
                        $score_color  = $score >= 90 ? '#22c55e' : ( $score >= 75 ? '#4ade80' : ( $score >= 55 ? '#fbbf24' : ( $score >= 35 ? '#f97316' : '#ef4444' ) ) );
                        $has_findings = ! empty( $entry['findings'] );
						?>
                        <div style="display:flex;align-items:flex-start;gap:14px;padding:10px 12px;background:#f8fafc;border-radius:6px;border:1px solid #e2e8f0;">
                            <div style="flex-shrink:0;text-align:center;min-width:48px;">
                                <div style="font-size:1.4rem;font-weight:700;color:<?php echo esc_attr( $score_color ); ?>;line-height:1;"><?php echo esc_html( $score ); ?></div>
                                <div style="font-size:10px;color:<?php echo esc_attr( $score_color ); ?>;opacity:.8;"><?php echo esc_html( $label ); ?></div>
                            </div>
                            <div style="flex:1;min-width:0;">
                                <div style="display:flex;align-items:center;gap:8px;margin-bottom:3px;flex-wrap:wrap;">
                                    <span style="font-size:12px;font-weight:600;color:#0f172a;"><?php echo esc_html( $type_label ); ?></span>
                                    <span style="font-size:12px;font-weight:400;color:#64748b;"><?php echo esc_html( $date ); ?></span>
                                    <?php if ( $has_findings ) : ?>
                                    <button type="button"
                                        class="csdt-view-report-btn"
                                        data-idx="<?php echo esc_attr( $idx ); ?>"
                                        data-type="<?php echo esc_attr( $type_label ); ?>"
                                        data-date="<?php echo esc_attr( $date ); ?>"
                                        data-score="<?php echo esc_attr( $score ); ?>"
                                        data-label="<?php echo esc_attr( $label ); ?>"
                                        data-summary="<?php echo esc_attr( $entry['summary'] ?? '' ); ?>"
                                        style="font-size:11px;font-weight:600;color:#60a5fa;background:none;border:1px solid #60a5fa;border-radius:4px;padding:1px 8px;cursor:pointer;line-height:1.5;flex-shrink:0;">
                                        View Report
                                    </button>
                                    <button type="button"
                                        class="csdt-history-pdf-btn"
                                        data-idx="<?php echo esc_attr( $idx ); ?>"
                                        data-scan-type="<?php echo esc_attr( $entry['type'] ?? 'standard' ); ?>"
                                        style="font-size:11px;font-weight:600;color:#6b7280;background:none;border:1px solid #d1d5db;border-radius:4px;padding:1px 8px;cursor:pointer;line-height:1.5;flex-shrink:0;">
                                        ↓ PDF
                                    </button>
                                    <?php endif; ?>
                                </div>
                                <div style="font-size:12px;color:#374151;line-height:1.5;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;">
                                    <?php echo esc_html( $entry['summary'] ?? '' ); ?>
                                </div>
                            </div>
                        </div>
                    <?php endforeach; ?>
                    </div>
                    </details>
                <?php endif; ?>
                </div><!-- /cs-panel-body scan-history -->
                </div><!-- /cs-panel-scan-history -->

                <div class="cs-panel" id="cs-panel-adhoc-audits">
                <div class="cs-section-header" style="background:linear-gradient(90deg,#0c1a2e 0%,#1e3a5f 100%);border-left:3px solid #60a5fa;">
                    <span>🌐 <?php esc_html_e( 'Adhoc Cyber Audits', 'cloudscale-cyber-devtools' ); ?></span>
                    <span class="cs-header-hint"><?php esc_html_e( 'External site scans, results saved here', 'cloudscale-cyber-devtools' ); ?></span>
                </div>
                <div class="cs-panel-body">
                    <div id="cs-adhoc-scan-area" style="display:none;margin-bottom:16px;">
                        <span id="cs-adhoc-scan-target" style="font-size:12px;font-weight:600;color:#374151;display:block;margin-bottom:6px;"></span>
                        <span id="cs-adhoc-scan-status" class="cs-vuln-inline-msg"></span>
                        <div id="cs-adhoc-progress" class="cs-scan-progress" style="margin-top:6px;">
                            <div class="cs-scan-progress-fill"></div>
                        </div>
                    </div>
                    <div id="cs-adhoc-list">
                        <div id="cs-adhoc-empty" style="text-align:center;padding:28px 20px;background:#f8fafc;border:2px dashed #e2e8f0;border-radius:8px;">
                            <div style="font-size:2rem;margin-bottom:8px;">🌐</div>
                            <div style="font-weight:700;font-size:14px;color:#0f172a;margin-bottom:5px;"><?php esc_html_e( 'No adhoc scans yet', 'cloudscale-cyber-devtools' ); ?></div>
                            <div style="font-size:13px;color:#6b7280;max-width:380px;margin:0 auto;"><?php esc_html_e( 'Enter a different site URL above, then run an AI Deep Dive Cyber Audit.', 'cloudscale-cyber-devtools' ); ?></div>
                        </div>
                    </div>
                </div>
                </div><!-- /cs-panel-adhoc-audits -->
        <?php
    }

    /**
     * Hunt for a domain across cron events, options values, and active plugin headers.
     * Called from the CSP violation log "Where is this from?" button.
     */
    public static function ajax_csp_domain_hunt(): void {
        check_ajax_referer( self::SECURITY_NONCE, 'nonce' );
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        try {
            $domain = isset( $_POST['domain'] ) ? sanitize_text_field( wp_unslash( $_POST['domain'] ) ) : '';
            if ( ! $domain ) {
                wp_send_json_error( 'No domain provided.' );
            }

            global $wpdb;
            $results = array();

            // ── 1. Cron events ──────────────────────────────────────────────────
            $cron      = _get_cron_array();
            $cron_hits = array();
            if ( is_array( $cron ) ) {
                foreach ( $cron as $timestamp => $hooks ) {
                    foreach ( $hooks as $hook => $events ) {
                        if ( stripos( $hook, $domain ) !== false ) {
                            $cron_hits[] = $hook . ' (next: ' . gmdate( 'Y-m-d H:i', (int) $timestamp ) . ' UTC)';
                        }
                        foreach ( $events as $event ) {
                            $serialized = maybe_serialize( $event['args'] ?? array() );
                            if ( stripos( (string) $serialized, $domain ) !== false ) {
                                $cron_hits[] = $hook . ' args contain domain (next: ' . gmdate( 'Y-m-d H:i', (int) $timestamp ) . ' UTC)';
                            }
                        }
                    }
                }
            }
            if ( $cron_hits ) {
                $results['cron'] = array_unique( $cron_hits );
            }

            // ── 2. wp_options values ─────────────────────────────────────────────
            $like        = '%' . $wpdb->esc_like( $domain ) . '%';
            $option_hits = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table; WP_Query not applicable
                $wpdb->prepare(
                    "SELECT option_name FROM {$wpdb->options} WHERE option_value LIKE %s AND autoload = 'yes' LIMIT 20",
                    $like
                ),
                ARRAY_A
            );
            if ( $option_hits ) {
                $results['options'] = array_column( $option_hits, 'option_name' );
            }

            // ── 3. Non-autoloaded options (slower, limit tightly) ────────────────
            $slow_hits = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table; WP_Query not applicable
                $wpdb->prepare(
                    "SELECT option_name FROM {$wpdb->options} WHERE option_value LIKE %s AND autoload != 'yes' LIMIT 10",
                    $like
                ),
                ARRAY_A
            );
            if ( $slow_hits ) {
                $results['options_noautoload'] = array_column( $slow_hits, 'option_name' );
            }

            // ── 4. Active plugin file headers ────────────────────────────────────
            $active      = get_option( 'active_plugins', array() );
            $plugin_hits = array();
            foreach ( $active as $plugin_file ) {
                $path = WP_PLUGIN_DIR . '/' . $plugin_file;
                if ( ! file_exists( $path ) ) {
					continue; }
                // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_get_contents -- reading plugin file header at known local path
                $header = file_get_contents( $path, false, null, 0, 4000 );
                if ( $header && stripos( $header, $domain ) !== false ) {
                    $plugin_hits[] = $plugin_file;
                }
            }
            if ( $plugin_hits ) {
                $results['active_plugins'] = $plugin_hits;
            }

            // ── 5. Inactive plugin directories (name match only) ─────────────────
            $all_plugins   = get_plugins();
            $inactive_hits = array();
            foreach ( $all_plugins as $file => $data ) {
                if ( in_array( $file, $active, true ) ) {
					continue; }
                $slug = dirname( $file );
                if ( stripos( $slug, $domain ) !== false || stripos( $data['Name'] ?? '', $domain ) !== false ) {
                    $inactive_hits[] = ( $data['Name'] ?? $file ) . ' (inactive)';
                }
            }
            if ( $inactive_hits ) {
                $results['inactive_plugins'] = $inactive_hits;
            }

            wp_send_json_success(
                array(
					'domain'  => $domain,
					'results' => $results,
					'found'   => ! empty( $results ),
                )
            );
        } catch ( \Throwable $e ) {
            wp_send_json_error( $e->getMessage(), 500 );
        }
    }
}

CloudScale_DevTools::init();
