<?php
/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 * 
 * Copyright (c) 2025 CastConductor.com. All Rights Reserved.
 * 
 * This file is part of Cast Conductor ("Software"). The Software and its source
 * code constitute proprietary, confidential, and trade secret information of
 * CastConductor.com ("Company"). Any access or use is governed strictly by the
 * Cast Conductor Proprietary License v5 ("License"). By installing, copying,
 * accessing, compiling, or otherwise using the Software you agree to be bound by
 * all terms of the License. If you do not agree, you must cease use immediately.
 * 
 * Key Terms (Summary – see full License for binding terms):
 *  1. No Redistribution: You may not publish, distribute, sublicense, rent,
 *     lease, transfer, sell, or otherwise make the Software (or any derivative)
 *     available to any third party without prior written consent of Company.
 *  2. No Modification: Modification, reverse engineering, decompilation, or
 *     disassembly is prohibited except to the limited extent expressly permitted
 *     by applicable law that cannot be contractually waived.
 *  3. Confidentiality: Treat all source code and related artifacts as Company
 *     Confidential Information. Maintain at least the same degree of care as for
 *     your own confidential materials, and not less than reasonable care.
 *  4. No Patent License: No express or implied patent rights are granted. Future
 *     patents (if any) are fully reserved.
 *  5. No Trademark License: Company names, marks, and logos may not be used
 *     without prior written permission.
 *  6. Limited Internal Use: Use is limited solely to internal evaluation and
 *     operation of licensed Cast Conductor deployments. Commercial hosting or
 *     resale as a service requires a separate written agreement.
 *  7. Telemetry & License Validation: The Software may periodically transmit a
 *     hashed installation identifier, domain (or site ID), plugin/app version,
 *     and a truncated (non-reversible) fragment of the license key solely to
 *     validate activation status and enforce licensing. This minimal "phone home"
 *     check contains no personal or content data. If optional telemetry is later
 *     introduced it will be limited to aggregate operational metrics (no PII),
 *     fully documented, and optionally disableable per published instructions.
 *  8. Third-Party Components: The Software may include open source components
 *     covered by their own licenses. See THIRD-PARTY-NOTICES.md. Those licenses
 *     govern their respective components; this License governs all remaining code.
 *  9. Export Compliance: You are responsible for compliance with all applicable
 *     export control and sanctions laws.
 * 10. Warranty Disclaimer: THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 *     ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
 * 11. Limitation of Liability: IN NO EVENT WILL COMPANY OR AUTHORS BE LIABLE FOR
 *     ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE
 *     DAMAGES, OR LOST PROFITS, EVEN IF ADVISED OF THE POSSIBILITY.
 * 12. Acceptance: Use of the Software constitutes acceptance of the License.
 * 13. Enforcement: Unauthorized reproduction or distribution may result in civil
 *     and criminal penalties and will be prosecuted to the maximum extent allowed
 *     by law.
 * 
 * Authoritative EULA: EULA-v5.1.md (repository root – private) and https://castconductor.com/eula
 * Precedence: If this summary conflicts with the EULA, the EULA governs.
 * Revision: Current EULA revision v5.1 (subject to update; check EULA for current enterprise thresholds).
 * 
 * Full License text available from: licensing@castconductor.com
 * Security reports: security@castconductor.com
 * Commercial inquiries: licensing@castconductor.com
 * 
 * END OF HEADER
 */

/**
 * CastConductor Content Blocks Manager
 * 
 * Handles content block registration, management, and integration
 * Manages the 8 default content block types for V5
 */

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

