<?php
/**
 * Name: Favicon Generator
 * Description: Create a custom favicon using letters, emojis, or icons with customizable colors, fonts, shapes, and styles. Or upload your own icon.
 * Category: Utilities
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Quick Brand Favicon",
 *     "description": "Create a simple favicon with your brand initials, emojis, or icons without needing design software."
 *   },
 *   {
 *     "title": "Custom Icon Upload",
 *     "description": "Upload your own icon image and let the plugin handle all the favicon size requirements."
 *   }
 * ]
 * Added: 2025-12-25
 * Tags: favicon, icon, branding, generator, site-icon, emoji
 * WhereToFind: {
 *   "location": "Configure in module settings",
 *   "url": "",
 *   "instructions": "Set up your favicon with colors, letters, emojis, or icons",
 *   "newTab": false
 * }
 */

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

class SwitchboardFaviconGenerator {

    private $upload_dir;
    private $upload_url;
    private $dirs_initialized = false;

    public function __construct() {
        // Hook into wp_head to output favicon
        add_action('wp_head', [$this, 'output_favicon'], 1);
        add_action('admin_head', [$this, 'output_favicon'], 1);

        // Remove default WordPress site icon - must run before wp_site_icon (priority 99)
        add_action('wp_head', [$this, 'maybe_remove_wp_site_icon'], 0);

        // Filter site icon URL
        add_filter('get_site_icon_url', [$this, 'filter_site_icon'], 10, 3);

        // AJAX handler for generating favicon
        add_action('wp_ajax_switchboard_generate_favicon', [$this, 'ajax_generate_favicon']);
    }

    /**
     * Initialize upload directories (lazy loading)
     */
    private function init_dirs() {
        if ($this->dirs_initialized) {
            return;
        }

        $upload = wp_upload_dir();
        $this->upload_dir = $upload['basedir'] . '/switchboard-favicons';
        $this->upload_url = $upload['baseurl'] . '/switchboard-favicons';
        $this->dirs_initialized = true;
    }

    /**
     * Ensure upload directory exists (only when needed)
     */
    private function ensure_upload_dir() {
        $this->init_dirs();
        if (!file_exists($this->upload_dir)) {
            wp_mkdir_p($this->upload_dir);
        }
    }

    /**
     * Sanitize SVG file to prevent XSS and XXE attacks
     *
     * @param string $file_path Path to the SVG file
     * @return string|false Sanitized SVG content or false on failure
     */
    private function sanitize_svg($file_path) {
        if (!file_exists($file_path)) {
            return false;
        }

        $svg_content = file_get_contents($file_path);
        if (empty($svg_content)) {
            return false;
        }

        // Disable XML external entity loading (XXE protection)
        $previous_value = libxml_disable_entity_loader(true);
        libxml_use_internal_errors(true);

        $dom = new DOMDocument();
        $dom->formatOutput = false;
        $dom->preserveWhiteSpace = false;

        // Load SVG with LIBXML_NONET to prevent network access
        if (!$dom->loadXML($svg_content, LIBXML_NONET | LIBXML_NOENT)) {
            libxml_disable_entity_loader($previous_value);
            libxml_clear_errors();
            return false;
        }

        // Remove potentially dangerous elements
        $dangerous_tags = [
            'script', 'iframe', 'object', 'embed', 'foreignObject',
            'handler', 'set', 'animate', 'animateMotion', 'animateTransform',
            'mpath', 'image', // image can load external resources
        ];

        foreach ($dangerous_tags as $tag) {
            $elements = $dom->getElementsByTagName($tag);
            while ($elements->length > 0) {
                $element = $elements->item(0);
                if ($element && $element->parentNode) {
                    $element->parentNode->removeChild($element);
                }
            }
        }

        // Remove dangerous attributes from all elements
        $dangerous_attrs = [
            'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover',
            'onmousemove', 'onmouseout', 'onkeydown', 'onkeypress', 'onkeyup',
            'onload', 'onerror', 'onfocus', 'onblur', 'onsubmit', 'onreset',
            'onselect', 'onchange', 'onabort', 'onunload', 'onscroll', 'onresize',
            'xlink:href', // Only remove if it starts with javascript:
        ];

        $xpath = new DOMXPath($dom);
        $all_elements = $xpath->query('//*');

        foreach ($all_elements as $element) {
            // Remove event handlers and script-loading attributes
            foreach ($dangerous_attrs as $attr) {
                if ($element->hasAttribute($attr)) {
                    $element->removeAttribute($attr);
                }
            }

            // Check href attributes for javascript: protocol
            if ($element->hasAttribute('href')) {
                $href = $element->getAttribute('href');
                if (preg_match('/^\s*javascript:/i', $href)) {
                    $element->removeAttribute('href');
                }
            }

            // Check xlink:href for javascript: protocol
            if ($element->hasAttributeNS('http://www.w3.org/1999/xlink', 'href')) {
                $href = $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href');
                if (preg_match('/^\s*javascript:/i', $href)) {
                    $element->removeAttributeNS('http://www.w3.org/1999/xlink', 'href');
                }
            }

            // Check style attribute for url() that could load external resources
            if ($element->hasAttribute('style')) {
                $style = $element->getAttribute('style');
                // Remove any url() references (can load external resources)
                $style = preg_replace('/url\s*\([^)]*\)/i', '', $style);
                // Remove expression() (IE-specific, can execute JS)
                $style = preg_replace('/expression\s*\([^)]*\)/i', '', $style);
                $element->setAttribute('style', $style);
            }
        }

        // Restore previous entity loader setting
        libxml_disable_entity_loader($previous_value);
        libxml_clear_errors();

        // Return sanitized SVG
        return $dom->saveXML($dom->documentElement);
    }

    /**
     * Sanitize and save an uploaded SVG file
     *
     * @param string $file_path Path to the uploaded SVG
     * @return bool True if sanitized and saved successfully
     */
    private function sanitize_svg_file($file_path) {
        $sanitized = $this->sanitize_svg($file_path);
        if ($sanitized === false) {
            return false;
        }

        // Add XML declaration and write back
        $xml_declaration = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        return file_put_contents($file_path, $xml_declaration . $sanitized) !== false;
    }

    /**
     * Remove WordPress default site icon if we have a custom one
     */
    public function maybe_remove_wp_site_icon() {
        $settings = get_option('switchboard_favicon-generator_settings', []);

        if (empty($settings)) {
            return;
        }

        $has_favicon = false;
        $use_uploaded = isset($settings['use_uploaded']) && $settings['use_uploaded'] === '1';

        if ($use_uploaded) {
            $has_favicon = !empty($settings['uploaded_icon_url']);
        } else {
            $has_favicon = !empty($settings['generated_favicon']) || !empty($settings['letters']) || !empty($settings['emoji']) || !empty($settings['icon']);
        }

        if ($has_favicon) {
            remove_action('wp_head', 'wp_site_icon', 99);
        }
    }

    public function register_data_locations() {
        return [
            'options' => ['switchboard_favicon-generator_settings'],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
            'transients' => []
        ];
    }