class CastConductor_Content_Blocks {
    /**
     * Handle AJAX shoutout submission
     * Creates a new cc_shoutout post from public form submission
     */
    public function handle_shoutout_submission() {
        // Verify nonce for security
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'castconductor_shoutout_nonce')) {
            wp_send_json_error(['message' => __('Security check failed. Please refresh and try again.', 'castconductor')]);
            return;
        }

        // Sanitize and validate inputs
        $name = isset($_POST['name']) ? sanitize_text_field(trim($_POST['name'])) : '';
        $location = isset($_POST['location']) ? sanitize_text_field(trim($_POST['location'])) : '';
        $message = isset($_POST['message']) ? sanitize_textarea_field(trim($_POST['message'])) : '';

        // Validate required fields
        if (empty($name)) {
            wp_send_json_error(['message' => __('Please enter your name.', 'castconductor')]);
            return;
        }
        if (empty($message)) {
            wp_send_json_error(['message' => __('Please enter a message.', 'castconductor')]);
            return;
        }

        // Check message length (max 280 chars like a tweet)
        if (strlen($message) > 280) {
            wp_send_json_error(['message' => __('Message is too long. Please keep it under 280 characters.', 'castconductor')]);
            return;
        }

        // Check if manual approval is required
        $require_approval = get_option('castconductor_shoutout_manual_approval', false);
        $post_status = $require_approval ? 'pending' : 'publish';

        // Create the shoutout post
        $post_data = [
            'post_type'    => 'cc_shoutout',
            'post_status'  => $post_status,
            'post_title'   => sprintf(__('Shoutout from %s', 'castconductor'), $name),
            'post_content' => $message,
        ];

        $post_id = wp_insert_post($post_data, true);

        if (is_wp_error($post_id)) {
            wp_send_json_error(['message' => __('Failed to submit shoutout. Please try again.', 'castconductor')]);
            return;
        }

        // Save meta fields
        update_post_meta($post_id, 'cc_shoutout_name', $name);
        update_post_meta($post_id, 'cc_shoutout_location', $location ?: __('Unknown Location', 'castconductor'));
        update_post_meta($post_id, 'cc_shoutout_approved', $require_approval ? 0 : 1);
        update_post_meta($post_id, 'cc_shoutout_source', 'website_form');

        // Handle retention limit (FIFO - remove oldest if over limit)
        $this->enforce_shoutout_retention_limit();

        // Success response
        $success_message = $require_approval 
            ? __('Thank you! Your shoutout has been submitted and is pending approval.', 'castconductor')
            : __('Thank you! Your shoutout has been submitted and will appear on the channel soon.', 'castconductor');

        wp_send_json_success(['message' => $success_message]);
    }

    /**
     * Enforce shoutout retention limit (FIFO)
     * Removes oldest shoutouts when limit is exceeded
     */
    private function enforce_shoutout_retention_limit() {
        $limit = (int) get_option('castconductor_shoutout_retention_limit', 50);
        if ($limit <= 0) {
            return; // Unlimited
        }

        $shoutouts = get_posts([
            'post_type'      => 'cc_shoutout',
            'post_status'    => 'publish',
            'posts_per_page' => -1,
            'orderby'        => 'date',
            'order'          => 'ASC', // Oldest first
            'fields'         => 'ids',
        ]);

        $count = count($shoutouts);
        if ($count > $limit) {
            $to_delete = array_slice($shoutouts, 0, $count - $limit);
            foreach ($to_delete as $post_id) {
                wp_delete_post($post_id, true); // Force delete, skip trash
            }
        }
    }

    /**
     * Render shoutout form shortcode
     * 
     * Usage: [castconductor_shoutout_form]
     * Options: [castconductor_shoutout_form theme="dark" button_text="Send Shoutout!"]
     */
    public function render_shoutout_form_shortcode($atts = [], $content = null) {
        // Parse shortcode attributes with defaults
        $atts = shortcode_atts([
            'theme'               => 'light',
            'width'               => '100%',
            'button_text'         => __('Submit Shoutout', 'castconductor'),
            'placeholder_name'    => __('Your Name', 'castconductor'),
            'placeholder_location'=> __('Your Location', 'castconductor'),
            'placeholder_message' => __('Send us a shoutout! (280 chars max)', 'castconductor'),
            'success_message'     => '',
        ], $atts, 'castconductor_shoutout_form');

        // Generate unique form ID for multiple forms on same page
        $form_id = 'cc-shoutout-form-' . wp_rand(1000, 9999);
        
        // Enqueue inline styles (no external CSS file needed)
        $this->enqueue_shoutout_form_styles();

        // Build the form HTML
        $theme_class = $atts['theme'] === 'dark' ? 'cc-shoutout-dark' : 'cc-shoutout-light';
        
        ob_start();
        ?>
        <div class="cc-shoutout-form-wrapper <?php echo esc_attr($theme_class); ?>" style="max-width: <?php echo esc_attr($atts['width']); ?>;">
            <form id="<?php echo esc_attr($form_id); ?>" class="cc-shoutout-form" method="post">
                <?php wp_nonce_field('castconductor_shoutout_nonce', 'cc_shoutout_nonce'); ?>
                
                <div class="cc-shoutout-field">
                    <input type="text" 
                           name="cc_shoutout_name" 
                           placeholder="<?php echo esc_attr($atts['placeholder_name']); ?>" 
                           required 
                           maxlength="100"
                           class="cc-shoutout-input" />
                </div>
                
                <div class="cc-shoutout-field">
                    <input type="text" 
                           name="cc_shoutout_location" 
                           placeholder="<?php echo esc_attr($atts['placeholder_location']); ?>" 
                           required
                           maxlength="100"
                           class="cc-shoutout-input" />
                </div>
                
                <div class="cc-shoutout-field">
                    <textarea name="cc_shoutout_message" 
                              placeholder="<?php echo esc_attr($atts['placeholder_message']); ?>" 
                              required 
                              maxlength="280"
                              rows="3"
                              class="cc-shoutout-textarea"></textarea>
                    <div class="cc-shoutout-char-count"><span class="cc-char-current">0</span>/280</div>
                </div>
                
                <div class="cc-shoutout-field">
                    <button type="submit" class="cc-shoutout-submit">
                        <?php echo esc_html($atts['button_text']); ?>
                    </button>
                </div>
                
                <div class="cc-shoutout-message" style="display: none;"></div>
            </form>
        </div>
        
        <script>
        (function() {
            var form = document.getElementById('<?php echo esc_js($form_id); ?>');
            if (!form) return;
            
            var textarea = form.querySelector('textarea[name="cc_shoutout_message"]');
            var charCount = form.querySelector('.cc-char-current');
            var messageDiv = form.querySelector('.cc-shoutout-message');
            var submitBtn = form.querySelector('.cc-shoutout-submit');
            
            // Character counter
            if (textarea && charCount) {
                textarea.addEventListener('input', function() {
                    charCount.textContent = this.value.length;
                    if (this.value.length > 260) {
                        charCount.parentElement.classList.add('cc-char-warning');
                    } else {
                        charCount.parentElement.classList.remove('cc-char-warning');
                    }
                });
            }
            
            // Form submission
            form.addEventListener('submit', function(e) {
                e.preventDefault();
                
                submitBtn.disabled = true;
                submitBtn.textContent = '<?php echo esc_js(__('Sending...', 'castconductor')); ?>';
                messageDiv.style.display = 'none';
                
                var formData = new FormData();
                formData.append('action', 'castconductor_submit_shoutout');
                formData.append('nonce', form.querySelector('[name="cc_shoutout_nonce"]').value);
                formData.append('name', form.querySelector('[name="cc_shoutout_name"]').value);
                formData.append('location', form.querySelector('[name="cc_shoutout_location"]').value);
                formData.append('message', form.querySelector('[name="cc_shoutout_message"]').value);
                
                fetch('<?php echo esc_url(admin_url('admin-ajax.php')); ?>', {
                    method: 'POST',
                    body: formData,
                    credentials: 'same-origin'
                })
                .then(function(response) { return response.json(); })
                .then(function(data) {
                    messageDiv.style.display = 'block';
                    if (data.success) {
                        messageDiv.className = 'cc-shoutout-message cc-shoutout-success';
                        messageDiv.textContent = data.data.message;
                        form.reset();
                        if (charCount) charCount.textContent = '0';
                    } else {
                        messageDiv.className = 'cc-shoutout-message cc-shoutout-error';
                        messageDiv.textContent = data.data.message || '<?php echo esc_js(__('An error occurred. Please try again.', 'castconductor')); ?>';
                    }
                    submitBtn.disabled = false;
                    submitBtn.textContent = '<?php echo esc_js($atts['button_text']); ?>';
                })
                .catch(function(error) {
                    messageDiv.style.display = 'block';
                    messageDiv.className = 'cc-shoutout-message cc-shoutout-error';
                    messageDiv.textContent = '<?php echo esc_js(__('Network error. Please check your connection and try again.', 'castconductor')); ?>';
                    submitBtn.disabled = false;
                    submitBtn.textContent = '<?php echo esc_js($atts['button_text']); ?>';
                });
            });
        })();
        </script>
        <?php
        return ob_get_clean();
    }

    /**
     * Enqueue inline CSS for shoutout form (runs once per page)
     */
    private function enqueue_shoutout_form_styles() {
        static $styles_enqueued = false;
        if ($styles_enqueued) return;
        $styles_enqueued = true;

        add_action('wp_footer', function() {
            ?>
            <style>
            .cc-shoutout-form-wrapper {
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
                margin: 1rem 0;
            }
            .cc-shoutout-form .cc-shoutout-field {
                margin-bottom: 0.75rem;
                position: relative;
            }
            .cc-shoutout-form .cc-shoutout-input,
            .cc-shoutout-form .cc-shoutout-textarea {
                width: 100%;
                padding: 0.75rem 1rem;
                border: 1px solid #ccc;
                border-radius: 6px;
                font-size: 1rem;
                box-sizing: border-box;
                transition: border-color 0.2s, box-shadow 0.2s;
            }
            .cc-shoutout-form .cc-shoutout-input:focus,
            .cc-shoutout-form .cc-shoutout-textarea:focus {
                outline: none;
                border-color: #0073aa;
                box-shadow: 0 0 0 2px rgba(0, 115, 170, 0.2);
            }
            .cc-shoutout-form .cc-shoutout-textarea {
                resize: vertical;
                min-height: 80px;
            }
            .cc-shoutout-form .cc-shoutout-char-count {
                text-align: right;
                font-size: 0.75rem;
                color: #666;
                margin-top: 0.25rem;
            }
            .cc-shoutout-form .cc-shoutout-char-count.cc-char-warning {
                color: #d63638;
                font-weight: 600;
            }
            .cc-shoutout-form .cc-shoutout-submit {
                width: 100%;
                padding: 0.875rem 1.5rem;
                background: #0073aa;
                color: #fff;
                border: none;
                border-radius: 6px;
                font-size: 1rem;
                font-weight: 600;
                cursor: pointer;
                transition: background-color 0.2s;
            }
            .cc-shoutout-form .cc-shoutout-submit:hover {
                background: #005a87;
            }
            .cc-shoutout-form .cc-shoutout-submit:disabled {
                background: #999;
                cursor: not-allowed;
            }
            .cc-shoutout-form .cc-shoutout-message {
                padding: 0.75rem 1rem;
                border-radius: 6px;
                margin-top: 0.75rem;
                font-size: 0.9rem;
            }
            .cc-shoutout-form .cc-shoutout-success {
                background: #d4edda;
                color: #155724;
                border: 1px solid #c3e6cb;
            }
            .cc-shoutout-form .cc-shoutout-error {
                background: #f8d7da;
                color: #721c24;
                border: 1px solid #f5c6cb;
            }
            /* Dark theme */
            .cc-shoutout-dark {
                background: #1e1e1e;
                padding: 1.5rem;
                border-radius: 8px;
            }
            .cc-shoutout-dark .cc-shoutout-input,
            .cc-shoutout-dark .cc-shoutout-textarea {
                background: #2d2d2d;
                border-color: #444;
                color: #fff;
            }
            .cc-shoutout-dark .cc-shoutout-input::placeholder,
            .cc-shoutout-dark .cc-shoutout-textarea::placeholder {
                color: #888;
            }
            .cc-shoutout-dark .cc-shoutout-input:focus,
            .cc-shoutout-dark .cc-shoutout-textarea:focus {
                border-color: #4fc3f7;
                box-shadow: 0 0 0 2px rgba(79, 195, 247, 0.3);
            }
            .cc-shoutout-dark .cc-shoutout-char-count {
                color: #888;
            }
            .cc-shoutout-dark .cc-shoutout-submit {
                background: #4fc3f7;
                color: #000;
            }
            .cc-shoutout-dark .cc-shoutout-submit:hover {
                background: #29b6f6;
            }
            .cc-shoutout-dark .cc-shoutout-success {
                background: rgba(76, 175, 80, 0.2);
                color: #81c784;
                border-color: rgba(76, 175, 80, 0.4);
            }
            .cc-shoutout-dark .cc-shoutout-error {
                background: rgba(244, 67, 54, 0.2);
                color: #ef9a9a;
                border-color: rgba(244, 67, 54, 0.4);
            }
            </style>
            <?php
        }, 100);
    }

    /**
     * Enqueue admin scripts and styles (safe default)
     */
    /**
     * Add admin menu pages for CastConductor
     */
    public function add_admin_menu() {
        // Note: "Content Blocks" menu item removed - functionality merged into Canvas Editor
        
        // Add Album Artwork Discovery admin page
        add_submenu_page(
            'castconductor',
            'Album Artwork Discovery',
            'Album Artwork',
            'edit_posts',  // Allow editors access
            'castconductor-album-artwork',
            [$this, 'render_album_artwork_admin_page']
        );
        // Add Canvas Editor admin page
        add_submenu_page(
            'castconductor',
            'Canvas Editor',
            'Canvas Editor',
            'edit_posts',  // Allow editors access
            'castconductor-canvas-editor',
            [$this, 'render_canvas_editor_admin_page']
        );
        // Add isolated Scenes & Containers (read-only UI)
        add_submenu_page(
            'castconductor',
            'Scenes & Containers',
            'Scenes & Containers',
            'edit_posts',  // Allow editors access
            'castconductor-scenes-stage',
            [$this, 'render_scenes_stage_admin_page']
        );
    }
    /**
     * Register all WordPress hooks for the plugin
     */
    public function setup_hooks() {
        // Admin hooks
        if (is_admin()) {
            add_action('admin_menu', [$this, 'add_admin_menu']);
            add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
        }
        // AJAX hooks for public shoutout submissions
        add_action('wp_ajax_castconductor_submit_shoutout', [$this, 'handle_shoutout_submission']);
        add_action('wp_ajax_nopriv_castconductor_submit_shoutout', [$this, 'handle_shoutout_submission']);
        // Shortcode for public shoutout form
        add_shortcode('castconductor_shoutout_form', [$this, 'render_shoutout_form_shortcode']);
    }
    /**
     * WordPress init callback
     */
    public function init() {
        $this->register_content_block_types();
        $this->setup_hooks();
    }
    /**
     * Content block instances
     */
    private $content_block_types = [];

    /**
     * Constructor
     */
    public function __construct() {
        add_action('init', [$this, 'init']);
    }
    
    /**
     * Register all 8 default content block types
     */
    public function register_content_block_types() {
        // Base Block - Universal template for all content blocks
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-base-block.php';
        $this->content_block_types['base'] = new CastConductor_Base_Block();

        // Demo Block - Example block extending BaseBlock
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-demo-block.php';
        $this->content_block_types['demo'] = new CastConductor_Demo_Block();

        // Track Info - Current song with enhanced album artwork
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-track-info-block.php';
        $this->content_block_types['track_info'] = new CastConductor_Track_Info_Block();

        // Shoutout - User submissions with moderation
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-shoutout-block.php';
        $this->content_block_types['shoutout'] = new CastConductor_Shoutout_Block();

        // Sponsor - Sponsor content with scheduling
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-sponsor-block.php';
        $this->content_block_types['sponsor'] = new CastConductor_Sponsor_Block();

        // Promo - Promotional content with scheduling
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-promo-block.php';
        $this->content_block_types['promo'] = new CastConductor_Promo_Block();

        // Weather - Weather based on IP geolocation
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-weather-block.php';
        $this->content_block_types['weather'] = new CastConductor_Weather_Block();

        // Location/Time - Viewer location and time
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-location-time-block.php';
        $this->content_block_types['location_time'] = new CastConductor_Location_Time_Block();

        // Custom API - External API integration
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-custom-api-block.php';
        $this->content_block_types['custom_api'] = new CastConductor_Custom_API_Block();

        // Custom - User-defined content
        require_once CASTCONDUCTOR_PLUGIN_DIR . 'includes/content-blocks/class-custom-block.php';
        $this->content_block_types['custom'] = new CastConductor_Custom_Block();
    }

    /**
     * Enqueue admin scripts and styles
     */
    public function enqueue_admin_scripts($hook) {
        // Album Artwork page - enable media library for artwork upload
        if ($hook === 'castconductor_page_castconductor-album-artwork') {
            if (function_exists('wp_enqueue_media')) {
                wp_enqueue_media();
            }
        }
        // Only enqueue on the Canvas Editor admin page
        if ($hook === 'castconductor_page_castconductor-canvas-editor') {
            // Load Google Fonts for Canvas Editor preview (matches Roku bundled fonts)
            wp_enqueue_style(
                'castconductor-google-fonts',
                'https://fonts.googleapis.com/css2?family=Lexend:wght@400;600;700&family=Oxygen:wght@400;700&family=Share+Tech+Mono&family=Space+Grotesk:wght@400;600;700&display=swap',
                [],
                null
            );
            wp_enqueue_style(
                'castconductor-canvas-editor',
                plugins_url('../assets/css/canvas-editor.css', __FILE__),
                ['castconductor-google-fonts'],
                filemtime(plugin_dir_path(__FILE__) . '../assets/css/canvas-editor.css')
            );
            // Enqueue QRCode.js library
            wp_enqueue_script(
                'qrcode-js',
                plugins_url('../assets/js/qrcode.min.js', __FILE__),
                [],
                '1.0.0',
                true
            );
            wp_enqueue_script(
                'castconductor-canvas-editor',
                plugins_url('../assets/js/canvas-editor.js', __FILE__),
                ['jquery', 'qrcode-js'],
                filemtime(plugin_dir_path(__FILE__) . '../assets/js/canvas-editor.js'),
                true
            );
            // Ensure ES module imports inside canvas-editor.js execute properly
            add_filter('script_loader_tag', function($tag, $handle) {
                if ($handle === 'castconductor-canvas-editor' || $handle === 'castconductor-scenes-stage') {
                    if (strpos($tag, 'type="module"') === false) {
                        $tag = str_replace('<script ', '<script type="module" ', $tag);
                    }
                }
                return $tag;
            }, 10, 2);
            // Provide REST + admin context to JS (was missing causing ReferenceError)
            wp_localize_script('castconductor-canvas-editor', 'castconductorCanvasAjax', [
                'admin_url' => admin_url(),
                'rest_url'  => rest_url(),
                'nonce'     => wp_create_nonce('wp_rest'),
                'ajax_url'  => admin_url('admin-ajax.php'),
                'ajax_nonce'=> wp_create_nonce('castconductor_canvas_editor'),
                'is_admin_user' => current_user_can('manage_options') ? 1 : 0,
                'metadata_url' => get_option('castconductor_metadata_url', '')
            ]);
        } elseif ($hook === 'castconductor_page_castconductor-scenes-stage') {
            // Isolated Scenes/Containers stage assets
            // Enable media picker for Background & Branding modal
            if (function_exists('wp_enqueue_media')) {
                wp_enqueue_media();
            }
            // Load Google Fonts for Scenes Stage preview (matches Roku bundled fonts)
            wp_enqueue_style(
                'castconductor-google-fonts',
                'https://fonts.googleapis.com/css2?family=Lexend:wght@400;600;700&family=Oxygen:wght@400;700&family=Share+Tech+Mono&family=Space+Grotesk:wght@400;600;700&display=swap',
                [],
                null
            );
            wp_enqueue_style(
                'castconductor-scenes-stage',
                plugins_url('../assets/css/scenes-stage.css', __FILE__),
                ['castconductor-google-fonts'],
                filemtime(plugin_dir_path(__FILE__) . '../assets/css/scenes-stage.css')
            );
            wp_enqueue_script(
                'castconductor-scenes-stage',
                plugins_url('../assets/js/scenes-stage/index.js', __FILE__),
                [],
                filemtime(plugin_dir_path(__FILE__) . '../assets/js/scenes-stage/index.js'),
                true
            );
            // Add type="module" attribute for ES6 module support (Smart Reflow)
            add_filter('script_loader_tag', function($tag, $handle) {
                if ($handle === 'castconductor-scenes-stage') {
                    return str_replace(' src=', ' type="module" src=', $tag);
                }
                return $tag;
            }, 10, 2);
            // Provide REST + admin context to scenes stage as well
            wp_localize_script('castconductor-scenes-stage', 'castconductorScenesAjax', [
                'admin_url' => admin_url(),
                'rest_url'  => rest_url(),
                'nonce'     => wp_create_nonce('wp_rest'),
                'is_admin_user' => current_user_can('manage_options') ? 1 : 0
            ]);
        }
    }

        /**
         * Render admin page for content blocks management
         */
        public function render_admin_page() {
            ?>
            <div class="wrap">
                <h1>
                    🎨 Content Blocks
                    <a href="#" class="page-title-action" id="add-content-block">Add New</a>
                </h1>
                <div class="castconductor-content-blocks-wrapper">
                    <!-- Content blocks list will be loaded via JavaScript -->
                    <div id="content-blocks-list" class="content-blocks-grid">
                        <div class="loading">Loading content blocks...</div>
                    </div>
                    <div id="ccve-global-autofix" style="display:none;position:fixed;bottom:12px;right:12px;z-index:9999;background:#fff;border:1px solid #e11d48;border-radius:6px;padding:10px 14px;box-shadow:0 4px 14px rgba(0,0,0,0.25);font-family:system-ui,sans-serif;gap:10px;align-items:center;min-width:260px;">
                        <strong style="color:#b91c1c;font-size:13px;">Layout Issue</strong>
                        <div style="font-size:12px;color:#444;margin:4px 0 8px;" id="ccve-global-autofix-msg">Overflow or aspect mismatch detected.</div>
                        <div style="display:flex;gap:8px;justify-content:flex-end;">
                            <button type="button" class="button button-small" id="ccve-global-autofix-fix">Auto-Fix</button>
                            <button type="button" class="button button-small" id="ccve-global-autofix-dismiss">Dismiss</button>
                        </div>
                    </div>
                    <!-- Add/Edit modal -->
                    <div id="content-block-modal" class="castconductor-modal" style="display: none;">
                        <div class="modal-content">
                            <div class="modal-header">
                                <h2 id="modal-title">Add Content Block</h2>
                                <span class="close">&times;</span>
                            </div>
                            <div class="modal-body">
                                <form id="content-block-form">
                                    <table class="form-table">
                                        <tr>
                                            <th scope="row">
                                                <label for="block-name">Name</label>
                                            </th>
                                            <td>
                                                <input type="text" id="block-name" name="name" class="regular-text" required />
                                                <p class="description">A descriptive name for this content block</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th scope="row">
                                                <label for="block-type">Type</label>
                                            </th>
                                            <td>
                                                <select id="block-type" name="type" required>
                                                    <option value="">Select content block type...</option>
                                                </select>
                                                <p class="description">Choose the type of content this block will display</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th scope="row">
                                                <label for="block-enabled">Status</label>
                                            </th>
                                            <td>
                                                <label>
                                                    <input type="checkbox" id="block-enabled" name="enabled" value="1" checked />
                                                    Enable this content block
                                                </label>
                                            </td>
                                        </tr>
                                    </table>
                                    <!-- Type-specific configuration will be loaded here -->
                                    <div id="type-specific-config"></div>
                                    <!-- Visual configuration preview -->
                                    <div id="visual-config-preview" style="display: none;">
                                        <h3>Preview</h3>
                                        <div id="preview-area" class="content-block-preview"></div>
                                    </div>
                                </form>
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="button" id="cancel-content-block">Cancel</button>
                                <button type="submit" class="button button-primary" id="save-content-block">Save Content Block</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <style>
            .content-blocks-grid {
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
                gap: 20px;
                margin-top: 20px;
            }
            .content-block-card {
                background: #fff;
                border: 1px solid #ddd;
                border-radius: 4px;
                padding: 20px;
                position: relative;
            }
            .content-block-card h3 {
                margin: 0 0 10px 0;
                font-size: 16px;
            }
            .content-block-type {
                background: #2271b1;
                color: white;
                padding: 2px 8px;
                border-radius: 3px;
                font-size: 12px;
                text-transform: uppercase;
            }
            .content-block-actions {
                margin-top: 15px;
            }
            .content-block-actions button {
                margin-right: 5px;
            }
            .castconductor-modal {
                position: fixed;
                z-index: 1000;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                background-color: rgba(0,0,0,0.5);
            }
            .modal-content {
                background: #fff;
                margin: 5% auto;
                width: 80%;
                max-width: 800px;
                border-radius: 4px;
                box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            }
            .modal-header {
                padding: 20px;
                border-bottom: 1px solid #ddd;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }
            .modal-header h2 {
                margin: 0;
            }
            .close {
                font-size: 24px;
                cursor: pointer;
            }
            .modal-body {
                padding: 20px;
                max-height: 60vh;
                overflow-y: auto;
            }
            .modal-footer {
                padding: 20px;
                border-top: 1px solid #ddd;
                text-align: right;
            }
            .content-block-preview {
                border: 1px solid #ddd;
                padding: 20px;
                background: #f9f9f9;
                border-radius: 4px;
                min-height: 100px;
            }
            .loading {
                text-align: center;
                padding: 40px;
                color: #666;
            }
            </style>
            <?php
        }

        /**
         * Render album artwork admin page
         */
        public function render_album_artwork_admin_page() {
            ?>
            <div class="wrap" id="album-artwork-admin">
                <h1>🎨 Album Artwork Management</h1>
                <p class="description">Search, override, and cache album artwork for Track Info content blocks.</p>
                
                <!-- Cache Management -->
                <div class="postbox">
                    <div class="postbox-header">
                        <h2>🗂️ Cache Management</h2>
                    </div>
                    <div class="inside">
                        <p>Manage the album artwork cache to improve performance and resolve issues.</p>
                        <div class="action-buttons">
                            <button type="button" id="clear-artwork-cache" class="button button-secondary">Clear Artwork Cache</button>
                        </div>
                        <div id="cache-status"></div>
                    </div>
                </div>

                <!-- Artwork Overrides Management -->
                <div class="postbox">
                    <div class="postbox-header">
                        <h2>🎯 Artwork Overrides</h2>
                    </div>
                    <div class="inside">
                        <p class="description">Manually override incorrect artwork matches. Overrides take priority over API lookups.</p>
                        
                        <!-- Live Now Playing -->
                        <?php 
                        $metadata_url = get_option('castconductor_metadata_url', '');
                        if (!empty($metadata_url)): 
                        ?>
                        <div class="live-now-playing" style="background:linear-gradient(135deg,#1e1b4b 0%,#312e81 100%);padding:16px;border-radius:8px;margin-bottom:20px;color:#fff;">
                            <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
                                <h4 style="margin:0;color:#fff;display:flex;align-items:center;gap:8px;">
                                    <span style="display:inline-block;width:8px;height:8px;background:#22c55e;border-radius:50%;animation:pulse 2s infinite;"></span>
                                    Live Now Playing
                                </h4>
                                <button type="button" id="refresh-now-playing" class="button button-small" style="background:#4338ca;border:none;color:#fff;">↻ Refresh</button>
                            </div>
                            <div id="now-playing-content" style="font-size:14px;">
                                <div style="color:#a5b4fc;">Loading...</div>
                            </div>
                            <div id="now-playing-actions" style="margin-top:12px;display:none;">
                                <button type="button" id="use-as-match-key" class="button" style="background:#22c55e;border:none;color:#fff;font-size:12px;">
                                    ✓ Use as Match Key
                                </button>
                                <span style="margin-left:8px;font-size:12px;color:#a5b4fc;">Auto-fill the exact metadata for override matching</span>
                            </div>
                        </div>
                        <style>
                            @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
                        </style>
                        <?php endif; ?>
                        
                        <!-- Search & Override Form -->
                        <div class="override-add-section" style="background:#f9fafb;padding:16px;border-radius:8px;margin-bottom:20px;">
                            <h4 style="margin-top:0;margin-bottom:12px;">🔍 Search & Override</h4>
                            <form id="artwork-override-search-form">
                                <div class="form-row" style="display:grid;grid-template-columns:1fr 1fr auto;gap:12px;margin-bottom:12px;align-items:end;">
                                    <div class="form-group">
                                        <label for="override-artist">Artist Name <span class="required">*</span></label>
                                        <input type="text" id="override-artist" placeholder="e.g., Jason Nevins" required />
                                    </div>
                                    <div class="form-group">
                                        <label for="override-title">Track Title <span class="required">*</span></label>
                                        <input type="text" id="override-title" placeholder="e.g., Heaven" required />
                                    </div>
                                    <button type="submit" class="button button-primary" style="height:30px;">🔍 Search All Sources</button>
                                </div>
                            </form>
                            <div id="search-status" style="margin:8px 0;font-size:13px;"></div>
                            
                            <!-- Match Key Configuration (for exact metadata matching) -->
                            <details id="match-key-section" style="margin-top:12px;">
                                <summary style="cursor:pointer;color:#6b7280;font-size:13px;">🎯 Match Key (if metadata shows differently than search)</summary>
                                <div style="margin-top:12px;padding:12px;background:#fff;border:1px dashed #d1d5db;border-radius:6px;">
                                    <p style="margin:0 0 12px 0;font-size:13px;color:#6b7280;">Enter the <strong>exact</strong> artist/title as it appears in your stream metadata. This ensures the override matches even when the track displays differently (e.g., "Jason Nevins p/ Holly" vs "Jason Nevins presents Holly").</p>
                                    <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
                                        <div class="form-group">
                                            <label for="match-artist" style="font-size:13px;">Match Artist <span style="color:#888;font-weight:normal;">(as in metadata)</span></label>
                                            <input type="text" id="match-artist" placeholder="e.g., Jason Nevins p/ Holly" style="width:100%;" />
                                        </div>
                                        <div class="form-group">
                                            <label for="match-title" style="font-size:13px;">Match Title <span style="color:#888;font-weight:normal;">(as in metadata)</span></label>
                                            <input type="text" id="match-title" placeholder="e.g., Heaven" style="width:100%;" />
                                        </div>
                                    </div>
                                    <p style="margin:12px 0 0 0;font-size:12px;color:#888;">💡 Tip: Check how the track appears on your Roku screen to get the exact metadata string.</p>
                                </div>
                            </details>
                            
                            <!-- Search Results Grid -->
                            <div id="search-results-grid" style="display:none;margin-top:16px;">
                                <h5 style="margin:0 0 12px 0;color:#374151;">Select artwork to use as override:</h5>
                                <div id="search-results-container" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:16px;"></div>
                            </div>
                            
                            <!-- Manual Override Option -->
                            <div style="margin-top:16px;padding-top:16px;border-top:1px solid #e2e8f0;">
                                <details>
                                    <summary style="cursor:pointer;color:#6b7280;font-size:13px;">📤 Or upload/enter artwork manually</summary>
                                    <div style="margin-top:12px;">
                                        <div style="display:flex;gap:12px;align-items:end;">
                                            <div style="flex:1;">
                                                <label for="override-url-manual" style="font-size:13px;">Artwork URL</label>
                                                <input type="url" id="override-url-manual" placeholder="https://..." style="width:100%;" />
                                            </div>
                                            <button type="button" id="override-upload-btn" class="button">📤 Upload</button>
                                            <button type="button" id="override-save-manual" class="button button-primary">💾 Save Override</button>
                                        </div>
                                        <div id="manual-preview" style="margin-top:8px;display:none;">
                                            <img id="manual-preview-img" style="max-width:100px;max-height:100px;border-radius:4px;" />
                                        </div>
                                    </div>
                                </details>
                            </div>
                        </div>

                        <!-- Current Overrides List -->
                        <h4>📋 Current Overrides</h4>
                        <div id="overrides-loading" style="color:#666;padding:20px;text-align:center;">Loading overrides...</div>
                        <table id="overrides-table" class="wp-list-table widefat fixed striped" style="display:none;">
                            <thead>
                                <tr>
                                    <th style="width:100px;">Preview</th>
                                    <th>Artist</th>
                                    <th>Title</th>
                                    <th style="width:180px;">Match Keys</th>
                                    <th style="width:100px;">Created</th>
                                    <th style="width:120px;">Actions</th>
                                </tr>
                            </thead>
                            <tbody id="overrides-table-body">
                            </tbody>
                        </table>
                        <div id="no-overrides-message" style="display:none;padding:20px;text-align:center;color:#666;background:#f9fafb;border-radius:8px;">
                            No artwork overrides configured yet. Search for a track above to find and set artwork.
                        </div>
                    </div>
                </div>
            </div>

            <script type="text/javascript">
            (function() {
                const apiBase = '<?php echo esc_url(rest_url('castconductor/v5')); ?>';
                const nonce = '<?php echo wp_create_nonce('wp_rest'); ?>';
                const metadataUrl = '<?php echo esc_url(get_option('castconductor_metadata_url', '')); ?>';
                let currentArtist = '';
                let currentTitle = '';
                let liveArtist = '';
                let liveTitle = '';

                // Load overrides on page load
                document.addEventListener('DOMContentLoaded', () => {
                    loadOverrides();
                    setupEventHandlers();
                    if (metadataUrl) {
                        fetchNowPlaying();
                        // Auto-refresh every 30 seconds
                        setInterval(fetchNowPlaying, 30000);
                    }
                });

                // Fetch live now playing data
                async function fetchNowPlaying() {
                    const content = document.getElementById('now-playing-content');
                    const actions = document.getElementById('now-playing-actions');
                    if (!content) return;
                    
                    try {
                        // Call the raw metadata URL directly (AzuraCast format)
                        const response = await fetch(metadataUrl);
                        const data = await response.json();
                        
                        // Parse AzuraCast format: now_playing.song.artist / now_playing.song.title
                        const song = data?.now_playing?.song;
                        if (song && (song.artist || song.title)) {
                            liveArtist = song.artist || '';
                            liveTitle = song.title || '';
                            const artworkUrl = song.art || '';
                            const album = song.album || '';
                            
                            content.innerHTML = `
                                <div style="display:flex;align-items:center;gap:16px;">
                                    ${artworkUrl ? `<img src="${escapeHtml(artworkUrl)}" style="width:60px;height:60px;border-radius:6px;object-fit:cover;" onerror="this.style.display='none'" />` : ''}
                                    <div style="flex:1;">
                                        <div style="font-size:16px;font-weight:600;color:#fff;">${escapeHtml(liveTitle)}</div>
                                        <div style="font-size:14px;color:#c7d2fe;">${escapeHtml(liveArtist)}</div>
                                        ${album ? `<div style="font-size:12px;color:#a5b4fc;">${escapeHtml(album)}</div>` : ''}
                                    </div>
                                </div>
                                <div style="margin-top:8px;padding:8px;background:rgba(255,255,255,0.1);border-radius:4px;font-family:monospace;font-size:11px;color:#e0e7ff;">
                                    <strong>Raw metadata:</strong> ${escapeHtml(liveArtist)} - ${escapeHtml(liveTitle)}
                                </div>
                            `;
                            if (actions) actions.style.display = 'block';
                        } else {
                            content.innerHTML = '<div style="color:#fca5a5;">No track currently playing</div>';
                            if (actions) actions.style.display = 'none';
                        }
                    } catch (err) {
                        content.innerHTML = `<div style="color:#fca5a5;">Error: ${err.message}</div>`;
                        if (actions) actions.style.display = 'none';
                    }
                }

                function setupEventHandlers() {
                    // Search form
                    document.getElementById('artwork-override-search-form').addEventListener('submit', handleSearch);
                    
                    // Live Now Playing handlers
                    const refreshBtn = document.getElementById('refresh-now-playing');
                    if (refreshBtn) {
                        refreshBtn.addEventListener('click', fetchNowPlaying);
                    }
                    
                    const useAsMatchKeyBtn = document.getElementById('use-as-match-key');
                    if (useAsMatchKeyBtn) {
                        useAsMatchKeyBtn.addEventListener('click', () => {
                            if (liveArtist || liveTitle) {
                                // Fill match key fields
                                const matchArtist = document.getElementById('match-artist');
                                const matchTitle = document.getElementById('match-title');
                                if (matchArtist) matchArtist.value = liveArtist;
                                if (matchTitle) matchTitle.value = liveTitle;
                                
                                // Also fill search fields if empty
                                const searchArtist = document.getElementById('override-artist');
                                const searchTitle = document.getElementById('override-title');
                                if (searchArtist && !searchArtist.value) searchArtist.value = liveArtist;
                                if (searchTitle && !searchTitle.value) searchTitle.value = liveTitle;
                                
                                // Open the match key section
                                const matchSection = document.getElementById('match-key-section');
                                if (matchSection) matchSection.open = true;
                                
                                // Visual feedback
                                useAsMatchKeyBtn.textContent = '✓ Filled!';
                                useAsMatchKeyBtn.style.background = '#16a34a';
                                setTimeout(() => {
                                    useAsMatchKeyBtn.textContent = '✓ Use as Match Key';
                                    useAsMatchKeyBtn.style.background = '#22c55e';
                                }, 1500);
                            }
                        });
                    }
                    
                    // Manual URL preview
                    document.getElementById('override-url-manual').addEventListener('input', (e) => {
                        const url = e.target.value.trim();
                        const preview = document.getElementById('manual-preview');
                        const img = document.getElementById('manual-preview-img');
                        if (url && url.startsWith('http')) {
                            img.src = url;
                            preview.style.display = 'block';
                        } else {
                            preview.style.display = 'none';
                        }
                    });
                    
                    // Manual save
                    document.getElementById('override-save-manual').addEventListener('click', async () => {
                        const artist = document.getElementById('override-artist').value.trim();
                        const title = document.getElementById('override-title').value.trim();
                        const url = document.getElementById('override-url-manual').value.trim();
                        
                        if (!artist || !title || !url) {
                            alert('Please fill in artist, title, and artwork URL');
                            return;
                        }
                        
                        await saveOverride(artist, title, url);
                    });
                    
                    // Upload button
                    document.getElementById('override-upload-btn').addEventListener('click', () => {
                        if (typeof wp !== 'undefined' && wp.media) {
                            const mediaFrame = wp.media({
                                title: 'Select or Upload Artwork',
                                button: { text: 'Use this artwork' },
                                multiple: false,
                                library: { type: 'image' }
                            });
                            mediaFrame.on('select', function() {
                                const attachment = mediaFrame.state().get('selection').first().toJSON();
                                document.getElementById('override-url-manual').value = attachment.url;
                                document.getElementById('override-url-manual').dispatchEvent(new Event('input'));
                            });
                            mediaFrame.open();
                        } else {
                            alert('WordPress Media Library not available.');
                        }
                    });
                }

                async function handleSearch(e) {
                    e.preventDefault();
                    const artist = document.getElementById('override-artist').value.trim();
                    const title = document.getElementById('override-title').value.trim();
                    const status = document.getElementById('search-status');
                    const grid = document.getElementById('search-results-grid');
                    const container = document.getElementById('search-results-container');
                    
                    if (!artist && !title) {
                        status.innerHTML = '<span style="color:#dc2626;">❌ Enter artist and/or title</span>';
                        return;
                    }
                    
                    currentArtist = artist;
                    currentTitle = title;
                    status.innerHTML = '<span style="color:#666;">🔍 Searching iTunes, Deezer, MusicBrainz...</span>';
                    grid.style.display = 'none';
                    
                    try {
                        const response = await fetch(`${apiBase}/album-artwork/search-all`, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
                            body: JSON.stringify({ artist, title })
                        });
                        const data = await response.json();
                        
                        if (data.results && data.results.length > 0) {
                            status.innerHTML = `<span style="color:#16a34a;">✅ Found ${data.count} options. Click one to use it:</span>`;
                            grid.style.display = 'block';
                            
                            container.innerHTML = data.results.map((r, i) => `
                                <div class="artwork-result-card" data-url="${escapeHtml(r.artwork_url)}" style="background:#fff;border:2px solid #e2e8f0;border-radius:8px;padding:12px;cursor:pointer;transition:all 0.2s;">
                                    <img src="${escapeHtml(r.artwork_url)}" style="width:100%;aspect-ratio:1;object-fit:cover;border-radius:4px;background:#f3f4f6;" onerror="this.parentElement.style.display='none'" />
                                    <div style="margin-top:8px;">
                                        <div style="font-size:10px;text-transform:uppercase;color:#6b7280;font-weight:600;">${escapeHtml(r.source)}</div>
                                        <div style="font-size:12px;font-weight:500;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${escapeHtml(r.artist || 'Unknown Artist')}</div>
                                        <div style="font-size:11px;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${escapeHtml(r.album || '')}</div>
                                    </div>
                                    <button type="button" class="button button-primary use-artwork-btn" style="width:100%;margin-top:8px;font-size:12px;">✅ Use This</button>
                                </div>
                            `).join('');
                            
                            // Add click handlers
                            container.querySelectorAll('.use-artwork-btn').forEach(btn => {
                                btn.addEventListener('click', handleUseArtwork);
                            });
                            
                            // Hover effect
                            container.querySelectorAll('.artwork-result-card').forEach(card => {
                                card.addEventListener('mouseenter', () => card.style.borderColor = '#2563eb');
                                card.addEventListener('mouseleave', () => card.style.borderColor = '#e2e8f0');
                            });
                        } else {
                            status.innerHTML = '<span style="color:#d97706;">⚠️ No artwork found. Try different search terms or upload manually.</span>';
                            grid.style.display = 'none';
                        }
                    } catch (err) {
                        status.innerHTML = `<span style="color:#dc2626;">❌ Search failed: ${err.message}</span>`;
                    }
                }

                async function handleUseArtwork(e) {
                    const card = e.target.closest('.artwork-result-card');
                    const url = card.dataset.url;
                    const btn = e.target;
                    
                    // Get match key values (the exact metadata strings)
                    const matchArtist = document.getElementById('match-artist')?.value.trim() || '';
                    const matchTitle = document.getElementById('match-title')?.value.trim() || '';
                    
                    btn.disabled = true;
                    btn.textContent = '⏳ Uploading...';
                    
                    try {
                        // Sideload to media library and set as override
                        const response = await fetch(`${apiBase}/album-artwork/sideload`, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
                            body: JSON.stringify({ 
                                url, 
                                artist: currentArtist, 
                                title: currentTitle,
                                match_artist: matchArtist,
                                match_title: matchTitle
                            })
                        });
                        const data = await response.json();
                        
                        if (data.success) {
                            btn.textContent = '✅ Saved!';
                            btn.style.background = '#16a34a';
                            const matchNote = (matchArtist || matchTitle) ? ` (match key: ${matchArtist || currentArtist} - ${matchTitle || currentTitle})` : '';
                            document.getElementById('search-status').innerHTML = 
                                `<span style="color:#16a34a;">✅ Override saved for "${currentArtist} - ${currentTitle}"${matchNote}</span>`;
                            
                            // Clear match key fields
                            if (document.getElementById('match-artist')) document.getElementById('match-artist').value = '';
                            if (document.getElementById('match-title')) document.getElementById('match-title').value = '';
                            
                            // Refresh overrides list
                            setTimeout(() => {
                                loadOverrides();
                                document.getElementById('search-results-grid').style.display = 'none';
                            }, 1000);
                        } else {
                            throw new Error(data.message || 'Failed to save');
                        }
                    } catch (err) {
                        btn.textContent = '❌ Failed';
                        btn.disabled = false;
                        alert('Failed to save: ' + err.message);
                    }
                }

                async function saveOverride(artist, title, url) {
                    // Get match key values
                    const matchArtist = document.getElementById('match-artist')?.value.trim() || '';
                    const matchTitle = document.getElementById('match-title')?.value.trim() || '';
                    
                    try {
                        const response = await fetch(`${apiBase}/metadata/artwork-override`, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
                            body: JSON.stringify({ 
                                artist, 
                                title, 
                                artwork_url: url,
                                match_artist: matchArtist,
                                match_title: matchTitle
                            })
                        });
                        const data = await response.json();
                        
                        if (data.success) {
                            alert('Override saved!');
                            loadOverrides();
                            document.getElementById('override-url-manual').value = '';
                            if (document.getElementById('match-artist')) document.getElementById('match-artist').value = '';
                            if (document.getElementById('match-title')) document.getElementById('match-title').value = '';
                            document.getElementById('manual-preview').style.display = 'none';
                        } else {
                            throw new Error(data.message || 'Failed');
                        }
                    } catch (err) {
                        alert('Failed to save: ' + err.message);
                    }
                }

                async function loadOverrides() {
                    const loadingEl = document.getElementById('overrides-loading');
                    const tableEl = document.getElementById('overrides-table');
                    const noOverridesEl = document.getElementById('no-overrides-message');
                    const tbody = document.getElementById('overrides-table-body');

                    try {
                        const response = await fetch(`${apiBase}/metadata/artwork-overrides`, {
                            headers: { 'X-WP-Nonce': nonce }
                        });
                        const data = await response.json();

                        loadingEl.style.display = 'none';

                        if (data.overrides && data.overrides.length > 0) {
                            tableEl.style.display = 'table';
                            noOverridesEl.style.display = 'none';
                            
                            tbody.innerHTML = data.overrides.map(o => {
                                // Build match keys display
                                let matchKeysHtml = `<div style="font-size:11px;"><strong>Primary:</strong> ${escapeHtml(o.artist)} - ${escapeHtml(o.title)}</div>`;
                                if (o.match_keys && o.match_keys.length > 1) {
                                    matchKeysHtml += o.match_keys.slice(1).map(mk => 
                                        `<div style="font-size:10px;color:#666;margin-top:2px;">+ ${escapeHtml(mk.artist)} - ${escapeHtml(mk.title)}</div>`
                                    ).join('');
                                } else if (o.match_artist || o.match_title) {
                                    matchKeysHtml += `<div style="font-size:10px;color:#666;margin-top:2px;">+ ${escapeHtml(o.match_artist || o.artist)} - ${escapeHtml(o.match_title || o.title)}</div>`;
                                }
                                
                                return `
                                <tr data-artist="${escapeHtml(o.artist)}" data-title="${escapeHtml(o.title)}">
                                    <td><img src="${escapeHtml(o.artwork_url)}" style="max-width:80px;max-height:80px;border-radius:4px;" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%22 height=%22100%22><rect fill=%22%23ddd%22 width=%22100%22 height=%22100%22/><text x=%2250%%22 y=%2250%%22 text-anchor=%22middle%22 dy=%22.3em%22 fill=%22%23999%22>Error</text></svg>';" /></td>
                                    <td style="font-size:13px;">${escapeHtml(o.artist)}</td>
                                    <td style="font-size:13px;">${escapeHtml(o.title)}</td>
                                    <td>${matchKeysHtml}</td>
                                    <td style="font-size:11px;">${o.created_at || '—'}</td>
                                    <td>
                                        <button type="button" class="button button-small delete-override" title="Delete override">🗑️</button>
                                    </td>
                                </tr>
                            `}).join('');

                            // Add delete handlers
                            tbody.querySelectorAll('.delete-override').forEach(btn => {
                                btn.addEventListener('click', handleDeleteOverride);
                            });
                        } else {
                            tableEl.style.display = 'none';
                            noOverridesEl.style.display = 'block';
                        }
                    } catch (err) {
                        loadingEl.textContent = 'Error loading overrides: ' + err.message;
                    }
                }

                // Handle delete override
                async function handleDeleteOverride(e) {
                    const row = e.target.closest('tr');
                    const artist = row.dataset.artist;
                    const title = row.dataset.title;

                    if (!confirm(`Delete artwork override for "${artist} - ${title}"?`)) {
                        return;
                    }

                    try {
                        const response = await fetch(`${apiBase}/metadata/artwork-override?artist=${encodeURIComponent(artist)}&title=${encodeURIComponent(title)}`, {
                            method: 'DELETE',
                            headers: { 'X-WP-Nonce': nonce }
                        });

                        const data = await response.json();

                        if (data.success) {
                            loadOverrides();
                        } else {
                            alert('Failed to delete: ' + (data.message || 'Unknown error'));
                        }
                    } catch (err) {
                        alert('Delete failed: ' + err.message);
                    }
                }

                function escapeHtml(str) {
                    if (!str) return '';
                    const div = document.createElement('div');
                    div.textContent = str;
                    return div.innerHTML;
                }
            })();
            </script>
            <?php
        }

        /**
         * Render Canvas Editor admin page
         */
        public function render_canvas_editor_admin_page() {
            ?>
            <div class="wrap">
                <h1>🖼️ Content Block Visual Editor (V5)</h1>
                <div id="canvas-toolbar" class="canvas-toolbar"></div>
                <!-- Added expected #canvas-editor-container wrapper (was #canvas-editor-root causing JS init skip) -->
                <div id="canvas-editor-container" class="canvas-editor-root">
                    <div class="canvas-editor-layout">
                        <!-- Block Stage (Content Blocks tab only) -->
                        <div class="block-stage-wrapper" id="block-stage-wrapper">
                            <div class="ccve-stage-outer" data-surface="block">
                                <div class="canvas-stage" id="block-stage">
                                    <div id="canvas-preview-container" class="canvas-preview-container ccve-placeholder">
                                        <span>Preview will appear here</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <!-- Inline toggles apply to active surface; grid & zone edit hidden on Block Stage -->
                        <div class="canvas-inline-toggles" id="canvas-inline-toggles" style="text-align:center;margin:12px 0 32px 0;padding:8px 0 4px;display:flex;justify-content:center;gap:24px;border-top:1px solid #e2e8f0;">
                            <label style="margin-right:0;"><input type="checkbox" id="toggle-grid" checked /> Guides</label>
                            <label style="margin-right:16px;"><input type="checkbox" id="toggle-smart-snap" checked /> Smart Snapping</label>
                            <label style="margin-right:16px;"><input type="checkbox" id="toggle-zone-edit" /> Zone Edit</label>
                            <label><input type="checkbox" id="toggle-content-preview" checked /> Content Preview</label>
                            <label><input type="checkbox" id="toggle-rulers" /> Rulers</label>
                            <label><input type="checkbox" id="toggle-hud" checked /> HUD</label>
                        </div>
                        <!-- Persistent Controls/Menu below the stage surfaces -->
                        <div class="canvas-persistent-menu">
                            <!-- Removed old V4 Containers tab - now using dedicated Scenes & Containers stage -->
                            <!-- <div class="canvas-tabs canvas-editor-tabs">
                                <button class="canvas-tab-button active" data-tab="content-blocks">Content Blocks</button>
                            </div> -->
                            <!-- Persistent global actions bar (Save) -->
                            <div id="ccve-global-actions-bar" style="position:sticky;top:0;z-index:999;display:flex;gap:12px;align-items:center;background:#f8fafc;padding:8px 12px;border:1px solid #e2e8f0;border-radius:6px;margin-bottom:12px;box-shadow:0 1px 2px rgba(0,0,0,.05);">
                                <button type="button" id="ccve-global-save" class="button button-primary" disabled title="Save visual configuration (enabled when there are unsaved changes)">💾 Save Changes</button>
                                <!-- Auto-Fix button hidden (Dec 2025) - rarely used, clutters UI -->
                                <button type="button" id="ccve-global-autofix-open" class="button" title="Show Auto-Fix panel if hidden" style="display:none;">🛠 Auto-Fix</button>
                                <span id="ccve-unsaved-indicator" style="display:none;font-size:12px;color:#b45309;">Unsaved changes</span>
                            </div>
                            <!-- Sub-tabs for Content Block categories -->
                            <div id="content-blocks-tab" class="canvas-tab-content active">
                                <div class="canvas-category-tabs">
                                    <button class="canvas-category-tab-button active" data-category="baseblock">Block Template</button>
                                    <!-- Typography, Background, Artwork, Motion tabs removed - use double-click precision modal instead -->
                                    <button class="canvas-category-tab-button button button-primary" data-category="qr-generator" id="ccve-qr-code-tab-btn" style="margin-left: 12px; display: inline-flex; align-items: center; gap: 4px; padding: 6px 14px; font-size: 13px; font-weight: 600;">
                                        <svg style="width: 16px; height: 16px; vertical-align: middle;" fill="currentColor" viewBox="0 0 20 20">
                                            <path fill-rule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm3 2h6v4H7V5zm8 8v2h1v-2h-1zm-2-2H7v4h6v-4zm2 0h1V9h-1v2zm1-4V5h-1v2h1zM5 9H4v2h1V9zm0 4H4v2h1v-2zM5 5H4v2h1V5z" clip-rule="evenodd"/>
                                        </svg>
                                        <span>QR Code</span>
                                    </button>
                                    <button type="button" class="button button-primary" id="ccve-add-layer-btn" style="margin-left: 8px; display: inline-flex; align-items: center; gap: 4px; padding: 6px 14px; font-size: 13px; font-weight: 600;">
                                        <span style="font-size: 16px;">+</span>
                                        <span>Add Layer</span>
                                        <span style="font-size: 9px;">▼</span>
                                    </button>
                                    <!-- Public API button hidden (Dec 2025) - feature not fully ready -->
                                    <button type="button" class="button" id="ccve-public-api-btn" style="display: none; margin-left: 8px;">
                                        <span style="font-size: 14px;">🌐</span>
                                        <span>Public API</span>
                                        <span style="font-size: 9px;">▼</span>
                                    </button>
                                    <button type="button" class="button button-primary" id="ccve-container-preset-btn" style="margin-left: 8px; display: inline-flex; align-items: center; gap: 4px; padding: 6px 14px; font-size: 13px; font-weight: 600;">
                                        <span style="font-size: 14px;">📐</span>
                                        <span>Container Preset</span>
                                        <span style="font-size: 9px;">▼</span>
                                    </button>
                                </div>
                                <!-- Block Layers panel (moved from Containers tab; shows token + hydrated legacy layers for the active content block) -->
                                <div class="ccve-layers-panel-wrapper" style="display:none;" id="ccve-layers-panel-wrapper">
                                    <aside class="ccve-layers-panel" id="ccve-layers-panel" aria-label="Block Layers" style="display:none;">
                                        <h3>Block Layers</h3>
                                        <div class="ccve-layers-legend" style="font-size:11px;color:#666;margin-bottom:4px;display:flex;gap:8px;align-items:center;">
                                            <span title="Visibility">👁</span>
                                            <span title="Lock">🔒</span>
                                            <span title="Order">⇅</span>
                                            <span style="flex:1;">Name</span>
                                        </div>
                                        <ul id="ccve-layers-list"></ul>
                                    </aside>
                                </div>
                                <div id="canvas-controls-panel" class="canvas-controls-panel">
                                    <div class="controls-section category-panel active" id="category-baseblock">
                                        <h3>Block Template</h3>
                                        <label for="canvas-baseblock-select" style="display:none;">BaseBlock Template:</label>
                                        <select id="canvas-baseblock-select" data-dynamic-block-types style="display:none;">
                                            <option value="track_info">Track Info</option>
                                            <option value="shoutout">Shoutout</option>
                                            <option value="sponsor">Sponsor</option>
                                            <option value="promo">Promo</option>
                                            <option value="weather">Weather</option>
                                            <option value="location_time">Location & Time</option>
                                            <option value="responsive_generic">Responsive Generic</option>
                                            <!-- SHELVED (Dec 2025): Custom API hidden from UI -->
                                            <!-- <option value="custom_api">Custom API</option> -->
                                            <option value="custom">Custom Content</option>
                                        </select>
                                        <div style="margin-top:14px;display:grid;gap:6px;max-width:360px;">
                                            <!-- Primary action: Create New -->
                                            <button type="button" id="canvas-create-new-btn" class="button button-primary" style="font-size:14px;padding:8px 16px;">➕ Create New Content Block</button>
                                            
                                            <hr style="margin:12px 0;border:0;border-top:1px solid #334155;">
                                            
                                            <label for="canvas-content-block-select" style="font-weight:600;">Or edit an existing Content Block:</label>
                                            <select id="canvas-content-block-select">
                                                <option value="">-- Select to Edit --</option>
                                            </select>
                                            <div style="display:flex;gap:8px;align-items:center;">
                                                <button type="button" id="canvas-load-block-btn" class="button">Load Selected</button>
                                                <small style="color:#64748b;">Load a block to edit it</small>
                                            </div>
                                            
                                            <!-- CPT Content Import Section (hierarchical dropdown for shoutouts/sponsors/promos) -->
                                            <div id="canvas-cpt-import-section" style="margin-top:16px;padding-top:12px;border-top:1px solid #334155;display:none;">
                                                <label style="font-weight:600;display:block;margin-bottom:8px;">📥 Import Content from CPT:</label>
                                                <div style="display:grid;gap:8px;max-width:360px;">
                                                    <select id="canvas-cpt-type-select" style="width:100%;">
                                                        <option value="">-- Select Content Type --</option>
                                                        <option value="sponsor">Sponsors</option>
                                                        <option value="promo">Promos</option>
                                                        <option value="shoutout">Shoutouts</option>
                                                    </select>
                                                    <select id="canvas-cpt-post-select" style="width:100%;display:none;">
                                                        <option value="">-- Select Item --</option>
                                                    </select>
                                                    <div style="display:flex;gap:8px;align-items:center;">
                                                        <button type="button" id="canvas-cpt-load-btn" class="button" disabled>📥 Load CPT Content</button>
                                                        <small id="canvas-cpt-status" style="color:#64748b;"></small>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                           <span id="ccve-last-created-hint" style="display:none;margin-left:8px;font-size:11px;color:#3a7;">Last created <span data-rel-time></span></span>
                                            <div style="margin-top:12px;display:flex;gap:12px;align-items:center;">
                                                <button type="button" id="canvas-duplicate-as-new" class="button" disabled title="Load a block first" style="display:none;">⧉ Duplicate As New</button>
                                                <button type="button" id="canvas-save-config" class="button button-secondary" disabled style="margin-left:auto;">💾 Save Changes</button>
                                            </div>
                                        <!-- Intended Container dropdown hidden (Dec 2025) - redundant, confusing UX -->
                                        <div style="display:none;margin-top:16px;gap:4px;max-width:360px;">
                                            <label for="canvas-intended-container" style="font-weight:600;">Intended Container (Template Context Only):</label>
                                            <select id="canvas-intended-container" data-scope="baseblock">
                                                <option value="lower-third">Lower Third</option>
                                                <option value="upper-third">Upper Third</option>
                                                <option value="full-screen">Full Screen</option>
                                                <option value="left-half">Left Half</option>
                                                <option value="right-half">Right Half</option>
                                                <option value="upper-left-quarter">Upper Left Quarter</option>
                                                <option value="upper-right-quarter">Upper Right Quarter</option>
                                                <option value="lower-left-quarter">Lower Left Quarter</option>
                                                <option value="lower-right-quarter">Lower Right Quarter</option>
                                                <option value="horizontal-upper-half">Horizontal Upper Half</option>
                                                <option value="horizontal-lower-half">Horizontal Lower Half</option>
                                            </select>
                                            <small style="color:#64748b;">Used for preset geometry seeding only; not required during style tuning.</small>
                                        </div>
                                    </div>
                                    <div class="controls-section category-panel" id="category-typography">
                                        <h3>Typography</h3>
                                        <label for="canvas-font-family">Font Family
                                            <select id="canvas-font-family">
                                                <option value="system-ui,Arial,Helvetica,sans-serif">Roku System Font</option>
                                                <option value="Lexend">Lexend</option>
                                                <option value="Oxygen">Oxygen</option>
                                                <option value="ShareTechMono">Share Tech Mono</option>
                                                <option value="SpaceGrotesk">Space Grotesk</option>
                                            </select>
                                        </label>
                                        <small style="color:#64748b;display:block;margin-top:-6px;margin-bottom:10px;">Custom fonts are bundled in the Roku app. Editor preview uses web-safe fallbacks.</small>
                                        <label for="canvas-font-size">Font Size (12-80pt)
                                            <input id="canvas-font-size" type="number" min="12" max="80" />
                                        </label>
                                        <small style="color:#64748b;display:block;margin-top:-6px;margin-bottom:10px;">Roku sizes: Small (12-17), Medium (18-23), Large (24-35), XL (36+)</small>
                                        <label for="canvas-font-weight">Font Weight
                                            <select id="canvas-font-weight">
                                                <option value="400">Regular (400)</option>
                                                <option value="700">Bold (700)</option>
                                            </select>
                                        </label>
                                        <label for="canvas-font-color">Font Color <input id="canvas-font-color" type="color" /></label>
                                        <label>Text Align
                                            <select id="canvas-text-align">
                                                <option value="left">Left</option>
                                                <option value="center">Center</option>
                                                <option value="right">Right</option>
                                            </select>
                                        </label>
                                        <label>Line Height <input id="canvas-line-height" type="number" min="0.5" max="3" step="0.01" /></label>
                                        <label>Text Shadow X <input id="canvas-text-shadow-x" type="number" /></label>
                                        <label>Text Shadow Y <input id="canvas-text-shadow-y" type="number" /></label>
                                        <label>Text Shadow Blur <input id="canvas-text-shadow-blur" type="number" /></label>
                                        <label>Text Shadow Color <input id="canvas-text-shadow-color" type="color" /></label>
                                    </div>
                                    <div class="controls-section category-panel" id="category-layout">
                                        <h3>Layout</h3>
                                        <div class="ccve-layout-visual-summary">
                                            <p class="hint">Drag block on stage to move. Drag edges to resize. Use Alt+Click on rulers to add guides.</p>
                                            <div class="ccve-layout-row">
                                                <label>Fit
                                                    <select id="canvas-fit-mode" class="ccve-fit-mode">
                                                        <option value="fill" selected>Fill</option>
                                                        <option value="contain">Contain</option>
                                                        <option value="cover">Cover</option>
                                                        <option value="maintain-aspect">Maintain Aspect</option>
                                                        <option value="anchor">Anchor</option>
                                                    </select>
                                                </label>
                                                <label>Mode
                                                    <select id="ccve-geometry-mode">
                                                        <option value="fixed">Fixed (px)</option>
                                                        <option value="responsive">Responsive (%)</option>
                                                    </select>
                                                </label>
                                                <button type="button" id="ccve-toggle-advanced-geometry" class="button">Show precise numbers</button>
                                            </div>
                                            <div class="ccve-layout-pill-group">
                                                <span class="pill" id="ccve-pill-left">Left: <strong>—</strong></span>
                                                <span class="pill" id="ccve-pill-top">Top: <strong>—</strong></span>
                                                <span class="pill" id="ccve-pill-width">Width: <strong>—</strong></span>
                                                <span class="pill" id="ccve-pill-height">Height: <strong>—</strong></span>
                                            </div>
                                        </div>
                                        <div id="ccve-advanced-geometry" class="ccve-advanced-geometry" style="display:none;margin-top:8px;">
                                            <div class="adv-grid">
                                                <label>Width <input id="canvas-width" type="number" min="1" max="1920" /></label>
                                                <label class="ccve-responsive-only" style="display:none;">Width % <input id="canvas-width-pct" type="number" min="0" max="100" step="0.01" /></label>
                                                <label>Height <input id="canvas-height" type="number" min="1" max="1080" /></label>
                                                <label class="ccve-responsive-only" style="display:none;">Height % <input id="canvas-height-pct" type="number" min="0" max="100" step="0.01" /></label>
                                                <label>X <input id="canvas-position-x" type="number" min="0" max="1920" /></label>
                                                <label class="ccve-responsive-only" style="display:none;">Left % <input id="canvas-position-x-pct" type="number" min="0" max="100" step="0.01" /></label>
                                                <label>Y <input id="canvas-position-y" type="number" min="0" max="1080" /></label>
                                                <label class="ccve-responsive-only" style="display:none;">Top % <input id="canvas-position-y-pct" type="number" min="0" max="100" step="0.01" /></label>
                                                <label>Padding Top <input id="canvas-padding-top" type="number" min="0" max="500" /></label>
                                                <label>Padding Right <input id="canvas-padding-right" type="number" min="0" max="500" /></label>
                                                <label>Padding Bottom <input id="canvas-padding-bottom" type="number" min="0" max="500" /></label>
                                                <label>Padding Left <input id="canvas-padding-left" type="number" min="0" max="500" /></label>
                                            </div>
                                            <button type="button" id="ccve-hide-advanced-geometry" class="button button-small" style="margin-top:6px;">Hide numbers</button>
                                        </div>
                                    </div>
                                    <div class="controls-section category-panel" id="category-background">
                                        <h3>Background</h3>
                                        <label>Type
                                            <select id="canvas-bg-type">
                                                <option value="color">Color</option>
                                                <option value="gradient">Gradient</option>
                                                <option value="image">Image (Legacy Base)</option>
                                            </select>
                                        </label>
                                        <label>Color <input id="canvas-bg-color" type="color" /></label>
                                        <label>Border Radius <input id="canvas-border-radius" type="number" min="0" max="100" /></label>
                                        <label>Border Width <input id="canvas-border-width" type="number" min="0" max="20" /></label>
                                        <label>Border Color <input id="canvas-border-color" type="color" /></label>
                                        <label>Border Style
                                            <select id="canvas-border-style">
                                                <option value="solid">Solid</option>
                                                <option value="dashed">Dashed</option>
                                                <option value="dotted">Dotted</option>
                                            </select>
                                        </label>
                                        <label>Gradient Color 1 <input id="canvas-bg-gradient-color1" type="color" /></label>
                                        <label>Gradient Color 2 <input id="canvas-bg-gradient-color2" type="color" /></label>
                                        <label>Gradient Direction <input id="canvas-bg-gradient-direction" type="number" min="0" max="360" /></label>
                                        <label>Overlay Color <input id="canvas-overlay-color" type="color" /></label>
                                        <label>Overlay Opacity <input id="canvas-overlay-opacity" type="number" min="0" max="1" step="0.01" /></label>
                                        <hr style="margin:10px 0;" />
                                        <h4 style="margin:4px 0 6px;">Background Layers</h4>
                                        <p style="margin:0 0 6px;font-size:11px;color:#666;">Toggle, reorder, and add layers. Order = stacking top➝bottom.</p>
                                        <ul id="ccve-bg-layer-list" style="list-style:none;margin:0 0 8px;padding:0;max-height:180px;overflow:auto;border:1px solid #ddd;padding:6px;border-radius:4px;background:#fff;"></ul>
                                        <div style="display:flex;gap:6px;align-items:center;">
                                            <select id="ccve-bg-layer-kind" style="min-width:120px;">
                                                <option value="image">Image</option>
                                                <option value="gradient">Gradient</option>
                                                <option value="overlay">Overlay</option>
                                                <option value="color">Color Layer</option>
                                            </select>
                                            <button type="button" class="button" id="ccve-bg-add-layer" style="margin-right:6px;">+ Add Layer</button>
                                        </div>
                                    </div>
                                    <div class="controls-section category-panel" id="category-diagnostics">
                                        <h3>Diagnostics</h3>
                                        <button type="button" id="ccve-refresh-diagnostics" class="button">Refresh Diagnostics</button>
                                        <div id="ccve-diagnostics-output" style="margin-top:8px;font-size:12px;line-height:1.4;background:#111;padding:8px;border:1px solid #333;max-height:200px;overflow:auto;white-space:pre-wrap;">
                                            (No data yet)
                                        </div>
                                    </div>
                                    <div class="controls-section category-panel" id="category-artwork">
                                        <h3>Artwork</h3>
                                        <label>Enable Artwork <input id="canvas-artwork-enabled" type="checkbox" /></label>
                                        <label for="canvas-artwork-width">Width <input id="canvas-artwork-width" type="number" min="0" max="1920" /></label>
                                        <label for="canvas-artwork-height">Height <input id="canvas-artwork-height" type="number" min="0" max="1080" /></label>
                                        <label for="canvas-artwork-position">Position
                                            <select id="canvas-artwork-position">
                                                <option value="left">Left</option>
                                                <option value="right">Right</option>
                                                <option value="top">Top</option>
                                                <option value="bottom">Bottom</option>
                                            </select>
                                        </label>
                                        <label for="canvas-artwork-gap">Artwork Gap (px) <input id="canvas-artwork-gap" type="number" min="0" max="300" /></label>
                                        <label for="canvas-artwork-border-radius">Border Radius <input id="canvas-artwork-border-radius" type="number" min="0" max="100" /></label>
                                        <!-- TODO: Add artwork API checkboxes if needed -->
                                        <hr style="margin:12px 0;" />
                                        <h4 style="margin:4px 0 8px;">Slideshow & Media</h4>
                                        <div style="display:flex;flex-wrap:wrap;gap:8px;align-items:flex-start;">
                                            <label style="display:flex;align-items:center;gap:6px;font-weight:500;">
                                                Enable <input id="ccve-slideshow-enabled" type="checkbox" aria-label="Enable slideshow" />
                                            </label>
                                            <label for="ccve-slideshow-interval" style="display:flex;flex-direction:column;gap:4px;">
                                                <span style="white-space:nowrap;">Slideshow Interval (ms)</span>
                                                <input id="ccve-slideshow-interval" type="number" min="500" step="100" aria-label="Slideshow interval in milliseconds" />
                                            </label>
                                            <label for="ccve-slideshow-transition">Transition
                                                <select id="ccve-slideshow-transition" aria-label="Slideshow transition">
                                                    <option value="fade">Fade</option>
                                                </select>
                                            </label>
                                            <button type="button" class="button" id="ccve-artwork-add-media" aria-label="Add artwork images">Add Images</button>
                                        </div>
                                        <div id="ccve-artwork-items-wrapper" style="margin-top:10px;">
                                            <ul id="ccve-artwork-items" role="list" aria-label="Artwork images" style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;max-height:260px;overflow:auto;border:1px solid #e2e8f0;padding:6px;border-radius:6px;background:#fff;">
                                                <!-- dynamically populated -->
                                            </ul>
                                            <template id="ccve-artwork-item-template">
                                                <li class="ccve-artwork-item" role="listitem" aria-label="Artwork item" style="display:flex;align-items:center;gap:8px;padding:4px 6px;border:1px solid #dbe3ec;border-radius:4px;background:#f9fafb;">
                                                    <span class="ccve-artwork-thumb" style="width:46px;height:46px;flex:0 0 46px;background:#111;border:1px solid #333;border-radius:4px;overflow:hidden;display:flex;align-items:center;justify-content:center;font-size:10px;color:#999;">IMG</span>
                                                    <input type="text" class="ccve-artwork-alt" placeholder="alt text" style="flex:1;min-width:120px;font-size:11px;padding:3px 4px;" />
                                                    <div style="display:flex;flex-direction:column;gap:2px;">
                                                        <button type="button" class="button button-small ccve-artwork-move-up" title="Move Up" aria-label="Move image up" style="line-height:1;padding:2px 6px;">↑</button>
                                                        <button type="button" class="button button-small ccve-artwork-move-down" title="Move Down" aria-label="Move image down" style="line-height:1;padding:2px 6px;">↓</button>
                                                    </div>
                                                    <button type="button" class="button ccve-artwork-set-active" title="Set Active" aria-label="Set active image" style="line-height:1;padding:4px 8px;">★</button>
                                                    <button type="button" class="button ccve-artwork-remove" title="Remove" aria-label="Remove image" style="line-height:1;padding:4px 8px;">✕</button>
                                                </li>
                                            </template>
                                        </div>
                                    </div>
                                    <div class="controls-section category-panel" id="category-motion">
                                        <h3>Motion & Behavior</h3>
                                        <div style="margin-bottom: 16px; padding: 12px; background: #f1f5f9; border-radius: 6px; border: 1px solid #e2e8f0;">
                                            <h4 style="margin: 0 0 12px 0; font-size: 14px; color: #475569;">Layer Animation</h4>
                                            <p id="layer-anim-hint" style="margin: 0 0 8px 0; font-size: 12px; color: #64748b;">Select a layer on the stage to animate it</p>
                                            <div id="layer-anim-controls" style="display: none;">
                                                <label style="display: block; margin-bottom: 8px;">Animation Type
                                                    <select id="canvas-layer-animation" style="width: 100%; margin-top: 4px;">
                                                        <option value="none">None</option>
                                                        <option value="fade">Fade In</option>
                                                        <option value="float">Float</option>
                                                        <option value="pulse">Pulse</option>
                                                        <option value="float-pulse">Float + Pulse</option>
                                                    </select>
                                                </label>
                                                <label id="layer-anim-speed-row" style="display: none; margin-bottom: 8px;">Animation Speed
                                                    <input type="range" id="canvas-layer-animation-speed" min="10" max="100" value="50" style="width: 100%; margin-top: 4px;" />
                                                    <span id="canvas-layer-speed-label" style="font-size: 11px; color: #64748b;">Medium</span>
                                                </label>
                                            </div>
                                        </div>
                                        <h4 style="margin: 16px 0 12px 0; font-size: 14px; color: #475569;">Block Animation</h4>
                                        <label>Enable Animation <input id="canvas-anim-enabled" type="checkbox" /></label>
                                        <label>Duration <input id="canvas-anim-duration" type="number" min="0" max="10" step="0.1" /></label>
                                        <label>Easing <input id="canvas-anim-easing" type="text" /></label>
                                        <label>Enable Ticker <input id="canvas-ticker-enabled" type="checkbox" /></label>
                                        <label>Ticker Speed <input id="canvas-ticker-speed" type="number" min="1" max="100" /></label>
                                        <label>Shuffle Items <input id="canvas-shuffle-items" type="checkbox" /></label>
                                    </div>
                                    <!-- If any controls are incomplete, mark them clearly below: -->
                                    <!-- TODO: Some advanced controls (e.g., color pickers, artwork API checkboxes, etc.) may need further enhancement. -->
                                </div>
                            </div>
                    <div class="canvas-loader-content">
                        <div class="canvas-loader-spinner"></div>
                        <div class="loader-text">Loading...</div>
                    </div>
                </div>
                <!-- Notifications -->
                <div id="canvas-notifications"></div>
            </div>
            <style>
            .canvas-toolbar { margin-bottom: 16px; }
            #canvas-editor-root { min-height: 700px; background: #f8fafc; border-radius: 8px; box-shadow: 0 2px 8px #0001; padding: 0; }
            .canvas-stage { display: block; }
            .canvas-persistent-menu { max-width: 960px; margin: 0 auto; }
            .canvas-tab-content { display: none; }
            .canvas-tab-content.active { display: block; }
            .canvas-tabs { display: flex; gap: 8px; margin-bottom: 16px; }
            .canvas-tab-button { background: #e5e7eb; border: none; border-radius: 4px 4px 0 0; padding: 8px 18px; font-size: 16px; cursor: pointer; color: #222; }
            .canvas-tab-button.active { background: #fff; color: #111; border-bottom: 2px solid #6366f1; }
            .canvas-controls-panel, .canvas-containers-panel { background: #fff; border-radius: 0 0 8px 8px; box-shadow: 0 2px 8px #0001; padding: 24px; }
            .controls-section { margin-bottom: 24px; }
            .controls-section h3 { margin-top: 0; margin-bottom: 12px; font-size: 18px; color: #374151; }
            .controls-section label { display: block; margin-bottom: 8px; font-weight: 500; }
            .controls-section input, .controls-section select { margin-left: 8px; min-width: 120px; }
            /* Hide WordPress footer/version info on canvas editor page */
            #wpfooter, .update-nag, .notice, .wrap > h1 + .notice, .wrap > .notice, .wrap > .updated, .wrap > .error { display: none !important; }
            </style>
            <script>
            // Ensure the new editor is initialized only once and after DOM is ready
            (function() {
                function initCastConductorEditor() {
                    if (window.castConductorCanvasEditor) return;
                    if (typeof CastConductorCanvasEditor === 'function') {
                        window.castConductorCanvasEditor = new CastConductorCanvasEditor();
                    }
                }
                if (document.readyState === 'loading') {
                    document.addEventListener('DOMContentLoaded', initCastConductorEditor);
                } else {
                    initCastConductorEditor();
                }
            })();
            </script>
            <?php
        }

        /**
         * Render isolated Scenes & Containers admin page (read-only UI)
         */
        public function render_scenes_stage_admin_page() {
            ?>
            <div class="wrap">
                <h1>🎬 Scenes & Containers</h1>
                <p style="margin:8px 0 16px;color:#475569;">Authoring space: 1280×720 (Roku scales on-device). This stage is isolated from the Content Block editor.</p>
                <div id="cc-scenes-stage-root"></div>
            </div>
            <?php
        }
    }