    public function render_settings($settings = []) {
        // Extract settings with defaults
        $bg_color = isset($settings['bg_color']) ? $settings['bg_color'] : '#3b82f6';
        $text_color = isset($settings['text_color']) ? $settings['text_color'] : '#ffffff';
        $transparent_bg = isset($settings['transparent_bg']) ? $settings['transparent_bg'] : '';
        $letters = isset($settings['letters']) ? $settings['letters'] : '';
        $font_size = isset($settings['font_size']) ? intval($settings['font_size']) : 64;
        $font_family = isset($settings['font_family']) ? $settings['font_family'] : 'system';
        $font_weight = isset($settings['font_weight']) ? $settings['font_weight'] : 'bold';
        $border_radius = isset($settings['border_radius']) ? $settings['border_radius'] : 'rounded';
        $content_type = isset($settings['content_type']) ? $settings['content_type'] : 'letters';
        $emoji = isset($settings['emoji']) ? $settings['emoji'] : '';
        $icon = isset($settings['icon']) ? $settings['icon'] : '';
        $use_uploaded = isset($settings['use_uploaded']) ? $settings['use_uploaded'] : '';
        $uploaded_icon_id = isset($settings['uploaded_icon_id']) ? intval($settings['uploaded_icon_id']) : 0;
        $uploaded_icon_url = isset($settings['uploaded_icon_url']) ? $settings['uploaded_icon_url'] : '';
        $generated_favicon = isset($settings['generated_favicon']) ? $settings['generated_favicon'] : '';

        // Popular emojis for favicon
        $popular_emojis = ['⚡', '🚀', '💡', '🎯', '🔥', '💎', '⭐', '🌟', '✨', '💫', '🎨', '🎪', '🎭', '🎬', '🎮', '🎲', '🏆', '🥇', '👑', '💼', '📱', '💻', '🖥️', '⌨️', '🔧', '⚙️', '🛠️', '📦', '📊', '📈', '💰', '🏠', '🏢', '🌍', '🌎', '🌏', '☀️', '🌙', '⛅', '🌈', '❤️', '💚', '💙', '💜', '🧡', '💛', '🤍', '🖤', '☕', '🍕'];

        // Simple icons (using SVG paths) - ~50 icons
        $simple_icons = [
            // Basic
            'home' => ['name' => 'Home', 'path' => 'M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'],
            'star' => ['name' => 'Star', 'path' => 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z'],
            'heart' => ['name' => 'Heart', 'path' => 'M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'],
            'bolt' => ['name' => 'Bolt', 'path' => 'M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66.19-.34.05-.08.07-.12C8.48 10.94 10.42 7.54 13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15C12.96 17.55 11 21 11 21z'],
            'zap' => ['name' => 'Zap', 'path' => 'M13 2L3 14h9l-1 8 10-12h-9l1-8z'],
            // Tech
            'globe' => ['name' => 'Globe', 'path' => 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z'],
            'code' => ['name' => 'Code', 'path' => 'M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z'],
            'terminal' => ['name' => 'Terminal', 'path' => 'M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8h16v10zm-2-1h-6v-2h6v2zM7.5 17l-1.41-1.41L8.67 13l-2.59-2.59L7.5 9l4 4-4 4z'],
            'cpu' => ['name' => 'CPU', 'path' => 'M15 9H9v6h6V9zm-2 4h-2v-2h2v2zm8-2V9h-2V7c0-1.1-.9-2-2-2h-2V3h-2v2h-2V3H9v2H7c-1.1 0-2 .9-2 2v2H3v2h2v2H3v2h2v2c0 1.1.9 2 2 2h2v2h2v-2h2v2h2v-2h2c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2zm-4 6H7V7h10v10z'],
            'database' => ['name' => 'Database', 'path' => 'M12 3C7.58 3 4 4.79 4 7v10c0 2.21 3.59 4 8 4s8-1.79 8-4V7c0-2.21-3.58-4-8-4zm0 2c3.87 0 6 1.5 6 2s-2.13 2-6 2-6-1.5-6-2 2.13-2 6-2zm6 12c0 .5-2.13 2-6 2s-6-1.5-6-2v-2.23c1.61.78 3.72 1.23 6 1.23s4.39-.45 6-1.23V17zm0-5c0 .5-2.13 2-6 2s-6-1.5-6-2V9.77c1.61.78 3.72 1.23 6 1.23s4.39-.45 6-1.23V12z'],
            'server' => ['name' => 'Server', 'path' => 'M4 1h16c1.1 0 2 .9 2 2v4c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V3c0-1.1.9-2 2-2zm0 8h16c1.1 0 2 .9 2 2v4c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2v-4c0-1.1.9-2 2-2zm0 8h16c1.1 0 2 .9 2 2v4c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2v-4c0-1.1.9-2 2-2zM9 5H7v2h2V5zm0 8H7v2h2v-2zm0 8H7v2h2v-2z'],
            'cloud' => ['name' => 'Cloud', 'path' => 'M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z'],
            'wifi' => ['name' => 'WiFi', 'path' => 'M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z'],
            // Objects
            'cube' => ['name' => 'Cube', 'path' => 'M21 16.5c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18-.21 0-.41-.06-.57-.18l-7.9-4.44A.991.991 0 0 1 3 16.5v-9c0-.38.21-.71.53-.88l7.9-4.44c.16-.12.36-.18.57-.18.21 0 .41.06.57.18l7.9 4.44c.32.17.53.5.53.88v9zM12 4.15L6.04 7.5 12 10.85l5.96-3.35L12 4.15zM5 15.91l6 3.38v-6.71L5 9.21v6.7zm14 0v-6.7l-6 3.37v6.71l6-3.38z'],
            'shield' => ['name' => 'Shield', 'path' => 'M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z'],
            'rocket' => ['name' => 'Rocket', 'path' => 'M12 2.5s4.5 2.04 4.5 10.5c0 2.49-1.04 5.57-1.6 7H9.1c-.56-1.43-1.6-4.51-1.6-7C7.5 4.54 12 2.5 12 2.5zm0 5a2 2 0 1 0 0 4 2 2 0 0 0 0-4zM7 22h4v-2.5c-.55-.32-1.11-.68-1.65-1.08L7 22zm10 0l-2.35-3.58c-.54.4-1.1.76-1.65 1.08V22h4z'],
            'settings' => ['name' => 'Settings', 'path' => 'M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z'],
            'mail' => ['name' => 'Mail', 'path' => 'M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z'],
            'cart' => ['name' => 'Cart', 'path' => 'M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z'],
            'music' => ['name' => 'Music', 'path' => 'M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z'],
            'camera' => ['name' => 'Camera', 'path' => 'M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4z M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z'],
            // Communication
            'phone' => ['name' => 'Phone', 'path' => 'M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z'],
            'chat' => ['name' => 'Chat', 'path' => 'M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z'],
            'send' => ['name' => 'Send', 'path' => 'M2.01 21L23 12 2.01 3 2 10l15 2-15 2z'],
            'bell' => ['name' => 'Bell', 'path' => 'M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z'],
            // Media
            'play' => ['name' => 'Play', 'path' => 'M8 5v14l11-7z'],
            'video' => ['name' => 'Video', 'path' => 'M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z'],
            'image' => ['name' => 'Image', 'path' => 'M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z'],
            'mic' => ['name' => 'Mic', 'path' => 'M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z'],
            'headphones' => ['name' => 'Headphones', 'path' => 'M12 1c-4.97 0-9 4.03-9 9v7c0 1.66 1.34 3 3 3h3v-8H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-4v8h3c1.66 0 3-1.34 3-3v-7c0-4.97-4.03-9-9-9z'],
            // Business
            'briefcase' => ['name' => 'Briefcase', 'path' => 'M20 7h-4V5c0-1.1-.9-2-2-2h-4c-1.1 0-2 .9-2 2v2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm-6 0h-4V5h4v2z'],
            'chart' => ['name' => 'Chart', 'path' => 'M3.5 18.5l6-6 4 4L22 6.92 20.59 5.5l-7.09 8-4-4L2 17l1.5 1.5z'],
            'trending' => ['name' => 'Trending', 'path' => 'M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z'],
            'dollar' => ['name' => 'Dollar', 'path' => 'M11.8 10.9c-2.27-.59-3-1.2-3-2.15 0-1.09 1.01-1.85 2.7-1.85 1.78 0 2.44.85 2.5 2.1h2.21c-.07-1.72-1.12-3.3-3.21-3.81V3h-3v2.16c-1.94.42-3.5 1.68-3.5 3.61 0 2.31 1.91 3.46 4.7 4.13 2.5.6 3 1.48 3 2.41 0 .69-.49 1.79-2.7 1.79-2.06 0-2.87-.92-2.98-2.1h-2.2c.12 2.19 1.76 3.42 3.68 3.83V21h3v-2.15c1.95-.37 3.5-1.5 3.5-3.55 0-2.84-2.43-3.81-4.7-4.4z'],
            'building' => ['name' => 'Building', 'path' => 'M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z'],
            // Nature
            'sun' => ['name' => 'Sun', 'path' => 'M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z'],
            'moon' => ['name' => 'Moon', 'path' => 'M9 2c-1.05 0-2.05.16-3 .46 4.06 1.27 7 5.06 7 9.54 0 4.48-2.94 8.27-7 9.54.95.3 1.95.46 3 .46 5.52 0 10-4.48 10-10S14.52 2 9 2z'],
            'leaf' => ['name' => 'Leaf', 'path' => 'M17.8 2.8C16 2.09 13.86 2 12 2c-1.86 0-4 .09-5.8.8C3.53 3.84 2 6.05 2 8.86V22h20V8.86c0-2.81-1.53-5.02-4.2-6.06zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z'],
            'tree' => ['name' => 'Tree', 'path' => 'M22 9.67c0-1.25-.58-2.39-1.57-3.18.15-.42.24-.86.24-1.32 0-2.07-1.64-3.76-3.73-3.86C15.71.49 14.24 0 12.67 0c-2.17 0-4.11.9-5.5 2.35C5.74 2.12 4.3 2.99 3.17 4.22 1.97 5.56 1.33 7.26 1.33 9c0 2.58 1.53 4.89 3.84 6 .48 2.84 2.94 5 5.83 5h4c3.04 0 5.52-2.4 5.67-5.41 0 0 0-.01.01-.01C22.03 13.41 22 12.21 22 11c0-.45-.17-.86-.35-1.33h.35z'],
            // Transport
            'car' => ['name' => 'Car', 'path' => 'M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z'],
            'plane' => ['name' => 'Plane', 'path' => 'M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z'],
            'ship' => ['name' => 'Ship', 'path' => 'M20 21c-1.39 0-2.78-.47-4-1.32-2.44 1.71-5.56 1.71-8 0C6.78 20.53 5.39 21 4 21H2v2h2c1.38 0 2.74-.35 4-.99 2.52 1.29 5.48 1.29 8 0 1.26.65 2.62.99 4 .99h2v-2h-2zM3.95 19H4c1.6 0 3.02-.88 4-2 .98 1.12 2.4 2 4 2s3.02-.88 4-2c.98 1.12 2.4 2 4 2h.05l1.89-6.68c.08-.26.06-.54-.06-.78s-.34-.42-.6-.5L20 10.62V6c0-1.1-.9-2-2-2h-3V1H9v3H6c-1.1 0-2 .9-2 2v4.62l-1.29.42c-.26.08-.48.26-.6.5s-.15.52-.06.78L3.95 19zM6 6h12v3.97L12 8 6 9.97V6z'],
            // UI
            'search' => ['name' => 'Search', 'path' => 'M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'],
            'menu' => ['name' => 'Menu', 'path' => 'M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z'],
            'plus' => ['name' => 'Plus', 'path' => 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'],
            'check' => ['name' => 'Check', 'path' => 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'],
            'close' => ['name' => 'Close', 'path' => 'M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'],
            'lock' => ['name' => 'Lock', 'path' => 'M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z'],
            'unlock' => ['name' => 'Unlock', 'path' => 'M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z'],
            'user' => ['name' => 'User', 'path' => 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z'],
            'users' => ['name' => 'Users', 'path' => 'M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z'],
            'calendar' => ['name' => 'Calendar', 'path' => 'M19 4h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V10h14v10zm0-12H5V6h14v2zm-7 5h5v5h-5v-5z'],
            'clock' => ['name' => 'Clock', 'path' => 'M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z'],
            'download' => ['name' => 'Download', 'path' => 'M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z'],
            'upload' => ['name' => 'Upload', 'path' => 'M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z'],
            'link' => ['name' => 'Link', 'path' => 'M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z'],
            'pin' => ['name' => 'Pin', 'path' => 'M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z'],
            'bookmark' => ['name' => 'Bookmark', 'path' => 'M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z'],
            'flag' => ['name' => 'Flag', 'path' => 'M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z'],
            'award' => ['name' => 'Award', 'path' => 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'],
            'coffee' => ['name' => 'Coffee', 'path' => 'M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM2 21h18v-2H2v2z'],
            'gift' => ['name' => 'Gift', 'path' => 'M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z'],
        ];
        ?>
        <input type="hidden" name="module" value="favicon-generator">

        <div class="sb-sp-group" style="margin-bottom: 24px;">
            <h3 class="sb-sp-heading">Favicon Generator</h3>
            <p class="sb-sp-description">Create a custom favicon with letters, emojis, icons, or upload your own.</p>
        </div>

        <!-- Preview (at the very top) -->
        <div class="sb-sp-group" style="margin-bottom: 24px;">
            <label class="sb-sp-label" style="margin-bottom: 12px; display: block;">Preview</label>
            <div id="favicon-preview-container" style="display: flex; align-items: flex-start; gap: 24px; padding: 24px; background: repeating-conic-gradient(var(--gray-200) 0% 25%, white 0% 50%) 50% / 12px 12px; border: 1px solid var(--gray-200); border-radius: 12px;">
                <!-- Generated preview -->
                <div id="generated-preview-wrapper" style="display: flex; align-items: flex-start; gap: 24px; <?php echo $use_uploaded === '1' ? 'display: none;' : ''; ?>">
                    <div style="text-align: center;">
                        <div style="background: white; padding: 12px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); display: inline-block;">
                            <canvas id="favicon-preview" width="128" height="128" style="display: block;"></canvas>
                        </div>
                        <span style="font-size: 11px; color: #64748b; margin-top: 8px; display: block;">128×128</span>
                    </div>
                    <div style="text-align: center;">
                        <div style="background: white; padding: 8px; border-radius: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); display: inline-block;">
                            <canvas id="favicon-preview-32" width="32" height="32" style="display: block;"></canvas>
                        </div>
                        <span style="font-size: 11px; color: #64748b; margin-top: 8px; display: block;">32×32</span>
                    </div>
                    <div style="text-align: center;">
                        <div style="background: white; padding: 6px; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); display: inline-block;">
                            <canvas id="favicon-preview-16" width="16" height="16" style="display: block;"></canvas>
                        </div>
                        <span style="font-size: 11px; color: #64748b; margin-top: 8px; display: block;">16×16</span>
                    </div>
                </div>
                <!-- Uploaded preview -->
                <div id="uploaded-preview-wrapper" style="<?php echo $use_uploaded !== '1' ? 'display: none;' : ''; ?>">
                    <div style="text-align: center;">
                        <div style="background: white; padding: 12px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); display: inline-block;">
                            <img id="uploaded-preview-img" src="<?php echo esc_url($uploaded_icon_url); ?>" style="width: 128px; height: 128px; object-fit: contain; display: block; <?php echo !$uploaded_icon_url ? 'opacity: 0.3;' : ''; ?>" onerror="this.style.opacity='0.3'">
                        </div>
                        <span style="font-size: 11px; color: #64748b; margin-top: 8px; display: block;"><?php echo $uploaded_icon_url ? 'Uploaded' : 'No image'; ?></span>
                    </div>
                </div>
            </div>
        </div>

        <!-- Mode Selection using toggle items -->
        <div class="sb-module-checkbox-grid" style="margin-bottom: 24px;">
            <label class="sb-module-checkbox-item <?php echo $use_uploaded !== '1' ? 'active' : ''; ?>" data-mode="generate" style="cursor: pointer;">
                <input type="radio" name="setting_use_uploaded" value="" <?php checked($use_uploaded, ''); ?> style="display: none;">
                <span style="flex: 1;">
                    <strong>Generate Custom</strong><br>
                    <small style="color: #64748b;">Create with letters, emoji, or icons</small>
                </span>
            </label>
            <label class="sb-module-checkbox-item <?php echo $use_uploaded === '1' ? 'active' : ''; ?>" data-mode="upload" style="cursor: pointer;">
                <input type="radio" name="setting_use_uploaded" value="1" <?php checked($use_uploaded, '1'); ?> style="display: none;">
                <span style="flex: 1;">
                    <strong>Upload Image</strong><br>
                    <small style="color: #64748b;">Use your own icon file</small>
                </span>
            </label>
        </div>

        <!-- Generated Favicon Options -->
        <div id="favicon-generate-options" style="<?php echo $use_uploaded === '1' ? 'display: none;' : ''; ?>">

            <!-- Content Type Toggle -->
            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <label class="sb-sp-label" style="margin-bottom: 12px; display: block;">Content Type</label>
                <div class="sb-module-checkbox-grid" style="grid-template-columns: repeat(3, 1fr);">
                    <label class="sb-module-checkbox-item favicon-content-tab <?php echo $content_type === 'letters' ? 'active' : ''; ?>" data-type="letters" style="cursor: pointer; flex-direction: column; text-align: center; padding: 16px 12px;">
                        <span style="font-size: 24px; margin-bottom: 6px;">Aa</span>
                        <span style="font-size: 13px; font-weight: 500;">Letters</span>
                    </label>
                    <label class="sb-module-checkbox-item favicon-content-tab <?php echo $content_type === 'emoji' ? 'active' : ''; ?>" data-type="emoji" style="cursor: pointer; flex-direction: column; text-align: center; padding: 16px 12px;">
                        <span style="font-size: 24px; margin-bottom: 6px;">😀</span>
                        <span style="font-size: 13px; font-weight: 500;">Emoji</span>
                    </label>
                    <label class="sb-module-checkbox-item favicon-content-tab <?php echo $content_type === 'icon' ? 'active' : ''; ?>" data-type="icon" style="cursor: pointer; flex-direction: column; text-align: center; padding: 16px 12px;">
                        <span style="font-size: 24px; margin-bottom: 6px;">⚡</span>
                        <span style="font-size: 13px; font-weight: 500;">Icon</span>
                    </label>
                </div>
                <input type="hidden" name="setting_content_type" id="setting_content_type" value="<?php echo esc_attr($content_type); ?>">
            </div>

            <!-- Letters Content -->
            <div id="favicon-content-letters" class="favicon-content-section" style="<?php echo $content_type !== 'letters' ? 'display: none;' : ''; ?>">
                <div class="sb-sp-group" style="margin-bottom: 24px;">
                    <label for="setting_letters" class="sb-sp-label">Letters (1-2 characters)</label>
                    <input type="text" name="setting_letters" id="setting_letters"
                           value="<?php echo esc_attr($letters); ?>"
                           maxlength="2"
                           placeholder="e.g., AB"
                           class="sb-sp-input" style="width: 100px; margin-top: 8px; font-size: 18px; text-align: center;">
                </div>
            </div>

            <!-- Emoji Content -->
            <div id="favicon-content-emoji" class="favicon-content-section" style="<?php echo $content_type !== 'emoji' ? 'display: none;' : ''; ?>">
                <div class="sb-sp-group" style="margin-bottom: 24px;">
                    <label class="sb-sp-label" style="margin-bottom: 12px; display: block;">Select Emoji</label>
                    <input type="hidden" name="setting_emoji" id="setting_emoji" value="<?php echo esc_attr($emoji); ?>">
                    <div id="emoji-picker" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); gap: 6px; max-height: 220px; overflow-y: auto; padding: 12px; border: 1px solid var(--gray-200); border-radius: 12px; background: var(--gray-50);">
                        <?php foreach ($popular_emojis as $e): ?>
                        <button type="button" class="emoji-option" data-emoji="<?php echo esc_attr($e); ?>"
                                style="font-size: 24px; padding: 8px; border: 2px solid <?php echo $emoji === $e ? 'var(--primary-500)' : 'transparent'; ?>; border-radius: 8px; background: <?php echo $emoji === $e ? 'var(--primary-50)' : 'white'; ?>; cursor: pointer; transition: all 0.15s ease; box-shadow: <?php echo $emoji === $e ? '0 0 0 3px rgba(var(--primary-500-rgb), 0.1)' : '0 1px 2px rgba(0,0,0,0.05)'; ?>;">
                            <?php echo $e; ?>
                        </button>
                        <?php endforeach; ?>
                    </div>
                    <p class="sb-sp-description" style="margin-top: 12px;">Or paste any emoji:</p>
                    <input type="text" id="emoji_custom_input" placeholder="🎯"
                           value="<?php echo esc_attr($emoji); ?>"
                           class="sb-sp-input" style="width: 80px; margin-top: 6px; font-size: 24px; text-align: center; padding: 8px;">
                </div>
            </div>

            <!-- Icon Content -->
            <div id="favicon-content-icon" class="favicon-content-section" style="<?php echo $content_type !== 'icon' ? 'display: none;' : ''; ?>">
                <div class="sb-sp-group" style="margin-bottom: 24px;">
                    <label class="sb-sp-label" style="margin-bottom: 12px; display: block;">Select Icon</label>
                    <input type="hidden" name="setting_icon" id="setting_icon" value="<?php echo esc_attr($icon); ?>">
                    <div id="icon-picker" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); gap: 8px; padding: 12px; border: 1px solid var(--gray-200); border-radius: 12px; background: var(--gray-50);">
                        <?php foreach ($simple_icons as $icon_key => $icon_data): ?>
                        <button type="button" class="icon-option" data-icon="<?php echo esc_attr($icon_key); ?>"
                                title="<?php echo esc_attr($icon_data['name']); ?>"
                                style="padding: 14px; border: 2px solid <?php echo $icon === $icon_key ? 'var(--primary-500)' : 'transparent'; ?>; border-radius: 10px; background: <?php echo $icon === $icon_key ? 'var(--primary-50)' : 'white'; ?>; cursor: pointer; transition: all 0.15s ease; box-shadow: <?php echo $icon === $icon_key ? '0 0 0 3px rgba(var(--primary-500-rgb), 0.1)' : '0 1px 2px rgba(0,0,0,0.05)'; ?>; display: flex; align-items: center; justify-content: center;">
                            <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" style="color: <?php echo $icon === $icon_key ? 'var(--primary-600)' : '#374151'; ?>;">
                                <path d="<?php echo esc_attr($icon_data['path']); ?>"/>
                            </svg>
                        </button>
                        <?php endforeach; ?>
                    </div>
                </div>
            </div>

            <!-- Colors Section -->
            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <label class="sb-sp-label" style="margin-bottom: 12px; display: block;">Colors</label>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                    <!-- Background Color -->
                    <div style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;">
                            <span style="font-weight: 600; color: #374151;">Background</span>
                            <label style="display: flex; align-items: center; gap: 6px; cursor: pointer;">
                                <input type="checkbox" name="setting_transparent_bg" id="setting_transparent_bg" value="1" <?php checked($transparent_bg, '1'); ?> style="width: 16px; height: 16px; accent-color: var(--primary-500);">
                                <span style="font-size: 13px; color: #64748b;">Transparent</span>
                            </label>
                        </div>
                        <div style="display: flex; align-items: center; gap: 12px;">
                            <input type="color" name="setting_bg_color" id="setting_bg_color"
                                   value="<?php echo esc_attr($bg_color); ?>"
                                   style="width: 48px; height: 48px; padding: 4px; border: 1px solid var(--gray-200); border-radius: 8px; cursor: pointer; flex-shrink: 0;"
                                   <?php echo $transparent_bg === '1' ? 'disabled' : ''; ?>>
                            <div style="width: 1px; height: 32px; background: var(--gray-200);"></div>
                            <input type="text" id="setting_bg_color_text"
                                   value="<?php echo esc_attr($bg_color); ?>"
                                   style="flex: 1; padding: 12px; border: 1px solid var(--gray-200); border-radius: 6px; font-family: monospace; font-size: 14px; text-align: center;"
                                   pattern="^#[0-9A-Fa-f]{6}$"
                                   <?php echo $transparent_bg === '1' ? 'disabled' : ''; ?>>
                        </div>
                    </div>
                    <!-- Content Color -->
                    <div style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;">
                            <span style="font-weight: 600; color: #374151;">Content</span>
                            <span style="font-size: 12px; color: #94a3b8;">Letters & icons</span>
                        </div>
                        <div style="display: flex; align-items: center; gap: 12px;">
                            <input type="color" name="setting_text_color" id="setting_text_color"
                                   value="<?php echo esc_attr($text_color); ?>"
                                   style="width: 48px; height: 48px; padding: 4px; border: 1px solid var(--gray-200); border-radius: 8px; cursor: pointer; flex-shrink: 0;">
                            <div style="width: 1px; height: 32px; background: var(--gray-200);"></div>
                            <input type="text" id="setting_text_color_text"
                                   value="<?php echo esc_attr($text_color); ?>"
                                   style="flex: 1; padding: 12px; border: 1px solid var(--gray-200); border-radius: 6px; font-family: monospace; font-size: 14px; text-align: center;"
                                   pattern="^#[0-9A-Fa-f]{6}$">
                        </div>
                    </div>
                </div>
            </div>

            <!-- Typography, Shape & Size - 2 columns -->
            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <label class="sb-sp-label" style="margin-bottom: 12px; display: block;">Typography & Shape</label>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                    <!-- Font Family (for letters) -->
                    <div id="font-family-group" style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <label for="setting_font_family" style="font-size: 12px; font-weight: 600; color: #64748b; display: block; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.5px;">Font Family</label>
                        <select name="setting_font_family" id="setting_font_family" class="sb-sp-select" style="width: 100%; font-size: 14px; padding: 10px 12px;">
                            <option value="system" <?php selected($font_family, 'system'); ?>>System</option>
                            <option value="serif" <?php selected($font_family, 'serif'); ?>>Serif</option>
                            <option value="mono" <?php selected($font_family, 'mono'); ?>>Mono</option>
                        </select>
                    </div>
                    <!-- Font Weight (for letters) -->
                    <div id="font-weight-group" style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <label for="setting_font_weight" style="font-size: 12px; font-weight: 600; color: #64748b; display: block; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.5px;">Font Weight</label>
                        <select name="setting_font_weight" id="setting_font_weight" class="sb-sp-select" style="width: 100%; font-size: 14px; padding: 10px 12px;">
                            <option value="normal" <?php selected($font_weight, 'normal'); ?>>Normal</option>
                            <option value="bold" <?php selected($font_weight, 'bold'); ?>>Bold</option>
                            <option value="900" <?php selected($font_weight, '900'); ?>>Black</option>
                        </select>
                    </div>
                    <!-- Border Radius -->
                    <div style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <label for="setting_border_radius" style="font-size: 12px; font-weight: 600; color: #64748b; display: block; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.5px;">Shape</label>
                        <select name="setting_border_radius" id="setting_border_radius" class="sb-sp-select" style="width: 100%; font-size: 14px; padding: 10px 12px;">
                            <option value="square" <?php selected($border_radius, 'square'); ?>>Square</option>
                            <option value="rounded" <?php selected($border_radius, 'rounded'); ?>>Rounded</option>
                            <option value="circle" <?php selected($border_radius, 'circle'); ?>>Circle</option>
                        </select>
                    </div>
                    <!-- Size Slider -->
                    <div style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <label for="setting_font_size" style="font-size: 12px; font-weight: 600; color: #64748b; display: block; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.5px;">Size</label>
                        <div style="display: flex; align-items: center; gap: 12px;">
                            <input type="range" name="setting_font_size" id="setting_font_size"
                                   value="<?php echo esc_attr($font_size); ?>"
                                   min="24" max="100" step="2"
                                   style="flex: 1; accent-color: var(--primary-500);">
                            <span id="font_size_value" style="min-width: 50px; text-align: center; font-size: 14px; font-weight: 600; color: var(--primary-600); background: var(--primary-50); padding: 6px 12px; border-radius: 6px;"><?php echo esc_html($font_size); ?>%</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Upload Option -->
        <div id="favicon-upload-options" style="<?php echo $use_uploaded !== '1' ? 'display: none;' : ''; ?>">
            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <input type="hidden" name="setting_uploaded_icon_id" id="setting_uploaded_icon_id" value="<?php echo esc_attr($uploaded_icon_id); ?>">
                <input type="hidden" name="setting_uploaded_icon_url" id="setting_uploaded_icon_url" value="<?php echo esc_attr($uploaded_icon_url); ?>">

                <div style="padding: 24px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 12px;">
                    <div style="display: flex; align-items: center; justify-content: center; gap: 20px;">
                        <!-- Small preview thumbnail -->
                        <div id="upload-thumbnail-wrapper" style="<?php echo !$uploaded_icon_url ? 'display: none;' : ''; ?>">
                            <div style="width: 64px; height: 64px; background: white; border: 1px solid var(--gray-200); border-radius: 8px; padding: 6px; display: flex; align-items: center; justify-content: center;">
                                <img id="upload-thumbnail-img" src="<?php echo esc_url($uploaded_icon_url); ?>" style="max-width: 100%; max-height: 100%; object-fit: contain;">
                            </div>
                        </div>
                        <!-- Buttons -->
                        <div style="display: flex; flex-direction: column; align-items: center; gap: 10px;">
                            <div style="display: flex; gap: 12px;">
                                <button type="button" id="upload-favicon-btn" class="button button-primary" style="padding: 10px 24px; font-size: 14px;">
                                    <?php echo $uploaded_icon_url ? 'Change Icon' : 'Choose File'; ?>
                                </button>
                                <button type="button" id="remove-favicon-btn" class="button" style="padding: 10px 24px; font-size: 14px; color: #dc2626; <?php echo !$uploaded_icon_url ? 'display: none;' : ''; ?>">Remove</button>
                            </div>
                            <p style="font-size: 12px; color: #94a3b8; margin: 0;">PNG, JPG or SVG (any size, will be resized)</p>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Background & Shape for uploaded image -->
            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <label class="sb-sp-label" style="margin-bottom: 12px; display: block;">Background & Shape</label>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                    <!-- Background Color for Upload -->
                    <?php
                    $upload_bg_color = isset($settings['upload_bg_color']) ? $settings['upload_bg_color'] : '#ffffff';
                    $upload_transparent_bg = isset($settings['upload_transparent_bg']) ? $settings['upload_transparent_bg'] : '1';
                    ?>
                    <div style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;">
                            <span style="font-weight: 600; color: #374151;">Background</span>
                            <label style="display: flex; align-items: center; gap: 6px; cursor: pointer;">
                                <input type="checkbox" name="setting_upload_transparent_bg" id="setting_upload_transparent_bg" value="1" <?php checked($upload_transparent_bg, '1'); ?> style="width: 16px; height: 16px; accent-color: var(--primary-500);">
                                <span style="font-size: 13px; color: #64748b;">Transparent</span>
                            </label>
                        </div>
                        <div style="display: flex; align-items: center; gap: 12px;">
                            <input type="color" name="setting_upload_bg_color" id="setting_upload_bg_color"
                                   value="<?php echo esc_attr($upload_bg_color); ?>"
                                   style="width: 48px; height: 48px; padding: 4px; border: 1px solid var(--gray-200); border-radius: 8px; cursor: pointer; flex-shrink: 0;"
                                   <?php echo $upload_transparent_bg === '1' ? 'disabled' : ''; ?>>
                            <div style="width: 1px; height: 32px; background: var(--gray-200);"></div>
                            <input type="text" id="setting_upload_bg_color_text"
                                   value="<?php echo esc_attr($upload_bg_color); ?>"
                                   style="flex: 1; padding: 12px; border: 1px solid var(--gray-200); border-radius: 6px; font-family: monospace; font-size: 14px; text-align: center;"
                                   pattern="^#[0-9A-Fa-f]{6}$"
                                   <?php echo $upload_transparent_bg === '1' ? 'disabled' : ''; ?>>
                        </div>
                        <p style="font-size: 11px; color: #94a3b8; margin-top: 10px;">Fill behind transparent images</p>
                    </div>
                    <!-- Shape for Upload -->
                    <?php
                    $upload_radius = isset($settings['upload_border_radius']) ? $settings['upload_border_radius'] : 'square';
                    ?>
                    <div style="padding: 16px; background: var(--gray-50); border: 1px solid var(--gray-200); border-radius: 10px;">
                        <span style="font-weight: 600; color: #374151; display: block; margin-bottom: 12px;">Shape</span>
                        <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;">
                            <label class="upload-shape-option" data-shape="square" style="cursor: pointer; display: flex; flex-direction: column; align-items: center; padding: 10px 6px; border: 2px solid <?php echo $upload_radius === 'square' ? 'var(--primary-500)' : 'transparent'; ?>; border-radius: 8px; background: <?php echo $upload_radius === 'square' ? 'var(--primary-50)' : 'white'; ?>;">
                                <div style="width: 24px; height: 24px; background: var(--primary-500); margin-bottom: 4px;"></div>
                                <span style="font-size: 11px; font-weight: 500;">Square</span>
                            </label>
                            <label class="upload-shape-option" data-shape="rounded" style="cursor: pointer; display: flex; flex-direction: column; align-items: center; padding: 10px 6px; border: 2px solid <?php echo $upload_radius === 'rounded' ? 'var(--primary-500)' : 'transparent'; ?>; border-radius: 8px; background: <?php echo $upload_radius === 'rounded' ? 'var(--primary-50)' : 'white'; ?>;">
                                <div style="width: 24px; height: 24px; background: var(--primary-500); border-radius: 4px; margin-bottom: 4px;"></div>
                                <span style="font-size: 11px; font-weight: 500;">Rounded</span>
                            </label>
                            <label class="upload-shape-option" data-shape="circle" style="cursor: pointer; display: flex; flex-direction: column; align-items: center; padding: 10px 6px; border: 2px solid <?php echo $upload_radius === 'circle' ? 'var(--primary-500)' : 'transparent'; ?>; border-radius: 8px; background: <?php echo $upload_radius === 'circle' ? 'var(--primary-50)' : 'white'; ?>;">
                                <div style="width: 24px; height: 24px; background: var(--primary-500); border-radius: 50%; margin-bottom: 4px;"></div>
                                <span style="font-size: 11px; font-weight: 500;">Circle</span>
                            </label>
                        </div>
                        <input type="hidden" name="setting_upload_border_radius" id="setting_upload_border_radius" value="<?php echo esc_attr($upload_radius); ?>">
                    </div>
                </div>
            </div>
        </div>

        <!-- Hidden field to store generated favicon data -->
        <input type="hidden" name="setting_generated_favicon" id="setting_generated_favicon" value="<?php echo esc_attr($generated_favicon); ?>">

        <!-- Icon paths data for JS -->
        <script type="application/json" id="icon-paths-data">
        <?php echo json_encode($simple_icons); ?>
        </script>

        <script>
        (function() {
            // Elements
            const bgColor = document.getElementById('setting_bg_color');
            const bgColorText = document.getElementById('setting_bg_color_text');
            const textColor = document.getElementById('setting_text_color');
            const textColorText = document.getElementById('setting_text_color_text');
            const transparentBg = document.getElementById('setting_transparent_bg');
            const letters = document.getElementById('setting_letters');
            const fontSize = document.getElementById('setting_font_size');
            const fontSizeValue = document.getElementById('font_size_value');
            const fontFamily = document.getElementById('setting_font_family');
            const fontWeight = document.getElementById('setting_font_weight');
            const borderRadius = document.getElementById('setting_border_radius');
            const contentType = document.getElementById('setting_content_type');
            const emojiInput = document.getElementById('setting_emoji');
            const iconInput = document.getElementById('setting_icon');
            const canvas = document.getElementById('favicon-preview');
            const canvas32 = document.getElementById('favicon-preview-32');
            const canvas16 = document.getElementById('favicon-preview-16');
            const generatedFaviconInput = document.getElementById('setting_generated_favicon');

            // Icon paths
            const iconPaths = JSON.parse(document.getElementById('icon-paths-data').textContent);

            // Guard against missing elements
            if (!bgColor || !canvas) {
                return;
            }

            // Font family mappings
            const fontFamilies = {
                'system': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
                'serif': 'Georgia, "Times New Roman", serif',
                'mono': '"SF Mono", Monaco, "Courier New", monospace'
            };

            // Mode selection (generate vs upload)
            document.querySelectorAll('.sb-module-checkbox-item[data-mode]').forEach(function(item) {
                item.addEventListener('click', function() {
                    const mode = this.dataset.mode;
                    const radio = this.querySelector('input[type="radio"]');
                    radio.checked = true;

                    // Update visual states
                    document.querySelectorAll('.sb-module-checkbox-item[data-mode]').forEach(i => i.classList.remove('active'));
                    this.classList.add('active');

                    // Show/hide sections
                    const generateOptions = document.getElementById('favicon-generate-options');
                    const uploadOptions = document.getElementById('favicon-upload-options');
                    const generatedPreview = document.getElementById('generated-preview-wrapper');
                    const uploadedPreview = document.getElementById('uploaded-preview-wrapper');
                    if (mode === 'upload') {
                        generateOptions.style.display = 'none';
                        uploadOptions.style.display = '';
                        generatedPreview.style.display = 'none';
                        uploadedPreview.style.display = '';
                    } else {
                        generateOptions.style.display = '';
                        uploadOptions.style.display = 'none';
                        generatedPreview.style.display = 'flex';
                        uploadedPreview.style.display = 'none';
                    }
                });
            });

            // Content type toggle
            document.querySelectorAll('.favicon-content-tab').forEach(function(item) {
                item.addEventListener('click', function() {
                    const type = this.dataset.type;
                    contentType.value = type;

                    // Update active states
                    document.querySelectorAll('.favicon-content-tab').forEach(t => t.classList.remove('active'));
                    this.classList.add('active');

                    // Show/hide sections
                    document.querySelectorAll('.favicon-content-section').forEach(s => s.style.display = 'none');
                    document.getElementById('favicon-content-' + type).style.display = '';

                    // Show/hide font options for letters only
                    const fontFamilyGroup = document.getElementById('font-family-group');
                    const fontWeightGroup = document.getElementById('font-weight-group');
                    if (type === 'letters') {
                        fontFamilyGroup.style.display = '';
                        fontWeightGroup.style.display = '';
                    } else {
                        fontFamilyGroup.style.display = 'none';
                        fontWeightGroup.style.display = 'none';
                    }

                    updatePreview();
                });
            });

            // Emoji picker
            document.querySelectorAll('.emoji-option').forEach(function(btn) {
                btn.addEventListener('click', function() {
                    const emoji = this.dataset.emoji;
                    emojiInput.value = emoji;
                    document.getElementById('emoji_custom_input').value = emoji;

                    // Update selection styles
                    document.querySelectorAll('.emoji-option').forEach(b => {
                        b.style.borderColor = 'transparent';
                        b.style.background = 'white';
                        b.style.boxShadow = '0 1px 2px rgba(0,0,0,0.05)';
                    });
                    this.style.borderColor = 'var(--primary-500)';
                    this.style.background = 'var(--primary-50)';
                    this.style.boxShadow = '0 0 0 3px rgba(var(--primary-500-rgb), 0.1)';

                    updatePreview();
                });
            });

            // Custom emoji input
            document.getElementById('emoji_custom_input').addEventListener('input', function() {
                emojiInput.value = this.value;
                // Deselect all emoji buttons
                document.querySelectorAll('.emoji-option').forEach(b => {
                    b.style.borderColor = 'transparent';
                    b.style.background = 'white';
                    b.style.boxShadow = '0 1px 2px rgba(0,0,0,0.05)';
                });
                updatePreview();
            });

            // Icon picker
            document.querySelectorAll('.icon-option').forEach(function(btn) {
                btn.addEventListener('click', function() {
                    const icon = this.dataset.icon;
                    iconInput.value = icon;

                    // Update selection styles
                    document.querySelectorAll('.icon-option').forEach(b => {
                        b.style.borderColor = 'transparent';
                        b.style.background = 'white';
                        b.style.boxShadow = '0 1px 2px rgba(0,0,0,0.05)';
                        b.querySelector('svg').style.color = '#374151';
                    });
                    this.style.borderColor = 'var(--primary-500)';
                    this.style.background = 'var(--primary-50)';
                    this.style.boxShadow = '0 0 0 3px rgba(var(--primary-500-rgb), 0.1)';
                    this.querySelector('svg').style.color = 'var(--primary-600)';

                    updatePreview();
                });
            });

            // Transparent background toggle
            transparentBg.addEventListener('change', function() {
                bgColor.disabled = this.checked;
                bgColorText.disabled = this.checked;
                updatePreview();
            });

            // Color inputs sync
            bgColor.addEventListener('input', function() {
                bgColorText.value = this.value;
                updatePreview();
            });
            bgColorText.addEventListener('input', function() {
                if (/^#[0-9A-Fa-f]{6}$/.test(this.value)) {
                    bgColor.value = this.value;
                    updatePreview();
                }
            });
            textColor.addEventListener('input', function() {
                textColorText.value = this.value;
                updatePreview();
            });
            textColorText.addEventListener('input', function() {
                if (/^#[0-9A-Fa-f]{6}$/.test(this.value)) {
                    textColor.value = this.value;
                    updatePreview();
                }
            });

            // Other inputs
            fontSize.addEventListener('input', function() {
                fontSizeValue.textContent = this.value + '%';
                updatePreview();
            });
            fontFamily.addEventListener('change', updatePreview);
            fontWeight.addEventListener('change', updatePreview);
            borderRadius.addEventListener('change', updatePreview);
            letters.addEventListener('input', function() {
                this.value = this.value.toUpperCase().substring(0, 2);
                updatePreview();
            });


            // Media uploader
            const uploadBtn = document.getElementById('upload-favicon-btn');
            if (uploadBtn) {
                uploadBtn.addEventListener('click', function(e) {
                    e.preventDefault();

                    const frame = wp.media({
                        title: 'Select Favicon Icon',
                        button: { text: 'Use as Favicon' },
                        multiple: false,
                        library: { type: 'image' }
                    });

                    frame.on('select', function() {
                        const attachment = frame.state().get('selection').first().toJSON();
                        document.getElementById('setting_uploaded_icon_id').value = attachment.id;
                        document.getElementById('setting_uploaded_icon_url').value = attachment.url;

                        // Update top preview
                        const topPreviewImg = document.getElementById('uploaded-preview-img');
                        if (topPreviewImg) {
                            topPreviewImg.src = attachment.url;
                            topPreviewImg.style.opacity = '1';
                            topPreviewImg.parentElement.nextElementSibling.textContent = 'Uploaded';
                        }

                        // Update thumbnail preview
                        const thumbnailWrapper = document.getElementById('upload-thumbnail-wrapper');
                        const thumbnailImg = document.getElementById('upload-thumbnail-img');
                        if (thumbnailWrapper && thumbnailImg) {
                            thumbnailWrapper.style.display = '';
                            thumbnailImg.src = attachment.url;
                        }

                        uploadBtn.textContent = 'Change Icon';

                        // Show remove button
                        const removeBtn = document.getElementById('remove-favicon-btn');
                        if (removeBtn) {
                            removeBtn.style.display = '';
                        }
                    });

                    frame.open();
                });
            }

            function attachRemoveHandler(btn) {
                btn.addEventListener('click', function(e) {
                    e.preventDefault();
                    document.getElementById('setting_uploaded_icon_id').value = '';
                    document.getElementById('setting_uploaded_icon_url').value = '';
                    document.getElementById('upload-favicon-btn').textContent = 'Choose File';

                    // Reset top preview
                    const topPreviewImg = document.getElementById('uploaded-preview-img');
                    if (topPreviewImg) {
                        topPreviewImg.src = '';
                        topPreviewImg.style.opacity = '0.3';
                        topPreviewImg.parentElement.nextElementSibling.textContent = 'No image';
                    }

                    // Hide thumbnail
                    const thumbnailWrapper = document.getElementById('upload-thumbnail-wrapper');
                    if (thumbnailWrapper) {
                        thumbnailWrapper.style.display = 'none';
                    }

                    // Hide remove button
                    this.style.display = 'none';
                });
            }

            const removeBtn = document.getElementById('remove-favicon-btn');
            if (removeBtn) {
                attachRemoveHandler(removeBtn);
            }

            // Upload background color toggle
            const uploadTransparentBg = document.getElementById('setting_upload_transparent_bg');
            const uploadBgColor = document.getElementById('setting_upload_bg_color');
            const uploadBgColorText = document.getElementById('setting_upload_bg_color_text');

            if (uploadTransparentBg) {
                uploadTransparentBg.addEventListener('change', function() {
                    uploadBgColor.disabled = this.checked;
                    uploadBgColorText.disabled = this.checked;
                    updateUploadedPreviewBg();
                });
            }

            // Upload bg color sync
            if (uploadBgColor) {
                uploadBgColor.addEventListener('input', function() {
                    uploadBgColorText.value = this.value;
                    updateUploadedPreviewBg();
                });
            }
            if (uploadBgColorText) {
                uploadBgColorText.addEventListener('input', function() {
                    if (/^#[0-9A-Fa-f]{6}$/.test(this.value)) {
                        uploadBgColor.value = this.value;
                        updateUploadedPreviewBg();
                    }
                });
            }

            function updateUploadedPreviewBg() {
                const previewWrapper = document.getElementById('uploaded-preview-wrapper');
                if (previewWrapper) {
                    const isTransparent = uploadTransparentBg && uploadTransparentBg.checked;
                    const bgColorVal = uploadBgColor ? uploadBgColor.value : '#ffffff';
                    // Update the preview container background
                    const previewBox = previewWrapper.querySelector('div > div');
                    if (previewBox) {
                        previewBox.style.background = isTransparent ? 'white' : bgColorVal;
                    }
                }
            }

            // Upload shape selection
            document.querySelectorAll('.upload-shape-option').forEach(function(item) {
                item.addEventListener('click', function() {
                    const shape = this.dataset.shape;
                    document.getElementById('setting_upload_border_radius').value = shape;

                    // Update active states
                    document.querySelectorAll('.upload-shape-option').forEach(s => {
                        s.style.borderColor = 'transparent';
                        s.style.background = 'white';
                    });
                    this.style.borderColor = 'var(--primary-500)';
                    this.style.background = 'var(--primary-50)';

                    // Update preview with shape
                    updateUploadedPreviewShape(shape);
                });
            });

            function updateUploadedPreviewShape(shape) {
                const previewImg = document.getElementById('uploaded-preview-img');
                if (previewImg) {
                    let radius = '0';
                    if (shape === 'rounded') radius = '15%';
                    else if (shape === 'circle') radius = '50%';
                    previewImg.style.borderRadius = radius;
                }
            }

            // Apply initial shape and bg to uploaded preview
            const initialUploadShape = document.getElementById('setting_upload_border_radius');
            if (initialUploadShape) {
                updateUploadedPreviewShape(initialUploadShape.value);
            }
            updateUploadedPreviewBg();

            function updatePreview() {
                const bg = transparentBg.checked ? 'transparent' : bgColor.value;
                const text = textColor.value;
                const size = parseInt(fontSize.value);
                const radius = borderRadius.value;
                const type = contentType.value;
                const font = fontFamilies[fontFamily.value] || fontFamilies.system;
                const weight = fontWeight.value;

                let content = '';
                if (type === 'letters') {
                    content = letters.value || '';
                } else if (type === 'emoji') {
                    content = emojiInput.value || '';
                } else if (type === 'icon') {
                    content = iconInput.value || '';
                }

                // Draw on all canvases
                drawFavicon(canvas, 128, bg, text, content, size, radius, type, font, weight);
                drawFavicon(canvas32, 32, bg, text, content, size, radius, type, font, weight);
                drawFavicon(canvas16, 16, bg, text, content, size, radius, type, font, weight);

                // Store base64 data
                generatedFaviconInput.value = canvas.toDataURL('image/png');
            }

            function drawFavicon(canvasEl, canvasSize, bg, textColor, content, sizePercent, radius, type, font, weight) {
                const ctx = canvasEl.getContext('2d');

                // Clear canvas
                ctx.clearRect(0, 0, canvasSize, canvasSize);

                // Calculate radius
                let cornerRadius = 0;
                if (radius === 'rounded') {
                    cornerRadius = canvasSize * 0.15;
                } else if (radius === 'circle') {
                    cornerRadius = canvasSize / 2;
                }

                // Draw background
                if (bg !== 'transparent') {
                    ctx.fillStyle = bg;
                    ctx.beginPath();
                    if (cornerRadius > 0) {
                        ctx.roundRect(0, 0, canvasSize, canvasSize, cornerRadius);
                    } else {
                        ctx.rect(0, 0, canvasSize, canvasSize);
                    }
                    ctx.fill();
                }

                // Calculate content size
                const actualSize = (canvasSize * sizePercent) / 100;

                if (!content) return;

                if (type === 'icon' && iconPaths[content]) {
                    // Draw SVG icon
                    const path = new Path2D(iconPaths[content].path);
                    ctx.save();

                    // Scale and center the icon (icons are 24x24 viewBox)
                    const scale = actualSize / 24;
                    const offset = (canvasSize - actualSize) / 2;

                    ctx.translate(offset, offset);
                    ctx.scale(scale, scale);
                    ctx.fillStyle = textColor;
                    ctx.fill(path);
                    ctx.restore();
                } else {
                    // Draw text (letters or emoji)
                    ctx.fillStyle = textColor;
                    const fontStyle = type === 'letters' ? (weight + ' ' + actualSize + 'px ' + font) : (actualSize + 'px sans-serif');
                    ctx.font = fontStyle;
                    ctx.textAlign = 'center';
                    ctx.textBaseline = 'middle';

                    // Slight vertical adjustment
                    const yOffset = type === 'emoji' ? 0 : actualSize * 0.05;
                    ctx.fillText(content, canvasSize / 2, canvasSize / 2 + yOffset);
                }
            }

            // Initial preview
            updatePreview();

            // Hide font options initially if not letters
            if (contentType.value !== 'letters') {
                document.getElementById('font-family-group').style.display = 'none';
                document.getElementById('font-weight-group').style.display = 'none';
            }
        })();
        </script>

        <div class="sb-sp-info-box sb-sp-info-box-tip" style="margin-top: 24px;">
            <p><strong>Tip:</strong> After saving, your favicon will be automatically applied to your site. Clear your browser cache if you don't see the changes immediately.</p>
        </div>
        <?php
    }

    /**
     * Output favicon link tags in the head
     */
    public function output_favicon() {
        $settings = get_option('switchboard_favicon-generator_settings', []);

        if (empty($settings)) {
            return;
        }

        $use_uploaded = isset($settings['use_uploaded']) && $settings['use_uploaded'] === '1';

        if ($use_uploaded) {
            // Use uploaded icon - process with shape/bg if needed
            $icon_url = isset($settings['uploaded_icon_url']) ? $settings['uploaded_icon_url'] : '';
            $upload_radius = isset($settings['upload_border_radius']) ? $settings['upload_border_radius'] : 'square';
            $upload_transparent_bg = isset($settings['upload_transparent_bg']) ? $settings['upload_transparent_bg'] : '1';
            $upload_bg_color = isset($settings['upload_bg_color']) ? $settings['upload_bg_color'] : '#ffffff';

            if (!empty($icon_url)) {
                // Process if shape is applied OR background color is set
                $needs_processing = ($upload_radius !== 'square') || ($upload_transparent_bg !== '1');
                if ($needs_processing && function_exists('imagecreatetruecolor')) {
                    $processed_url = $this->get_processed_uploaded_favicon($icon_url, $upload_radius, $settings);
                    if ($processed_url) {
                        $this->output_favicon_tags($processed_url);
                        return;
                    }
                }
                // Fallback to original image
                $this->output_favicon_tags($icon_url);
            }
        } else {
            // Initialize directories for generated favicon
            $this->init_dirs();

            $favicon_path = $this->upload_dir . '/favicon.png';
            $favicon_url = $this->upload_url . '/favicon.png';

            // Check if we need to regenerate
            $generated_data = isset($settings['generated_favicon']) ? $settings['generated_favicon'] : '';

            if (!empty($generated_data) && strpos($generated_data, 'data:image/png;base64,') === 0) {
                // Decode and save the favicon if settings changed
                $hash = md5($generated_data);
                $hash_file = $this->upload_dir . '/favicon.hash';
                $current_hash = file_exists($hash_file) ? file_get_contents($hash_file) : '';

                if ($hash !== $current_hash || !file_exists($favicon_path)) {
                    $this->ensure_upload_dir();
                    if ($this->save_favicon_from_base64($generated_data, $favicon_path)) {
                        file_put_contents($hash_file, $hash);
                    }
                }
            }

            if (file_exists($favicon_path)) {
                $this->output_favicon_tags($favicon_url . '?v=' . filemtime($favicon_path));
            }
        }
    }

    /**
     * Output the actual favicon link tags
     */
    private function output_favicon_tags($icon_url) {
        echo '<link rel="icon" type="image/png" sizes="32x32" href="' . esc_url($icon_url) . '">' . "\n";
        echo '<link rel="icon" type="image/png" sizes="16x16" href="' . esc_url($icon_url) . '">' . "\n";
        echo '<link rel="apple-touch-icon" sizes="180x180" href="' . esc_url($icon_url) . '">' . "\n";
        echo '<link rel="shortcut icon" href="' . esc_url($icon_url) . '">' . "\n";
    }

    /**
     * Filter the site icon URL
     */
    public function filter_site_icon($url, $size, $blog_id) {
        $settings = get_option('switchboard_favicon-generator_settings', []);

        if (empty($settings)) {
            return $url;
        }

        $use_uploaded = isset($settings['use_uploaded']) && $settings['use_uploaded'] === '1';

        if ($use_uploaded) {
            $icon_url = isset($settings['uploaded_icon_url']) ? $settings['uploaded_icon_url'] : '';
            if (!empty($icon_url)) {
                return $icon_url;
            }
        } else {
            $this->init_dirs();
            $favicon_path = $this->upload_dir . '/favicon.png';
            if (file_exists($favicon_path)) {
                return $this->upload_url . '/favicon.png?v=' . filemtime($favicon_path);
            }
        }

        return $url;
    }

    /**
     * Process uploaded image with border radius and background color
     */
    private function get_processed_uploaded_favicon($icon_url, $radius, $settings) {
        $this->init_dirs();
        $this->ensure_upload_dir();

        // Get background settings
        $transparent_bg = isset($settings['upload_transparent_bg']) ? $settings['upload_transparent_bg'] : '1';
        $bg_color = isset($settings['upload_bg_color']) ? $settings['upload_bg_color'] : '#ffffff';

        // Create hash based on all settings
        $hash = md5($icon_url . $radius . $transparent_bg . $bg_color);
        $processed_path = $this->upload_dir . '/uploaded-' . $hash . '.png';
        $processed_url = $this->upload_url . '/uploaded-' . $hash . '.png';

        // Return cached version if exists
        if (file_exists($processed_path)) {
            return $processed_url . '?v=' . filemtime($processed_path);
        }

        // Get the uploaded image
        $upload_id = isset($settings['uploaded_icon_id']) ? intval($settings['uploaded_icon_id']) : 0;
        if (!$upload_id) {
            return false;
        }

        $file_path = get_attached_file($upload_id);
        if (!$file_path || !file_exists($file_path)) {
            return false;
        }

        // Load source image
        $mime = wp_check_filetype($file_path)['type'];
        $source = null;
        $size = 512; // Target size for favicon

        // Handle SVG files
        if ($mime === 'image/svg+xml') {
            // Sanitize SVG before processing to prevent XSS/XXE attacks
            $sanitized_svg = $this->sanitize_svg($file_path);
            if ($sanitized_svg === false) {
                return false;
            }

            // Create a temporary sanitized SVG file
            $temp_svg = $this->upload_dir . '/temp-sanitized-' . md5($file_path) . '.svg';
            file_put_contents($temp_svg, '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $sanitized_svg);

            // Try Imagick for SVG rendering (if available)
            if (class_exists('Imagick')) {
                try {
                    $imagick = new \Imagick();
                    $imagick->setBackgroundColor(new \ImagickPixel('transparent'));
                    $imagick->readImage($temp_svg);
                    $imagick->setImageFormat('png');
                    $imagick->resizeImage($size, $size, \Imagick::FILTER_LANCZOS, 1);

                    // Convert Imagick to GD
                    $temp_png = $this->upload_dir . '/temp-svg-' . md5($file_path) . '.png';
                    $imagick->writeImage($temp_png);
                    $imagick->destroy();

                    $source = @imagecreatefrompng($temp_png);
                    @unlink($temp_png);
                    @unlink($temp_svg);

                    if ($source) {
                        imagealphablending($source, false);
                        imagesavealpha($source, true);
                    }
                } catch (\Exception $ex) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
                    // Imagick failed, SVG can't be processed
                    unset($ex);
                    @unlink($temp_svg);
                    return false;
                }
            } else {
                // No Imagick - save sanitized SVG and return false to use it directly
                // Overwrite original with sanitized version for safety
                file_put_contents($file_path, '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $sanitized_svg);
                @unlink($temp_svg);
                return false;
            }
        } elseif ($mime === 'image/png') {
            $source = @imagecreatefrompng($file_path);
            if ($source) {
                // Preserve PNG alpha channel
                imagealphablending($source, false);
                imagesavealpha($source, true);
            }
        } elseif ($mime === 'image/jpeg' || $mime === 'image/jpg') {
            $source = @imagecreatefromjpeg($file_path);
        } elseif ($mime === 'image/gif') {
            $source = @imagecreatefromgif($file_path);
        } elseif ($mime === 'image/webp' && function_exists('imagecreatefromwebp')) {
            $source = @imagecreatefromwebp($file_path);
            if ($source) {
                imagealphablending($source, false);
                imagesavealpha($source, true);
            }
        }

        if (!$source) {
            return false;
        }

        // Get source dimensions
        $src_w = imagesx($source);
        $src_h = imagesy($source);

        // Create destination
        $dest = imagecreatetruecolor($size, $size);
        imagealphablending($dest, false);
        imagesavealpha($dest, true);

        // Fill with background color or transparency
        if ($transparent_bg === '1') {
            // Fill with full transparency
            $bg = imagecolorallocatealpha($dest, 0, 0, 0, 127);
            imagefilledrectangle($dest, 0, 0, $size - 1, $size - 1, $bg);
        } else {
            // Fill with solid background color
            $bg_rgb = $this->hex_to_rgb($bg_color);
            $bg = imagecolorallocate($dest, $bg_rgb[0], $bg_rgb[1], $bg_rgb[2]);
            imagefilledrectangle($dest, 0, 0, $size - 1, $size - 1, $bg);
        }

        // Calculate crop dimensions (center crop to square)
        $crop_size = min($src_w, $src_h);
        $src_x = (int)(($src_w - $crop_size) / 2);
        $src_y = (int)(($src_h - $crop_size) / 2);

        // Enable alpha blending for compositing source onto dest
        imagealphablending($dest, true);

        // Resize and center crop
        imagecopyresampled($dest, $source, 0, 0, $src_x, $src_y, $size, $size, $crop_size, $crop_size);
        imagedestroy($source);

        // Disable blending for further operations
        imagealphablending($dest, false);

        // Apply border radius mask
        $corner_radius = 0;
        if ($radius === 'rounded') {
            $corner_radius = (int)($size * 0.15);
        } elseif ($radius === 'circle') {
            $corner_radius = (int)($size / 2);
        }

        if ($corner_radius > 0) {
            $this->apply_rounded_corners($dest, $size, $corner_radius);
        }

        // Ensure alpha is saved
        imagesavealpha($dest, true);

        // Save
        imagepng($dest, $processed_path);
        imagedestroy($dest);

        return $processed_url . '?v=' . time();
    }

    /**
     * Apply rounded corners to an image
     */
    private function apply_rounded_corners(&$img, $size, $radius) {
        $transparent = imagecolorallocatealpha($img, 0, 0, 0, 127);

        // Process each corner
        for ($x = 0; $x < $radius; $x++) {
            for ($y = 0; $y < $radius; $y++) {
                $dist = sqrt(pow($radius - $x, 2) + pow($radius - $y, 2));
                if ($dist > $radius) {
                    // Top-left
                    imagesetpixel($img, $x, $y, $transparent);
                    // Top-right
                    imagesetpixel($img, $size - 1 - $x, $y, $transparent);
                    // Bottom-left
                    imagesetpixel($img, $x, $size - 1 - $y, $transparent);
                    // Bottom-right
                    imagesetpixel($img, $size - 1 - $x, $size - 1 - $y, $transparent);
                }
            }
        }
    }

    /**
     * Save favicon from base64 data
     */
    private function save_favicon_from_base64($base64_data, $path) {
        // Remove the data URL prefix
        $base64_data = str_replace('data:image/png;base64,', '', $base64_data);
        $image_data = base64_decode($base64_data);

        if ($image_data) {
            file_put_contents($path, $image_data);
            return true;
        }

        return false;
    }

    /**
     * AJAX handler for generating favicon (optional server-side generation)
     */
    public function ajax_generate_favicon() {
        check_ajax_referer('switchboard_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Unauthorized');
        }

        $bg_color = isset($_POST['bg_color']) ? sanitize_hex_color($_POST['bg_color']) : '#3b82f6';
        $text_color = isset($_POST['text_color']) ? sanitize_hex_color($_POST['text_color']) : '#ffffff';
        $letters = isset($_POST['letters']) ? sanitize_text_field($_POST['letters']) : '';

        // Server-side generation using GD library (fallback)
        if (function_exists('imagecreatetruecolor')) {
            $this->ensure_upload_dir();

            $size = 128;
            $image = imagecreatetruecolor($size, $size);

            // Parse colors
            $bg_rgb = $this->hex_to_rgb($bg_color);
            $text_rgb = $this->hex_to_rgb($text_color);

            $bg = imagecolorallocate($image, $bg_rgb[0], $bg_rgb[1], $bg_rgb[2]);
            $text = imagecolorallocate($image, $text_rgb[0], $text_rgb[1], $text_rgb[2]);

            imagefill($image, 0, 0, $bg);

            if (!empty($letters)) {
                // Use built-in font (5 is the largest)
                $font = 5;
                $text_width = imagefontwidth($font) * strlen($letters);
                $text_height = imagefontheight($font);
                $x = (int) (($size - $text_width) / 2);
                $y = (int) (($size - $text_height) / 2);
                imagestring($image, $font, $x, $y, strtoupper($letters), $text);
            }

            // Save to file
            $path = $this->upload_dir . '/favicon.png';
            imagepng($image, $path);

            // Clean up (unset for PHP 8+ where imagedestroy is deprecated)
            unset($image);

            wp_send_json_success([
                'url' => $this->upload_url . '/favicon.png?v=' . time()
            ]);
        }

        wp_send_json_error('GD library not available');
    }

    /**
     * Convert hex color to RGB array
     */
    private function hex_to_rgb($hex) {
        $hex = ltrim($hex, '#');
        return [
            hexdec(substr($hex, 0, 2)),
            hexdec(substr($hex, 2, 2)),
            hexdec(substr($hex, 4, 2))
        ];
    }
}

$switchboard_instance = new SwitchboardFaviconGenerator();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
