<?php
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * VMO Consultation to Website Integration
 * 
 * Handles the complete flow from consultation completion to website generation
 * Integrates consultation data, uploaded media, and business intelligence into website
 */

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

class VMO_Consultation_Website_Integration {
    private static $instance = null;
    private static $hooks_registered = false;
    
    private $consultation_db;
    private $business_wizard;
    private $website_assistant;
    
    public function __construct() {
        if (!self::$hooks_registered) {
            $this->setup_hooks();
            self::$hooks_registered = true;
        }
        $this->consultation_db = new VMO_Consultation_Database();
        $this->business_wizard = VMO_Enhanced_Business_Wizard::get_instance();
        // VMO_Website_AI_Assistant is optional - only instantiate if available
        if (class_exists('VMO_Website_AI_Assistant')) {
            $this->website_assistant = new VMO_Website_AI_Assistant();
        } else {
            $this->website_assistant = null;
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO Integration: VMO_Website_AI_Assistant not available - some features may be limited');
            }
        }
        self::$instance = $this;
    }

    public static function get_instance() {
        if (!self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * Setup WordPress hooks
     */
    private function setup_hooks() {
        add_action('wp_ajax_vmo_generate_website_from_consultation', array($this, 'ajax_generate_website_from_consultation'));
        add_action('vmo_consultation_completed', array($this, 'trigger_website_generation'), 10, 1);
        add_action('wp_ajax_vmo_get_consultation_progress', array($this, 'ajax_get_consultation_progress'));
        
        // ✅ NEW: Chunked generation endpoint for long-running operations (like deep research)
        add_action('wp_ajax_vmo_trigger_business_generation', array($this, 'ajax_trigger_business_generation'));
        add_action('wp_ajax_vmo_get_generation_status', array($this, 'ajax_get_generation_status'));

        // ✅ NEW: Phase 6 store/catalog setup (products + checkout links)
        add_action('wp_ajax_vmo_phase6_setup_store_and_catalog', array($this, 'ajax_phase6_setup_store_and_catalog'));
        
        // ✅ NEW: Publish draft website endpoint
        add_action('wp_ajax_vmo_publish_draft_website', array($this, 'ajax_publish_draft_website'));
    }

    /**
     * Phase 6: Set up store catalog (upsert products + pre-provision payments where possible).
     * Returns store URL + buy URLs for each product.
     */
    public function ajax_phase6_setup_store_and_catalog() {
        $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : (isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '');
        $ok_ajax = $nonce && wp_verify_nonce($nonce, 'vmo_ajax_nonce');
        $ok_builder = $nonce && wp_verify_nonce($nonce, 'vmo_builder_nonce');
        $ok_admin = $nonce && wp_verify_nonce($nonce, 'vmo_admin_nonce');
        if (!($ok_ajax || $ok_builder || $ok_admin)) {
            wp_send_json_error(array('message' => 'Security check failed', 'status' => 'invalid_nonce'), 403);
            return;
        }

        $session_id = sanitize_text_field((string) ($_POST['session_id'] ?? ''));
        if (!$session_id) {
            wp_send_json_error(array('message' => 'Missing session_id'), 400);
            return;
        }

        $consultation_row = method_exists($this->consultation_db, 'get_consultation_by_session')
            ? $this->consultation_db->get_consultation_by_session($session_id)
            : null;
        if (!$consultation_row && ctype_digit($session_id)) {
            $consultation_row = $this->consultation_db->get_consultation((int)$session_id);
        }
        if (!$consultation_row) {
            wp_send_json_error(array('message' => 'Consultation not found'), 404);
            return;
        }

        try {
            $business_data = $this->process_consultation_data($consultation_row);

            $needs_ecommerce = false;
            if (is_array($business_data['ecommerce'] ?? null)) {
                $needs_ecommerce = !empty($business_data['ecommerce']['intends_to_sell'])
                    || !empty($business_data['ecommerce']['sell_on_website'])
                    || !empty($business_data['ecommerce']['collect_payments'])
                    || (($business_data['ecommerce']['collection_mode'] ?? '') === 'donations');
            }

            if (!$needs_ecommerce) {
                wp_send_json_success(array(
                    'message' => 'No e-commerce intent detected; catalog setup skipped.',
                    'ecommerce' => false,
                    'store_url' => (string) home_url('/store'),
                    'products' => array(),
                ));
                return;
            }

            $mode = (string) ($business_data['ecommerce']['collection_mode'] ?? 'products');

            // Donation-mode: provision payments (Stripe Connect) but do not create catalog products.
            if ($mode === 'donations') {
                if (defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED) {
                    $this->auto_setup_payments_and_store($business_data, true);
                }
                $funds = is_array($business_data['ecommerce']['donation_funds'] ?? null) ? $business_data['ecommerce']['donation_funds'] : array();
                wp_send_json_success(array(
                    'message' => 'Donation payments prepared successfully.',
                    'ecommerce' => true,
                    'donation_mode' => true,
                    'donate_url' => (string) home_url('/donate'),
                    'store_url' => (string) home_url('/donate'),
                    'products' => array(),
                    'donation_funds' => $funds,
                ));
                return;
            }

            $products_with_ids = array();
            if (defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED) {
                $products_with_ids = $this->auto_setup_payments_and_store($business_data);
            } else {
                // Even if payments are disabled, still try to upsert products into the local catalog.
                $merchant_user_id = get_current_user_id();
                $products = is_array($business_data['ecommerce']['products'] ?? null) ? $business_data['ecommerce']['products'] : array();
                $products_with_ids = $products ? $this->upsert_products_direct($merchant_user_id, $products) : array();
            }

            $out = array();
            foreach (($products_with_ids ?: array()) as $p) {
                $pid = (int) ($p['id'] ?? 0);
                $buy_url = $pid > 0 ? (string) add_query_arg(array('vmo_buy_product' => $pid), home_url('/')) : '';
                $out[] = array(
                    'id' => $pid,
                    'name' => (string) ($p['name'] ?? ''),
                    'slug' => (string) ($p['slug'] ?? ''),
                    'price' => $p['price'] ?? null,
                    'currency' => (string) ($p['currency'] ?? 'usd'),
                    'buy_url' => $buy_url,
                );
            }

            wp_send_json_success(array(
                'message' => 'Store catalog prepared successfully.',
                'ecommerce' => true,
                'store_url' => (string) home_url('/store'),
                'products' => $out,
            ));
        } catch (\Throwable $e) {
            wp_send_json_error(array('message' => 'Catalog setup failed: ' . $e->getMessage()), 500);
        }
    }
    
    // Total generation chunks - each handles a specific part to avoid timeouts
    const TOTAL_GENERATION_CHUNKS = 12;
    const MAX_PAGES_PER_GENERATION_CHUNK = 3;

    private function prepare_long_running_runtime() {
        if (function_exists('set_time_limit')) {
            @set_time_limit(0);
        }
        if (function_exists('ignore_user_abort')) {
            @ignore_user_abort(true);
        }
        if (function_exists('ini_set')) {
            @ini_set('memory_limit', '512M');
        }
    }
    
    /**
     * AJAX handler for generating website from consultation
     */
    public function ajax_generate_website_from_consultation() {
        $this->prepare_long_running_runtime();

        $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : (isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '');
        $ok_ajax = $nonce && wp_verify_nonce($nonce, 'vmo_ajax_nonce');
        $ok_builder = $nonce && wp_verify_nonce($nonce, 'vmo_builder_nonce');
        $ok_admin = $nonce && wp_verify_nonce($nonce, 'vmo_admin_nonce');
        if (!($ok_ajax || $ok_builder || $ok_admin)) {
            wp_send_json_error(array('message' => 'Security check failed', 'status' => 'invalid_nonce'), 403);
            return;
        }
        
        $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
        // Prefer fetching by session_id; fall back to numeric ID if provided
        $consultation_row = method_exists($this->consultation_db, 'get_consultation_by_session')
            ? $this->consultation_db->get_consultation_by_session($session_id)
            : null;
        if (!$consultation_row && ctype_digit($session_id)) {
            $consultation_row = $this->consultation_db->get_consultation((int)$session_id);
        }

        if (!$consultation_row) {
            wp_send_json_error(array('message' => 'Consultation not found'));
            return;
        }

        $authorized_row = $this->get_authorized_consultation_row((string) $consultation_row->session_id);
        if (is_wp_error($authorized_row)) {
            wp_send_json_error(array('message' => $authorized_row->get_error_message()), (int) ($authorized_row->get_error_data('status') ?: 403));
            return;
        }

        if (!class_exists('VMO_Canonical_Website_Pipeline')) {
            wp_send_json_error(array('message' => 'Canonical website pipeline is not available.'), 500);
            return;
        }

        $result = VMO_Canonical_Website_Pipeline::get_instance()->run_from_consultation((string) $consultation_row->session_id);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()), 500);
            return;
        }
        
        wp_send_json_success($result);
    }
    
    /**
     * Trigger website generation when consultation is completed
     */
    public function trigger_website_generation($payload) {
        // Normalize payload to just the session_id for lookup later
        $session_id = is_array($payload) && isset($payload['session_id']) ? $payload['session_id'] : (string)$payload;
        if (!$session_id) return;
        // Queue the website generation process
        wp_schedule_single_event(time() + 30, 'vmo_generate_website_async', array(array('session_id' => $session_id)));
    }
    
    /**
     * Generate complete website from consultation data
     */
    public function generate_complete_website($input) {
        $this->prepare_long_running_runtime();

        try {
            $this->send_progress_update('Starting comprehensive website generation...', 10);
            // Resolve consultation row by session_id
            $session_id = is_array($input) && isset($input['session_id']) ? $input['session_id'] : (is_object($input) && isset($input->session_id) ? $input->session_id : null);
            if (!$session_id && is_string($input)) { $session_id = $input; }
            if (!$session_id) { throw new Exception('Missing session_id for website generation'); }
            $consultation_row = method_exists($this->consultation_db, 'get_consultation_by_session')
                ? $this->consultation_db->get_consultation_by_session($session_id)
                : null;
            if (!$consultation_row) throw new Exception('Consultation not found for session ' . $session_id);
            
            // Step 1: Extract and process consultation data
            $business_data = $this->process_consultation_data($consultation_row);
            $this->send_progress_update('Business data processed successfully', 20);
            
            // Step 2: Process and integrate uploaded media
            $media_assets = $this->process_uploaded_media($session_id);
            $this->send_progress_update('Media assets processed and optimized', 30);
            
            // Step 3: Generate comprehensive business package
            $business_package = $this->generate_business_package($business_data);
            $business_data = $this->ensure_business_manifest_for_generation($business_data, $business_package);
            $business_data = $this->ensure_ecommerce_product_packets($business_data, $media_assets);
            $business_package = $this->attach_ecommerce_product_packets_to_package($business_package, $business_data);
            $this->send_progress_update('Business analysis and strategy completed', 50);
            
            // Step 3.5: If e-commerce intent detected, auto-provision payments and store catalog
            if (defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED) {
                $this->send_progress_update('E-commerce intent detected. Provisioning payments and store…', 55);
                $products_with_ids = $this->auto_setup_payments_and_store($business_data);
                if (is_array($products_with_ids) && isset($business_data['ecommerce'])) {
                    $business_data['ecommerce']['products'] = $products_with_ids;
                }
            }

            // Step 4: Create website architecture
            $website_architecture = $this->create_website_architecture($business_data, $media_assets);
            $this->send_progress_update('Website architecture designed', 60);
            
            // Step 5: Generate complete website pages
            $website_pages = $this->generate_website_pages($website_architecture, $business_data, $media_assets);
            $this->send_progress_update('Website pages generated', 80);
            
            // Step 6: Integrate AI assistant
            $this->integrate_ai_assistant($session_id, $business_data);
            $this->send_progress_update('AI assistant integrated', 90);
            
            // Step 7: Save website as a reviewable draft with QA metadata
            $website_draft = $this->save_and_publish_website($website_pages, $business_data, $session_id, $business_package, $media_assets);
            $preview_ready = is_array($website_draft) ? !empty($website_draft['preview_ready']) : false;
            $this->send_progress_update(
                $preview_ready
                    ? 'Website preview is ready for review and approval.'
                    : 'Website draft assembled. Required media must finish before preview release.',
                100
            );

            if (method_exists($this->business_wizard, 'persist_generation_results_from_business_data')) {
                $this->business_wizard->persist_generation_results_from_business_data(
                    $business_data,
                    $business_package,
                    $website_draft,
                    $session_id
                );
            }
            
            // Keep the consultation in-progress until required media has been generated and inserted.
            $this->update_status_by_session($session_id, $preview_ready ? 'completed' : 'generating_media');
            
            // Send completion notification only when the draft is actually ready to be shown.
            $this->send_completion_notification($consultation_row, $website_draft, $business_package);

            $public_preview_url = $preview_ready && is_array($website_draft)
                ? ($website_draft['preview_url'] ?? '')
                : '';
            $public_website_draft = $website_draft;
            if (!$preview_ready && is_array($public_website_draft)) {
                $public_website_draft['preview_url'] = '';
            }
            
            wp_send_json_success(array(
                'message' => $preview_ready
                    ? 'Website preview is ready for review.'
                    : 'Website draft assembled. Required media is still generating before preview release.',
                'website_url' => $public_preview_url,
                'website_draft' => $public_website_draft,
                'preview_url' => $public_preview_url,
                'business_package' => $business_package,
                'qa_report' => is_array($website_draft) ? ($website_draft['qa_report'] ?? array()) : array(),
                'final_qa_gate' => is_array($website_draft) ? ($website_draft['final_qa_gate'] ?? array()) : array(),
                'visual_preset' => is_array($website_draft) ? ($website_draft['visual_preset'] ?? '') : '',
                'publish_ready' => is_array($website_draft) ? !empty($website_draft['publish_ready']) : false
            ));
            
        } catch (Exception $e) {
            error_log('VMO Website Generation Error: ' . $e->getMessage());
            $this->send_progress_update('Error: ' . $e->getMessage(), 0);
            wp_send_json_error(array('message' => 'Website generation failed: ' . $e->getMessage()));
        }
    }

    /**
     * ✅ v9.2.0: Run the premium pipeline synchronously and return the result.
     * Used by handler demotion — handlers call this instead of generating directly.
     * Unlike generate_complete_website(), this does NOT call wp_send_json_*.
     *
     * @param string $session_id Consultation session ID
     * @return array|null Result array or null if consultation not found
     */
    public function run_pipeline_sync($session_id) {
        $this->prepare_long_running_runtime();

        $consultation_row = method_exists($this->consultation_db, 'get_consultation_by_session')
            ? $this->consultation_db->get_consultation_by_session($session_id)
            : null;
        if (!$consultation_row && ctype_digit((string) $session_id)) {
            $consultation_row = $this->consultation_db->get_consultation((int) $session_id);
        }
        if (!$consultation_row) {
            return null;
        }

        try {
            $business_data       = $this->process_consultation_data($consultation_row);
            $media_assets        = $this->process_uploaded_media($session_id);
            $business_package    = $this->generate_business_package($business_data);
            $business_data       = $this->ensure_business_manifest_for_generation($business_data, $business_package);
            $business_data       = $this->ensure_ecommerce_product_packets($business_data, $media_assets);
            $business_package    = $this->attach_ecommerce_product_packets_to_package($business_package, $business_data);

            if (defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED) {
                $products_with_ids = $this->auto_setup_payments_and_store($business_data);
                if (is_array($products_with_ids) && isset($business_data['ecommerce'])) {
                    $business_data['ecommerce']['products'] = $products_with_ids;
                }
            }

            $website_architecture = $this->create_website_architecture($business_data, $media_assets);
            $website_pages        = $this->generate_website_pages($website_architecture, $business_data, $media_assets);
            $this->integrate_ai_assistant($session_id, $business_data);
            $website_draft        = $this->save_and_publish_website($website_pages, $business_data, $session_id, $business_package, $media_assets);

            if (method_exists($this->business_wizard, 'persist_generation_results_from_business_data')) {
                $this->business_wizard->persist_generation_results_from_business_data(
                    $business_data,
                    $business_package,
                    $website_draft,
                    $session_id
                );
            }

            $media_complete = is_array($website_draft) && array_key_exists('media_complete', $website_draft)
                ? !empty($website_draft['media_complete'])
                : false;
            $preview_ready = is_array($website_draft) && !empty($website_draft['preview_ready']) && $media_complete;
            $consultation_status = $preview_ready ? 'completed' : 'generating_media';

            $this->update_status_by_session($session_id, $consultation_status);

            $main_html = '';
            if (is_array($website_draft) && !empty($website_draft['content'])) {
                $main_html = $website_draft['content'];
            } elseif (is_string($website_draft)) {
                $main_html = $website_draft;
            }

            $public_website_draft = $website_draft;
            if (!$preview_ready && is_array($public_website_draft)) {
                $public_website_draft['preview_url'] = '';
            }

            return array(
                'content'          => $main_html,
                'main_page'        => $main_html,
                'pages'            => array_keys($website_pages),
                'features'         => array('contact_forms', 'mobile_responsive_design', 'seo_optimization', 'premium_pipeline'),
                'business_summary' => $business_package['business_brief_summary'] ?? '',
                'phase'            => 'complete_website_generated',
                'message'          => $preview_ready
                    ? 'Your premium website preview is ready for review.'
                    : 'Your premium website draft has been assembled. Required media is still generating before preview release.',
                'website'          => array('main_page' => $main_html, 'pages' => $website_pages),
                'downloads'        => array(),
                'business_services'=> array(),
                'website_draft'    => $public_website_draft,
                'qa_report'        => is_array($website_draft) ? ($website_draft['qa_report'] ?? array()) : array(),
                'final_qa_gate'    => is_array($website_draft) ? ($website_draft['final_qa_gate'] ?? array()) : array(),
                'visual_preset'     => is_array($website_draft) ? ($website_draft['visual_preset'] ?? '') : '',
                'publish_ready'    => is_array($website_draft) ? !empty($website_draft['publish_ready']) : false,
                'media_complete'   => $media_complete,
                'preview_ready'    => $preview_ready,
                'consultation_complete' => $preview_ready,
                'status'           => $consultation_status,
            );
        } catch (Exception $e) {
            error_log('VMO run_pipeline_sync Error: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Process consultation data into structured business information
     */
    private function process_consultation_data($consultation_row) {
        // consultation_row is a DB row object; parse stored JSON fields
        $consultation_data = is_object($consultation_row) ? $consultation_row : (object)$consultation_row;
        $client_data = json_decode($consultation_data->consultation_data ?? '{}', true);
        $conversation_logs = json_decode($consultation_data->conversation_log ?? '[]', true);
        $phase_state = json_decode($consultation_data->phase_state ?? '{}', true);
        $website_data = json_decode($consultation_data->website_data ?? '{}', true);
        if (!is_array($client_data)) {
            $client_data = array();
        }
        if (!is_array($conversation_logs)) {
            $conversation_logs = array();
        }
        if (!is_array($phase_state)) {
            $phase_state = array();
        }
        if (!is_array($website_data)) {
            $website_data = array();
        }
        $canonical_context = is_array($client_data['canonical_generation_context'] ?? null)
            ? $client_data['canonical_generation_context']
            : (is_array($phase_state['canonical_generation_context'] ?? null)
                ? $phase_state['canonical_generation_context']
                : (is_array($website_data['canonical_generation_context'] ?? null) ? $website_data['canonical_generation_context'] : array()));

        // Attempt to derive core fields from stored data
        $derived = array(
            'session_id' => $consultation_data->session_id ?? '',
            'user_id' => (int) ($consultation_data->user_id ?? 0),
            'business_name' => $consultation_data->business_name ?: ($client_data['business_name'] ?? 'Your Business'),
            'business_type' => $consultation_data->business_type ?: ($client_data['business_type'] ?? ''),
            'industry' => $client_data['industry'] ?? '',
            'location' => $client_data['location'] ?? '',
            'target_audience' => $client_data['target_audience'] ?? '',
            'services' => $client_data['services'] ?? array(),
            'goals' => $client_data['goals'] ?? array(),
            'brand_values' => $client_data['brand_values'] ?? array(),
            'contact_info' => $client_data['contact_info'] ?? array(),
            'ecommerce' => $client_data['ecommerce'] ?? array(), // { intends_to_sell, sell_on_website, products: [...] }
            'email_roles' => $client_data['email_roles'] ?? array(),
            'email_roles_meta' => $client_data['email_roles_meta'] ?? array(),
            'conversation_context' => $conversation_logs,
            'consultation_snapshot' => $client_data,
            'canonical_generation_context' => $canonical_context,
            'visual_preset' => $canonical_context['selected_visual_preset'] ?? ($website_data['selected_visual_preset'] ?? ''),
        );

        $business_manifest = array();
        $manifest_user_id = (int) ($derived['user_id'] ?? 0);
        $manifest_session_id = (string) ($derived['session_id'] ?? '');
        if ($manifest_user_id > 0 && $manifest_session_id !== '') {
            $session_manifest = get_user_meta($manifest_user_id, 'vmo_business_manifest_' . $manifest_session_id, true);
            $global_manifest = get_user_meta($manifest_user_id, 'vmo_business_manifest', true);
            $business_manifest = is_array($session_manifest) && !empty($session_manifest)
                ? $session_manifest
                : (is_array($global_manifest) ? $global_manifest : array());
        }
        if (!empty($business_manifest)) {
            $derived['business_manifest'] = $business_manifest;
            $manifest_profile = is_array($business_manifest['business_profile'] ?? null) ? $business_manifest['business_profile'] : array();
            $manifest_audience = is_array($business_manifest['target_audience'] ?? null) ? $business_manifest['target_audience'] : array();
            $manifest_offers = is_array($business_manifest['offer_structure'] ?? null) ? $business_manifest['offer_structure'] : array();
            $manifest_ecommerce = is_array($business_manifest['ecommerce_config'] ?? null) ? $business_manifest['ecommerce_config'] : array();
            $manifest_email = is_array($business_manifest['email_system'] ?? null) ? $business_manifest['email_system'] : array();

            foreach (array('business_name', 'business_type', 'industry', 'location') as $manifest_field) {
                if (empty($derived[$manifest_field]) && !empty($manifest_profile[$manifest_field])) {
                    $derived[$manifest_field] = sanitize_text_field((string) $manifest_profile[$manifest_field]);
                }
            }
            if (empty($derived['target_audience']) && !empty($manifest_audience['primary_segments'])) {
                $derived['target_audience'] = implode(', ', array_map('sanitize_text_field', (array) $manifest_audience['primary_segments']));
            }
            if (empty($derived['services']) && !empty($manifest_offers['services'])) {
                $derived['services'] = $this->normalize_string_list($manifest_offers['services']);
            }
            if (empty($derived['email_roles']) && !empty($manifest_email['roles'])) {
                $manifest_roles = array();
                foreach ((array) $manifest_email['roles'] as $role) {
                    if (is_array($role) && !empty($role['address'])) {
                        $manifest_roles[$role['role'] ?? sanitize_key(strtok($role['address'], '@'))] = sanitize_email($role['address']);
                    }
                }
                $derived['email_roles'] = $manifest_roles;
            }
            if (empty($derived['email_roles_meta']) && !empty($manifest_email)) {
                $derived['email_roles_meta'] = array(
                    'provisioning_mode' => sanitize_key((string) ($manifest_email['provisioning_mode'] ?? '')),
                    'provisioning_status' => sanitize_key((string) ($manifest_email['status'] ?? '')),
                    'provider' => sanitize_key((string) ($manifest_email['provider'] ?? '')),
                    'vmo_interface' => 'vmo_email_dashboard',
                );
            }
            if (!empty($manifest_ecommerce)) {
                $derived['ecommerce'] = array_replace_recursive($manifest_ecommerce, is_array($derived['ecommerce'] ?? null) ? $derived['ecommerce'] : array());
                if (!empty($manifest_ecommerce['products']) && empty($derived['ecommerce']['products'])) {
                    $derived['ecommerce']['products'] = $manifest_ecommerce['products'];
                }
                if (!empty($manifest_ecommerce['donation_funds']) && empty($derived['ecommerce']['donation_funds'])) {
                    $derived['ecommerce']['donation_funds'] = $manifest_ecommerce['donation_funds'];
                }
            }
        }

        // ✅ Pull from extracted_information (wizard JS canonical) when ecommerce is missing.
        $extracted = is_array($client_data['extracted_information'] ?? null) ? $client_data['extracted_information'] : array();
        if (empty($derived['business_name']) && !empty($extracted['businessName'])) {
            $derived['business_name'] = sanitize_text_field((string) $extracted['businessName']);
        }
        if (empty($derived['industry']) && !empty($extracted['industry'])) {
            $derived['industry'] = sanitize_text_field((string) $extracted['industry']);
        }
        if (empty($derived['location']) && !empty($extracted['location'])) {
            $derived['location'] = sanitize_text_field((string) $extracted['location']);
        }
        if (empty($derived['target_audience']) && !empty($extracted['targetAudience'])) {
            $derived['target_audience'] = sanitize_text_field((string) $extracted['targetAudience']);
        }
        if (empty($derived['services']) && !empty($extracted['offerings'])) {
            $derived['services'] = $this->normalize_string_list($extracted['offerings']);
        }

        $derived['custom_pages'] = $this->normalize_string_list($extracted['customPages'] ?? ($extracted['custom_pages'] ?? array()));
        $derived['special_features'] = $this->normalize_string_list($extracted['specialFeatures'] ?? ($extracted['special_features'] ?? array()));
        $derived['page_descriptions'] = $this->normalize_page_descriptions($extracted['pageDescriptions'] ?? ($extracted['page_descriptions'] ?? array()));
        $derived['brand'] = $this->build_brand_profile($client_data, $extracted);
        $needs_ecommerce = null;
        if (is_array($extracted)) {
            if (isset($extracted['needsEcommerce']) && is_bool($extracted['needsEcommerce'])) {
                $needs_ecommerce = $extracted['needsEcommerce'];
            } elseif (isset($extracted['ecommerce_needed']) && is_bool($extracted['ecommerce_needed'])) {
                $needs_ecommerce = $extracted['ecommerce_needed'];
            } elseif (isset($extracted['needs']) && is_array($extracted['needs']) && isset($extracted['needs']['ecommerce']) && is_bool($extracted['needs']['ecommerce'])) {
                $needs_ecommerce = $extracted['needs']['ecommerce'];
            }
        }

        // Determine payment collection mode (products vs donations/contributions).
        // This is intentionally conservative: only classify as donations if there's a strong signal.
        $collection_mode = '';
        if (is_array($extracted)) {
            $raw_mode = sanitize_text_field((string) ($extracted['payment_collection_mode'] ?? ($extracted['payments_mode'] ?? ($extracted['ecommerce_mode'] ?? ''))));
            $raw_mode = strtolower(trim($raw_mode));
            if (in_array($raw_mode, array('donation','donations','contributions','contribution','giving','tithes','offering','campaign_donations','campaign','fundraising','fundraiser'), true)) {
                $collection_mode = 'donations';
            } elseif (in_array($raw_mode, array('products','product_sales','store','shop'), true)) {
                $collection_mode = 'products';
            }
        }
        if ($collection_mode === '') {
            $signal = array();
            $signal[] = (string) ($derived['business_type'] ?? '');
            $signal[] = (string) ($derived['industry'] ?? '');
            $signal[] = (string) ($derived['target_audience'] ?? '');
            $signal[] = is_array($derived['services'] ?? null) ? implode(' ', $derived['services']) : (string) ($derived['services'] ?? '');
            $signal_txt = strtolower(implode(' | ', array_filter($signal)));

            $donation_keywords = array(
                'church','ministry','parish','chapel','cathedral','mosque','synagogue','temple','faith','religious',
                'charity','nonprofit','non-profit','ngo','foundation','donation','donations','fundraising','fundraiser','relief','humanitarian','community support','missionary','missions',
                'politician','campaign','candidate','election','contribution','contributions','pac'
            );
            foreach ($donation_keywords as $kw) {
                if ($kw !== '' && strpos($signal_txt, $kw) !== false) {
                    $collection_mode = 'donations';
                    break;
                }
            }
        }
        if ($collection_mode === '') {
            $collection_mode = 'products';
        }

        if (!is_array($derived['ecommerce'] ?? null)) {
            $derived['ecommerce'] = array();
        }

        if ($needs_ecommerce === true) {
            // "needsEcommerce" means "needs to collect payments"; not necessarily selling products.
            if ($collection_mode === 'donations') {
                if (!isset($derived['ecommerce']['collect_payments'])) $derived['ecommerce']['collect_payments'] = true;
            } else {
                if (!isset($derived['ecommerce']['intends_to_sell'])) $derived['ecommerce']['intends_to_sell'] = true;
                if (!isset($derived['ecommerce']['sell_on_website'])) $derived['ecommerce']['sell_on_website'] = true;
            }
        }

        $derived['ecommerce']['collection_mode'] = $collection_mode;

        // Map products_inventory -> ecommerce.products when needed.
        $existing_products = is_array($derived['ecommerce']['products'] ?? null) ? $derived['ecommerce']['products'] : array();
        $products_inventory = is_array($extracted['products_inventory'] ?? null) ? $extracted['products_inventory'] : array();
        if (!$existing_products && $products_inventory) {
            $mapped = array();
            foreach ($products_inventory as $pi) {
                if (!is_array($pi)) continue;
                $name = sanitize_text_field((string) ($pi['name'] ?? ''));
                if ($name === '') continue;
                $desc = (string) ($pi['description'] ?? '');
                $price = isset($pi['price']) ? (float) $pi['price'] : 0.0;
                $currency = sanitize_text_field((string) ($pi['currency'] ?? 'usd'));
                $images = array();
                if (isset($pi['main_image']['url'])) $images[] = esc_url_raw((string) $pi['main_image']['url']);
                if (isset($pi['gallery_images']) && is_array($pi['gallery_images'])) {
                    foreach ($pi['gallery_images'] as $gi) {
                        if (is_array($gi) && isset($gi['url'])) $images[] = esc_url_raw((string) $gi['url']);
                        elseif (is_string($gi)) $images[] = esc_url_raw($gi);
                    }
                }
                $images = array_values(array_filter($images));

                // If variants exist, expand them into separate purchasable items.
                // The store products table doesn't have a variants column, so this keeps
                // variant pricing actionable by generating distinct products per variant.
                $variants = is_array($pi['variants'] ?? null) ? $pi['variants'] : array();
                if ($variants) {
                    foreach ($variants as $v) {
                        if (!is_array($v)) continue;
                        $v_label = sanitize_text_field((string) ($v['name'] ?? ($v['label'] ?? ($v['title'] ?? 'Variant'))));
                        if ($v_label === '') $v_label = 'Variant';
                        $v_name = trim($name . ' — ' . $v_label);
                        $v_slug = sanitize_title($name . '-' . $v_label);
                        $v_desc = (string) ($v['description'] ?? '');
                        $v_price = isset($v['price']) ? (float) $v['price'] : $price;
                        $v_currency = sanitize_text_field((string) ($v['currency'] ?? $currency));
                        $v_sku = sanitize_text_field((string) ($v['sku'] ?? ''));

                        $v_images = $images;
                        if (isset($v['image']['url'])) {
                            $v_images = array(esc_url_raw((string) $v['image']['url']));
                        } elseif (isset($v['image_url'])) {
                            $v_images = array(esc_url_raw((string) $v['image_url']));
                        }
                        $v_images = array_values(array_filter(is_array($v_images) ? $v_images : array()));

                        $mapped[] = array(
                            'name' => $v_name,
                            'slug' => $v_slug,
                            'description' => $v_desc ?: $desc,
                            'summary' => wp_trim_words(wp_strip_all_tags($v_desc ?: $desc), 24, '…'),
                            'price' => $v_price,
                            'currency' => $v_currency ?: 'usd',
                            'sku' => $v_sku,
                            'images' => $v_images,
                            'image' => (string) ($v_images[0] ?? ''),
                            'variant_of' => sanitize_title($name),
                            'variant_label' => $v_label,
                        );
                    }
                } else {
                    $mapped[] = array(
                        'name' => $name,
                        'slug' => sanitize_title($name),
                        'description' => $desc,
                        'summary' => wp_trim_words(wp_strip_all_tags($desc), 24, '…'),
                        'price' => $price,
                        'currency' => $currency ?: 'usd',
                        'images' => $images,
                        'image' => (string) ($images[0] ?? ''),
                    );
                }
            }
            if ($mapped) {
                $derived['ecommerce']['products'] = $mapped;
            }
        }

        // Donation funds (church/charity/campaign) — only when donation mode is detected.
        if (($derived['ecommerce']['collection_mode'] ?? '') === 'donations') {
            $funds = is_array($derived['ecommerce']['donation_funds'] ?? null) ? $derived['ecommerce']['donation_funds'] : array();
            if (!$funds) {
                $signal_txt = strtolower(
                    (string)($derived['business_type'] ?? '') . ' | ' .
                    (string)($derived['industry'] ?? '') . ' | ' .
                    (is_array($derived['services'] ?? null) ? implode(' ', $derived['services']) : (string)($derived['services'] ?? ''))
                );

                if (strpos($signal_txt, 'campaign') !== false || strpos($signal_txt, 'candidate') !== false || strpos($signal_txt, 'election') !== false) {
                    $funds = array(
                        array('name' => 'Campaign Contribution', 'amounts' => array(2500, 5000, 10000)),
                        array('name' => 'Event Support', 'amounts' => array(1000, 2500, 5000)),
                        array('name' => 'Volunteer & Outreach Fund', 'amounts' => array(1500, 3000, 7500)),
                    );
                } elseif (strpos($signal_txt, 'church') !== false || strpos($signal_txt, 'ministry') !== false || strpos($signal_txt, 'parish') !== false || strpos($signal_txt, 'mission') !== false) {
                    $funds = array(
                        array('name' => 'Tithes', 'amounts' => array(2500, 5000, 10000)),
                        array('name' => 'Sunday Offering', 'amounts' => array(1000, 2000, 5000)),
                        array('name' => 'Missions / Missionary Support', 'amounts' => array(2000, 5000, 15000)),
                        array('name' => 'Community Support', 'amounts' => array(1500, 3000, 7500)),
                    );
                } else {
                    // Charity/nonprofit/general donations
                    $funds = array(
                        array('name' => 'General Donation', 'amounts' => array(2000, 5000, 10000)),
                        array('name' => 'Emergency Relief Fund', 'amounts' => array(3000, 7500, 15000)),
                        array('name' => 'Community Support', 'amounts' => array(2500, 5000, 10000)),
                    );
                }
            }
            $derived['ecommerce']['donation_funds'] = $funds;
        }

        // If e-commerce intent exists but we still have no products, seed a small catalog
        // so Phase 6 (catalog prep) and Phase 7 (shop/product pages) are never empty.
        // IMPORTANT: donation-mode sites should NOT be seeded with product SKUs.
        $has_ecommerce_intent = ($needs_ecommerce === true)
            || !empty($derived['ecommerce']['intends_to_sell'])
            || !empty($derived['ecommerce']['sell_on_website'])
            || !empty($derived['ecommerce']['collect_payments'])
            || (($derived['ecommerce']['collection_mode'] ?? '') === 'donations');
        $final_products = is_array($derived['ecommerce']['products'] ?? null) ? $derived['ecommerce']['products'] : array();
        if ($has_ecommerce_intent && !$final_products && (($derived['ecommerce']['collection_mode'] ?? '') !== 'donations')) {
            if ($this->looks_like_vmo_plugin_blueprint($derived)) {
                $derived['ecommerce']['products'] = $this->get_vmo_plan_seed_catalog();
                return $derived;
            }

            $seed = array();
            $services = is_array($derived['services'] ?? null) ? array_values(array_filter($derived['services'])) : array();
            $services = array_slice($services, 0, 3);
            if ($services) {
                $base_prices = array(99.0, 149.0, 249.0);
                foreach ($services as $idx => $svc) {
                    $svc_name = sanitize_text_field((string) $svc);
                    if ($svc_name === '') continue;
                    $pname = $svc_name . ' Package';
                    $seed[] = array(
                        'name' => $pname,
                        'slug' => sanitize_title($pname),
                        'description' => 'A premium ' . $svc_name . ' package designed to deliver real results with a clear scope, fast turnaround, and high-quality outcomes.',
                        'summary' => 'Premium ' . $svc_name . ' package with clear scope and fast turnaround.',
                        'price' => $base_prices[$idx] ?? 99.0,
                        'currency' => 'usd',
                        'images' => array(),
                        'image' => '',
                        'demo_data' => true,
                        'requires_owner_confirmation' => true,
                        'source' => 'demo_seed_catalog',
                    );
                }
            }

            if (!$seed) {
                $seed = array(
                    array(
                        'name' => 'Starter Package',
                        'slug' => 'starter-package',
                        'description' => 'A high-quality starter offer that delivers a fast, polished outcome and sets a strong foundation for what comes next.',
                        'summary' => 'Fast, polished starter offer.',
                        'price' => 99.0,
                        'currency' => 'usd',
                        'images' => array(),
                        'image' => '',
                        'demo_data' => true,
                        'requires_owner_confirmation' => true,
                        'source' => 'demo_seed_catalog',
                    ),
                    array(
                        'name' => 'Growth Package',
                        'slug' => 'growth-package',
                        'description' => 'A growth-focused offer that adds depth, performance, and conversion improvements for customers ready to scale.',
                        'summary' => 'Growth-focused offer for scaling.',
                        'price' => 149.0,
                        'currency' => 'usd',
                        'images' => array(),
                        'image' => '',
                        'demo_data' => true,
                        'requires_owner_confirmation' => true,
                        'source' => 'demo_seed_catalog',
                    ),
                    array(
                        'name' => 'Premium Package',
                        'slug' => 'premium-package',
                        'description' => 'A premium end-to-end offer featuring the highest level of service, deeper customization, and the most complete deliverable set.',
                        'summary' => 'Top-tier, end-to-end premium offer.',
                        'price' => 249.0,
                        'currency' => 'usd',
                        'images' => array(),
                        'image' => '',
                        'demo_data' => true,
                        'requires_owner_confirmation' => true,
                        'source' => 'demo_seed_catalog',
                    ),
                );
            }

            $derived['ecommerce']['products'] = $seed;
        }

        // Normalize ecommerce goal if any payment collection intent detected
        if (!empty($derived['ecommerce']['intends_to_sell']) || !empty($derived['ecommerce']['sell_on_website']) || !empty($derived['ecommerce']['collect_payments']) || (($derived['ecommerce']['collection_mode'] ?? '') === 'donations')) {
            if (!in_array('e-commerce', $derived['goals'])) {
                $derived['goals'][] = 'e-commerce';
            }
        }

        // ✅ Derive the user-APPROVED website design concept (format + visual direction) so generation
        // honors exactly what the user approved during Phase 4. The user is the CEO; deliverables must
        // match the approved plan.
        $recommendations_profile = is_array($client_data['recommendations_profile'] ?? null)
            ? $client_data['recommendations_profile']
            : (is_array($website_data['recommendations_profile'] ?? null) ? $website_data['recommendations_profile'] : array());
        $approvals = is_array($client_data['approvals'] ?? null)
            ? $client_data['approvals']
            : (is_array($website_data['approvals'] ?? null) ? $website_data['approvals'] : array());
        $derived['website_design_concept'] = $this->derive_website_design_concept($recommendations_profile, $approvals, $derived);

        // ✅ Surface competitor website/design research so the design agent + page prompts can benchmark.
        $research_results_src = is_array($client_data['research_results'] ?? null)
            ? $client_data['research_results']
            : (is_array($website_data['research_results'] ?? null) ? $website_data['research_results'] : array());
        $derived['competitor_design_research'] = $this->extract_competitor_design_research($research_results_src);

        return $derived;
    }

    /**
     * Derive the user-approved website design concept from the Phase 4 recommendations profile and
     * approvals. Reads the chosen site_format (single_page|multi_page|hybrid) and design_direction so
     * generation produces exactly what the user approved.
     *
     * @return array { site_format, design_direction, design_choice, refinements[], approved, source }
     */
    private function derive_website_design_concept($recommendations_profile, $approvals, $derived) {
        $concept = array(
            'site_format' => '',
            'design_direction' => '',
            'design_choice' => '',
            'refinements' => array(),
            'approved' => false,
            'source' => 'default',
        );

        $items = array();
        if (is_array($recommendations_profile)) {
            if (isset($recommendations_profile['items']) && is_array($recommendations_profile['items'])) {
                $items = $recommendations_profile['items'];
            } elseif (isset($recommendations_profile['categories']) && is_array($recommendations_profile['categories'])) {
                foreach ($recommendations_profile['categories'] as $cat) {
                    if (is_array($cat) && is_array($cat['items'] ?? null)) {
                        foreach ($cat['items'] as $it) {
                            $items[] = $it;
                        }
                    }
                }
            }
        }

        $user_choices = array();
        $rejected = array();
        if (is_array($approvals) && is_array($approvals['recommendations'] ?? null)) {
            $rec = $approvals['recommendations'];
            if (is_array($rec['user_choices'] ?? null)) {
                $user_choices = $rec['user_choices'];
            }
            if (is_array($rec['rejected'] ?? null)) {
                $rejected = $rec['rejected'];
            }
        }

        $format_map = array('single_page' => 'single_page', 'multi_page' => 'multi_page', 'hybrid' => 'hybrid');

        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $id = (string) ($item['id'] ?? '');
            $label = strtolower((string) ($item['label'] ?? ''));

            $is_format = ($id === 'site_format') || (strpos($label, 'website format') !== false) || (strpos($label, 'site format') !== false);
            $is_direction = ($id === 'design_direction') || (strpos($label, 'design direction') !== false) || (strpos($label, 'visual design') !== false);

            if ($is_format) {
                if (in_array($id, $rejected, true)) {
                    continue;
                }
                $chosen = '';
                if ($id !== '' && isset($user_choices[$id])) {
                    $chosen = (string) $user_choices[$id];
                } elseif (!empty($item['user_choice'])) {
                    $chosen = (string) $item['user_choice'];
                } elseif (!empty($item['default_choice'])) {
                    $chosen = (string) $item['default_choice'];
                }
                $chosen = strtolower(trim($chosen));
                if (isset($format_map[$chosen])) {
                    $concept['site_format'] = $format_map[$chosen];
                    $concept['approved'] = true;
                    $concept['source'] = 'approved_recommendation';
                }
                if (!empty($item['refinement_note'])) {
                    $concept['refinements'][] = sanitize_text_field((string) $item['refinement_note']);
                }
            }

            if ($is_direction) {
                if (in_array($id, $rejected, true)) {
                    continue;
                }
                $direction = trim((string) ($item['recommendation'] ?? ''));
                if ($direction === '') {
                    $direction = trim((string) ($item['why'] ?? ''));
                }
                if ($direction !== '') {
                    $concept['design_direction'] = wp_strip_all_tags($direction);
                }
                if ($id !== '' && isset($user_choices[$id])) {
                    $concept['design_choice'] = (string) $user_choices[$id];
                } elseif (!empty($item['user_choice'])) {
                    $concept['design_choice'] = (string) $item['user_choice'];
                } elseif (!empty($item['default_choice'])) {
                    $concept['design_choice'] = (string) $item['default_choice'];
                }
                if (!empty($item['refinement_note'])) {
                    $concept['refinements'][] = sanitize_text_field((string) $item['refinement_note']);
                }
            }
        }

        // Heuristic fallback if the concept was never captured in the recommendations profile.
        if ($concept['site_format'] === '') {
            $goals = is_array($derived['goals'] ?? null) ? array_map('strval', $derived['goals']) : array();
            $is_ecommerce = in_array('e-commerce', $goals, true)
                || !empty($derived['ecommerce']['intends_to_sell'])
                || !empty($derived['ecommerce']['sell_on_website']);
            $custom_pages = is_array($derived['custom_pages'] ?? null) ? $derived['custom_pages'] : array();
            $services = is_array($derived['services'] ?? null) ? $derived['services'] : array();
            if ($is_ecommerce || count($custom_pages) > 2 || count($services) > 4) {
                $concept['site_format'] = 'multi_page';
            } else {
                $concept['site_format'] = 'single_page';
            }
            $concept['source'] = 'heuristic_default';
        }

        return $concept;
    }

    /**
     * Extract a compact summary of competitor website/design research for use in design prompts.
     */
    private function extract_competitor_design_research($research_results) {
        if (!is_array($research_results)) {
            return '';
        }
        $fragments = array();
        foreach (array('competitor_website_batch_1', 'competitor_website_batch_2', 'competitor_website_batch_3', 'competitor_websites', 'website_analysis', 'design_analysis', 'ux_analysis') as $key) {
            if (empty($research_results[$key])) {
                continue;
            }
            $val = $research_results[$key];
            if (is_string($val)) {
                $fragments[] = trim($val);
            } elseif (is_array($val)) {
                $flat = array();
                array_walk_recursive($val, function ($leaf) use (&$flat) {
                    if (is_string($leaf) && strlen(trim($leaf)) > 8) {
                        $flat[] = trim($leaf);
                    }
                });
                if (!empty($flat)) {
                    $fragments[] = implode(' | ', array_slice($flat, 0, 12));
                }
            }
        }
        if (empty($fragments)) {
            return '';
        }
        $text = preg_replace('/\s+/', ' ', implode(' ', $fragments));
        return function_exists('mb_substr') ? trim(mb_substr($text, 0, 1400)) : trim(substr($text, 0, 1400));
    }

    private function ensure_business_manifest_for_generation($business_data, $business_package, $website_result = array()) {
        $business_data = is_array($business_data) ? $business_data : array();
        $business_package = is_array($business_package) ? $business_package : array();
        $website_result = is_array($website_result) ? $website_result : array();

        $user_id = (int) ($business_data['user_id'] ?? get_current_user_id());
        $session_id = sanitize_text_field((string) ($business_data['session_id'] ?? ''));
        if ($user_id <= 0 || $session_id === '') {
            return $business_data;
        }

        $manifest = get_user_meta($user_id, 'vmo_business_manifest_' . $session_id, true);
        if (!is_array($manifest) || empty($manifest)) {
            if (!class_exists('VMO_Business_Manifest_Generator')) {
                $manifest_file = plugin_dir_path(__FILE__) . 'class-vmo-business-manifest-generator.php';
                if (file_exists($manifest_file)) {
                    require_once $manifest_file;
                }
            }

            if (class_exists('VMO_Business_Manifest_Generator')) {
                try {
                    $generator = new VMO_Business_Manifest_Generator();
                    $research_results = is_array($business_data['research_results'] ?? null) ? $business_data['research_results'] : array();
                    $manifest = $generator->generate_manifest($user_id, $session_id, $business_data, $business_package, $website_result, $research_results);
                    $generator->persist_manifest($user_id, $session_id, $manifest);
                } catch (\Throwable $e) {
                    if (defined('WP_DEBUG') && WP_DEBUG) {
                        error_log('VMO preliminary business manifest failed: ' . $e->getMessage());
                    }
                    $manifest = array();
                }
            }
        }

        if (is_array($manifest) && !empty($manifest)) {
            $business_data['business_manifest'] = $manifest;
            $business_data = $this->apply_business_manifest_to_generation_data($business_data, $manifest);
        }

        return $business_data;
    }

    private function apply_business_manifest_to_generation_data($business_data, $manifest) {
        $business_data = is_array($business_data) ? $business_data : array();
        $manifest = is_array($manifest) ? $manifest : array();

        $profile = is_array($manifest['business_profile'] ?? null) ? $manifest['business_profile'] : array();
        foreach (array('business_name', 'business_type', 'industry', 'location') as $field) {
            if (empty($business_data[$field]) && !empty($profile[$field])) {
                $business_data[$field] = sanitize_text_field((string) $profile[$field]);
            }
        }

        $offers = is_array($manifest['offer_structure'] ?? null) ? $manifest['offer_structure'] : array();
        if (empty($business_data['services']) && !empty($offers['services'])) {
            $business_data['services'] = $this->normalize_string_list($offers['services']);
        }

        $email_system = is_array($manifest['email_system'] ?? null) ? $manifest['email_system'] : array();
        if (empty($business_data['email_roles']) && !empty($email_system['roles'])) {
            $roles = array();
            foreach ((array) $email_system['roles'] as $role) {
                if (is_array($role) && !empty($role['address'])) {
                    $roles[$role['role'] ?? sanitize_key(strtok($role['address'], '@'))] = sanitize_email($role['address']);
                }
            }
            $business_data['email_roles'] = $roles;
        }

        $ecommerce = is_array($manifest['ecommerce_config'] ?? null) ? $manifest['ecommerce_config'] : array();
        if (!empty($ecommerce)) {
            $business_data['ecommerce'] = array_replace_recursive($ecommerce, is_array($business_data['ecommerce'] ?? null) ? $business_data['ecommerce'] : array());
        }

        return $business_data;
    }
    
    /**
     * Process and optimize uploaded media files
     */
    private function process_uploaded_media($session_id) {
        // Resolve numeric consultation ID first
        $row = method_exists($this->consultation_db, 'get_consultation_by_session') ? $this->consultation_db->get_consultation_by_session($session_id) : null;
        $cid = $row ? (int)$row->id : (int)$session_id;
        $media_files = $this->consultation_db->get_consultation_media($cid);
        $processed_media = array();
        
        foreach ($media_files as $media) {
            $url = $media['url'] ?? '';
            if (!$url) { continue; }
            $context = is_array($media['context'] ?? null) ? $media['context'] : array('user_context' => sanitize_text_field((string) ($media['context'] ?? '')));
            $file_type = !empty($context['media_type']) ? sanitize_key((string) $context['media_type']) : sanitize_key((string) ($media['type'] ?? $this->determine_media_type($url)));
            if (!in_array($file_type, array('image', 'video', 'document', 'other'), true)) {
                $file_type = $this->determine_media_type($url);
            }
            $title = $media['title'] ?? basename(parse_url($url, PHP_URL_PATH));
            $attachment_id = isset($context['attachment_id']) ? intval($context['attachment_id']) : 0;
            $classification = $this->classify_consultation_media_asset($media, $context, $file_type, $title);

            $processed_media[] = array(
                'id' => intval($media['id']),
                'type' => $file_type,
                'url' => $url,
                'title' => $title,
                'alt_text' => $classification['alt_text'],
                'usage_context' => $classification['usage_context'],
                'attachment_id' => $attachment_id,
                'context' => $context,
                'roles' => $classification['roles'],
                'business_role' => $classification['business_role'],
                'placement_role' => $classification['placement_role'],
                'page_target' => $classification['page_target'],
                'section_target' => $classification['section_target'],
                'product_id' => $classification['product_id'],
                'product_name' => $classification['product_name'],
                'quality_status' => $classification['quality_status'],
                'classification_confidence' => $classification['classification_confidence'],
                'unassigned' => !empty($classification['unassigned']),
                'media_asset' => $classification['media_asset']
            );
        }
        
        return $processed_media;
    }

    private function normalize_media_roles($roles) {
        if (!is_array($roles)) {
            $roles = preg_split('/[,|;\n]+/', (string) $roles);
        }

        $normalized = array();
        foreach ((array) $roles as $role) {
            if (is_array($role)) {
                $role = $role['role'] ?? $role['name'] ?? '';
            }
            $role = sanitize_key((string) $role);
            if ($role !== '') {
                $normalized[] = $role;
            }
        }

        return array_values(array_unique($normalized));
    }

    private function classify_consultation_media_asset(array $media, array $context, $file_type, $title) {
        $roles = $this->normalize_media_roles($context['roles'] ?? array());
        foreach (array('business_role', 'placement_role', 'intent', 'user_context') as $key) {
            if (!empty($context[$key]) && is_scalar($context[$key])) {
                $roles[] = sanitize_key((string) $context[$key]);
            }
        }
        $roles = array_values(array_unique(array_filter($roles)));

        $usage_context = sanitize_key((string) ($context['placement_role'] ?? $context['business_role'] ?? ''));
        if ($usage_context === '' || $usage_context === 'consultation_media') {
            $usage_context = $this->suggest_media_usage($file_type, $title);
        }
        if (!$roles && $usage_context !== '') {
            $roles[] = $usage_context;
        }

        $product_id = sanitize_key((string) ($context['product_id'] ?? $context['product_slug'] ?? $context['product_key'] ?? ''));
        $product_name = sanitize_text_field((string) ($context['product_name'] ?? $context['product_title'] ?? ''));
        $placement_role = sanitize_key((string) ($context['placement_role'] ?? $context['placement'] ?? $context['intent'] ?? $usage_context));
        $business_role = sanitize_key((string) ($context['business_role'] ?? ($roles[0] ?? $usage_context)));
        $has_explicit_target = !empty($context['schema']) || $product_id !== '' || $product_name !== '' || !empty($context['page_target']) || !empty($context['section_target']) || !empty($context['placement_role']);
        $unassigned = in_array('unassigned_media', $roles, true) || (!$has_explicit_target && in_array($usage_context, array('gallery', 'general_content', 'promotional_video'), true));

        return array(
            'usage_context' => $usage_context,
            'roles' => $roles,
            'business_role' => $business_role,
            'placement_role' => $placement_role,
            'page_target' => sanitize_key((string) ($context['page_target'] ?? '')),
            'section_target' => sanitize_key((string) ($context['section_target'] ?? '')),
            'product_id' => $product_id,
            'product_name' => $product_name,
            'alt_text' => sanitize_text_field((string) ($context['alt_text'] ?? $context['prompt'] ?? $title)),
            'quality_status' => sanitize_key((string) ($context['quality_status'] ?? 'pending_review')),
            'classification_confidence' => $has_explicit_target ? 'high' : ($unassigned ? 'low' : 'medium'),
            'unassigned' => $unassigned,
            'media_asset' => array_merge($context, array(
                'url' => esc_url_raw((string) ($media['url'] ?? '')),
                'title' => sanitize_text_field((string) $title),
                'media_type' => sanitize_key((string) $file_type),
                'roles' => $roles,
                'business_role' => $business_role,
                'placement_role' => $placement_role,
                'product_id' => $product_id,
                'product_name' => $product_name,
                'quality_status' => sanitize_key((string) ($context['quality_status'] ?? 'pending_review')),
                'classification_confidence' => $has_explicit_target ? 'high' : ($unassigned ? 'low' : 'medium'),
                'unassigned' => $unassigned,
            )),
        );
    }

    private function uploaded_media_assignment_gate(array $media_assets) {
        $gate = array(
            'ready' => true,
            'unassigned_uploaded_media_count' => 0,
            'quality_issues' => array(),
        );

        foreach ($media_assets as $asset) {
            if (!is_array($asset)) {
                continue;
            }
            $media_type = sanitize_key((string) ($asset['type'] ?? ''));
            if (!in_array($media_type, array('image', 'video'), true)) {
                continue;
            }
            if (!empty($asset['unassigned']) || (($asset['classification_confidence'] ?? '') === 'low')) {
                $gate['unassigned_uploaded_media_count']++;
                $gate['ready'] = false;
            }
            $quality_status = sanitize_key((string) ($asset['quality_status'] ?? ''));
            if (in_array($quality_status, array('rejected', 'failed', 'needs_replacement', 'blocked'), true)) {
                $gate['quality_issues'][] = array(
                    'media_id' => intval($asset['id'] ?? 0),
                    'title' => sanitize_text_field((string) ($asset['title'] ?? 'Uploaded media')),
                    'status' => $quality_status,
                );
                $gate['ready'] = false;
            }
        }

        return $gate;
    }
    
    /**
     * Determine media type for optimal placement
     */
    private function determine_media_type($file_path) {
        $extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
        
        $image_extensions = array('jpg', 'jpeg', 'png', 'gif', 'webp', 'svg');
        $video_extensions = array('mp4', 'mov', 'avi', 'wmv', 'flv');
        $document_extensions = array('pdf', 'doc', 'docx', 'txt', 'rtf');
        
        if (in_array($extension, $image_extensions)) return 'image';
        if (in_array($extension, $video_extensions)) return 'video';
        if (in_array($extension, $document_extensions)) return 'document';
        
        return 'other';
    }
    
    /**
     * Suggest optimal usage context for media
     */
    private function suggest_media_usage($type, $title) {
        $title_lower = strtolower($title);
        
        if ($type === 'image') {
            if (strpos($title_lower, 'logo') !== false) return 'logo';
            if (strpos($title_lower, 'hero') !== false) return 'hero_image';
            if (strpos($title_lower, 'team') !== false) return 'team_photo';
            if (strpos($title_lower, 'product') !== false) return 'product_image';
            return 'gallery';
        }
        
        if ($type === 'video') {
            if (strpos($title_lower, 'intro') !== false) return 'intro_video';
            if (strpos($title_lower, 'testimonial') !== false) return 'testimonial_video';
            return 'promotional_video';
        }
        
        if ($type === 'document') {
            if (strpos($title_lower, 'menu') !== false) return 'menu';
            if (strpos($title_lower, 'brochure') !== false) return 'brochure';
            return 'downloadable_resource';
        }
        
        return 'general_content';
    }

    private function ensure_ecommerce_product_packets($business_data, $media_assets = array()) {
        if (!is_array($business_data)) {
            return array();
        }

        $ecommerce = is_array($business_data['ecommerce'] ?? null) ? $business_data['ecommerce'] : array();
        $products = is_array($ecommerce['products'] ?? null) ? array_values($ecommerce['products']) : array();
        if (!$products && !$this->ecommerce_generation_required($business_data)) {
            return $business_data;
        }

        $normalized_products = array();
        foreach ($products as $index => $product) {
            if (!is_array($product)) {
                continue;
            }
            $content_packet = $this->build_product_content_packet($product, $business_data, $products, $index);
            $product['content_packet'] = $content_packet;
            $product['product_content_packet'] = $content_packet;
            $media_packet = $this->build_product_media_packet($product, $content_packet, $business_data, is_array($media_assets) ? $media_assets : array(), $index);
            $product['media_packet'] = $media_packet;
            $product['product_media_packet'] = $media_packet;
            $product['product_generation_status'] = array(
                'content_packet_complete' => empty($this->validate_product_content_packet($content_packet)),
                'media_packet_complete' => empty($this->validate_product_media_packet($media_packet)),
                'hard_requirement' => 'product_content_packet_and_product_media_packet_required_before_preview',
            );
            if (empty($product['name'])) {
                $product['name'] = $content_packet['title'];
            }
            if (empty($product['title'])) {
                $product['title'] = $content_packet['title'];
            }
            if (empty($product['description'])) {
                $product['description'] = $content_packet['long_description'];
            }
            if (empty($product['summary'])) {
                $product['summary'] = $content_packet['short_description'];
            }
            $normalized_products[] = $product;
        }

        $business_data['ecommerce'] = $ecommerce;
        $business_data['ecommerce']['products'] = $normalized_products;
        $business_data['ecommerce']['product_generation_packets'] = $this->get_ecommerce_product_generation_packets($business_data);
        $business_data['ecommerce']['product_packet_policy'] = array(
            'required_before_preview' => true,
            'content_packet_required' => true,
            'media_packet_required' => true,
            'media_director_generates_missing_product_visuals' => true,
        );

        return $business_data;
    }

    private function ecommerce_generation_required($business_data) {
        $business_data = is_array($business_data) ? $business_data : array();
        $ecommerce = is_array($business_data['ecommerce'] ?? null) ? $business_data['ecommerce'] : array();
        $serialized = strtolower(wp_json_encode(array(
            'business_type' => $business_data['business_type'] ?? '',
            'industry' => $business_data['industry'] ?? '',
            'ecommerce' => $ecommerce,
            'features' => $business_data['features_needed'] ?? ($business_data['special_features'] ?? array()),
        )));

        return !empty($ecommerce['products'])
            || !empty($ecommerce['intends_to_sell'])
            || !empty($ecommerce['sell_on_website'])
            || !empty($ecommerce['collect_payments'])
            || (bool) preg_match('/ecommerce|e-commerce|shop|store|product|cart|checkout|sell online|online sales/', $serialized);
    }

    private function attach_ecommerce_product_packets_to_package($business_package, $business_data) {
        $business_package = is_array($business_package) ? $business_package : array();
        $packets = $this->get_ecommerce_product_generation_packets($business_data);
        if (!empty($packets['product_count'])) {
            $business_package['product_generation_packets'] = $packets;
            if (!isset($business_package['ecommerce']) || !is_array($business_package['ecommerce'])) {
                $business_package['ecommerce'] = array();
            }
            $business_package['ecommerce']['product_generation_packets'] = $packets;
        }
        return $business_package;
    }

    private function get_ecommerce_product_generation_packets($business_data) {
        $business_data = is_array($business_data) ? $business_data : array();
        $products = is_array($business_data['ecommerce']['products'] ?? null) ? array_values($business_data['ecommerce']['products']) : array();
        $summary = array(
            'version' => '2026-05-product-packets',
            'hard_requirement' => 'complete_product_content_packet_and_product_media_packet_before_ecommerce_preview',
            'required_content_fields' => $this->required_product_content_packet_fields(),
            'required_media_slots' => $this->required_product_media_roles(false),
            'product_count' => count($products),
            'content_packets_complete' => true,
            'media_packets_complete' => true,
            'products' => array(),
            'missing' => array(),
        );

        foreach ($products as $index => $product) {
            if (!is_array($product)) {
                continue;
            }
            $content_packet = is_array($product['content_packet'] ?? null) ? $product['content_packet'] : (is_array($product['product_content_packet'] ?? null) ? $product['product_content_packet'] : array());
            $media_packet = is_array($product['media_packet'] ?? null) ? $product['media_packet'] : (is_array($product['product_media_packet'] ?? null) ? $product['product_media_packet'] : array());
            $content_missing = $this->validate_product_content_packet($content_packet);
            $media_missing = $this->validate_product_media_packet($media_packet);
            if ($content_missing) {
                $summary['content_packets_complete'] = false;
            }
            if ($media_missing) {
                $summary['media_packets_complete'] = false;
            }
            $title = sanitize_text_field((string) ($content_packet['title'] ?? $product['name'] ?? $product['title'] ?? 'Product ' . ($index + 1)));
            $slug = sanitize_title((string) ($product['slug'] ?? $content_packet['slug'] ?? $title));
            $summary['products'][] = array(
                'index' => $index,
                'title' => $title,
                'slug' => $slug,
                'content_packet_complete' => empty($content_missing),
                'media_packet_complete' => empty($media_missing),
                'content_missing' => $content_missing,
                'media_missing' => $media_missing,
                'content_packet' => $content_packet,
                'media_packet' => $media_packet,
            );
            if ($content_missing || $media_missing) {
                $summary['missing'][] = array(
                    'product' => $title,
                    'content_missing' => $content_missing,
                    'media_missing' => $media_missing,
                );
            }
        }

        $summary['ready_before_preview'] = $summary['product_count'] > 0 && $summary['content_packets_complete'] && $summary['media_packets_complete'];
        return $summary;
    }

    private function build_product_content_packet(array $product, array $business_data, array $all_products, $index) {
        $business_name = sanitize_text_field((string) ($business_data['business_name'] ?? 'this business'));
        $industry = sanitize_text_field((string) ($business_data['industry'] ?? $business_data['business_type'] ?? ''));
        $title = $this->first_product_text($product, array('title', 'name', 'product_name', 'label'));
        if ($title === '') {
            $title = 'Featured Product ' . ((int) $index + 1);
        }
        $slug = sanitize_title((string) ($product['slug'] ?? $title));
        $price_text = $this->format_product_price($product);
        $short_description = $this->first_product_text($product, array('short_description', 'description_short', 'summary', 'tagline', 'excerpt'));
        if ($short_description === '') {
            $short_description = $title . ' is a polished ' . ($industry ? $industry . ' ' : '') . 'offer from ' . $business_name . ' built for customers who want quality, clarity, and an easy path to purchase.';
        }
        $long_description = $this->first_product_text($product, array('long_description', 'description_long', 'description', 'details', 'body'));
        if ($long_description === '') {
            $long_description = $title . ' brings together dependable quality, practical value, and a customer-friendly buying experience. It is presented with clear details, transparent fulfillment notes, and checkout-ready information so shoppers can understand the offer before they buy.';
        }
        $story = $this->first_product_text($product, array('product_story', 'story', 'marketing_description', 'sales_copy'));
        if ($story === '') {
            $story = $business_name . ' positions ' . $title . ' as a confident choice for customers who want the result without guesswork. The product story emphasizes trust, usefulness, and the care behind the offer.';
        }

        $benefits = $this->normalize_product_list($product['key_benefits'] ?? ($product['benefits'] ?? ($product['features'] ?? array())));
        if (!$benefits) {
            $benefits = array(
                'Clear value and easy decision-making for shoppers.',
                'Designed to fit the customer need described in the consultation.',
                'Presented with transparent pricing, fulfillment, and checkout details.',
                'Backed by the business brand, support path, and customer-ready store flow.',
            );
        }
        $benefits = array_slice(array_values(array_unique($benefits)), 0, 6);

        $technical_details = $this->normalize_product_detail_map($product['technical_details'] ?? ($product['specifications'] ?? ($product['specs'] ?? array())));
        if (!$technical_details) {
            $technical_details = array_filter(array(
                'Product type' => sanitize_text_field((string) ($product['type'] ?? $product['category'] ?? 'Retail product')),
                'SKU' => sanitize_text_field((string) ($product['sku'] ?? '')),
                'Price' => $price_text,
                'Currency' => strtoupper(sanitize_text_field((string) ($product['currency'] ?? 'USD'))),
            ));
        }

        $relevant_details = array(
            'materials' => $this->first_product_text($product, array('materials', 'material')) ?: 'Not applicable or not specified for this product type.',
            'ingredients' => $this->first_product_text($product, array('ingredients')) ?: 'Not applicable unless required by the product category.',
            'dimensions' => $this->first_product_text($product, array('dimensions', 'size', 'sizing')) ?: 'Confirm final dimensions from the catalog source when applicable.',
            'compatibility' => $this->first_product_text($product, array('compatibility', 'fits', 'compatible_with')) ?: 'Compatible with the intended use case described in the product details.',
            'care' => $this->first_product_text($product, array('care', 'care_instructions')) ?: 'Follow standard care or handling guidance for this product category.',
            'usage' => $this->first_product_text($product, array('usage', 'usage_details', 'how_to_use', 'instructions')) ?: 'Use as described in the product overview and support materials.',
        );

        $variant_descriptions = $this->build_variant_option_descriptions($product);
        $shipping_notes = $this->first_product_text($product, array('shipping_fulfillment_notes', 'shipping_notes', 'fulfillment_notes', 'shipping', 'fulfillment'));
        if ($shipping_notes === '') {
            $shipping_notes = !empty($product['download_url']) || (($product['fulfillment_type'] ?? '') === 'digital')
                ? 'Delivered digitally or fulfilled through the configured VMO delivery workflow after checkout.'
                : 'Fulfillment and shipping are handled through the configured VMO store workflow. Final rates, regions, and timing remain visible to the business before launch approval.';
        }

        $seo_title = $this->first_product_text($product, array('seo_title', 'meta_title'));
        if ($seo_title === '') {
            $seo_title = $this->trim_text($title . ' | ' . $business_name, 60);
        }
        $meta_description = $this->first_product_text($product, array('meta_description', 'seo_description'));
        if ($meta_description === '') {
            $meta_description = $this->trim_text($short_description . ' Shop ' . $title . ' from ' . $business_name . ' with clear product details and secure checkout.', 155);
        }

        $alt_text = $this->build_product_alt_text_map($title, $business_name);
        $faq = $this->build_product_faq($product, $title, $shipping_notes, $variant_descriptions);
        $upsells = $this->build_product_upsell_copy($product, $all_products, $title);
        $offer_schema = $this->build_product_offer_schema($product);
        $product_schema = array(
            '@context' => 'https://schema.org',
            '@type' => 'Product',
            'name' => $title,
            'description' => $meta_description,
            'brand' => array('@type' => 'Brand', 'name' => $business_name),
            'sku' => sanitize_text_field((string) ($product['sku'] ?? $slug)),
        );

        return array(
            'title' => $title,
            'slug' => $slug,
            'short_description' => $short_description,
            'long_description' => $long_description,
            'product_story' => $story,
            'marketing_description' => $story,
            'key_benefits' => $benefits,
            'technical_details' => $technical_details,
            'relevant_details' => $relevant_details,
            'variant_option_descriptions' => $variant_descriptions,
            'shipping_fulfillment_notes' => $shipping_notes,
            'seo_title' => $seo_title,
            'meta_description' => $meta_description,
            'product_schema' => $product_schema,
            'offer_schema' => $offer_schema,
            'media_alt_text' => $alt_text,
            'alt_text' => $alt_text,
            'faq' => $faq,
            'product_faq' => $faq,
            'upsell_cross_sell_copy' => $upsells,
            'generated_by' => 'vmo_ecommerce_product_packet_builder',
            'required_before_preview' => true,
        );
    }

    private function build_product_media_packet(array $product, array $content_packet, array $business_data, array $media_assets, $index) {
        $title = sanitize_text_field((string) ($content_packet['title'] ?? $product['name'] ?? 'Product'));
        $slug = sanitize_title((string) ($content_packet['slug'] ?? $product['slug'] ?? $title));
        $role_urls = $this->collect_product_media_urls_by_role($product, $media_assets);
        $urls = $this->collect_product_media_urls($product, $media_assets);
        $video_required = $this->product_video_needed($product, $business_data);
        $roles = $this->required_product_media_roles($video_required);
        $flat_assets = array();
        $gallery_assets = array();
        $detail_assets = array();

        foreach ($roles as $role_index => $role) {
            $url = $role_urls[$role] ?? ($urls[$role_index] ?? '');
            $asset = $this->build_product_media_asset($product, $content_packet, $business_data, $role, $role_index, $url);
            $flat_assets[$role] = $asset;
            if (strpos($role, 'gallery_image_') === 0) {
                $gallery_assets[] = $asset;
            } elseif (strpos($role, 'detail_image_') === 0) {
                $detail_assets[] = $asset;
            }
        }

        $packet = array(
            'version' => '2026-05-product-media-packet',
            'product_title' => $title,
            'product_slug' => $slug,
            'required_before_preview' => true,
            'media_director_policy' => 'generate_missing_product_visuals_before_preview',
            'required_roles' => $roles,
            'video_required' => $video_required,
            'product_card_image' => $flat_assets['product_card_image'] ?? array(),
            'product_hero_image' => $flat_assets['product_hero_image'] ?? array(),
            'gallery_images' => $gallery_assets,
            'detail_images' => $detail_assets,
            'product_lifestyle_image' => $flat_assets['product_lifestyle_image'] ?? array(),
            'mobile_fallback_image' => $flat_assets['mobile_fallback_image'] ?? array(),
            'video_poster' => $video_required ? ($flat_assets['video_poster'] ?? array()) : array('required' => false, 'reason' => 'No product video requirement inferred.'),
            'assets' => $flat_assets,
        );
        $packet['complete'] = empty($this->validate_product_media_packet($packet));
        return $packet;
    }

    private function required_product_content_packet_fields() {
        return array('title', 'short_description', 'long_description', 'product_story', 'key_benefits', 'technical_details', 'relevant_details', 'variant_option_descriptions', 'shipping_fulfillment_notes', 'seo_title', 'meta_description', 'product_schema', 'offer_schema', 'media_alt_text', 'faq', 'upsell_cross_sell_copy');
    }

    private function required_product_media_roles($include_video = false) {
        $roles = array('product_card_image', 'product_hero_image', 'gallery_image_1', 'gallery_image_2', 'gallery_image_3', 'detail_image_1', 'detail_image_2', 'product_lifestyle_image', 'mobile_fallback_image');
        if ($include_video) {
            $roles[] = 'video_poster';
        }
        return $roles;
    }

    private function validate_product_content_packet($packet) {
        $packet = is_array($packet) ? $packet : array();
        $missing = array();
        foreach ($this->required_product_content_packet_fields() as $field) {
            $value = $packet[$field] ?? null;
            if (in_array($field, array('key_benefits', 'technical_details', 'relevant_details', 'variant_option_descriptions', 'product_schema', 'offer_schema', 'media_alt_text', 'faq', 'upsell_cross_sell_copy'), true)) {
                if (!is_array($value) || empty($value)) {
                    $missing[] = $field;
                }
                continue;
            }
            if (!is_string($value) || trim($value) === '') {
                $missing[] = $field;
            }
        }
        return array_values(array_unique($missing));
    }

    private function validate_product_media_packet($packet) {
        $packet = is_array($packet) ? $packet : array();
        $missing = array();
        $required_roles = is_array($packet['required_roles'] ?? null) ? $packet['required_roles'] : $this->required_product_media_roles(!empty($packet['video_required']));
        $assets = is_array($packet['assets'] ?? null) ? $packet['assets'] : array();
        foreach ($required_roles as $role) {
            $role = sanitize_key((string) $role);
            $asset = is_array($assets[$role] ?? null) ? $assets[$role] : array();
            if (!$asset) {
                $missing[] = $role;
                continue;
            }
            $has_prompt_or_url = !empty($asset['url']) || !empty($asset['prompt']);
            if (empty($asset['manifest_asset_id']) || empty($asset['alt_text']) || !$has_prompt_or_url || empty($asset['required_before_preview'])) {
                $missing[] = $role;
            }
        }
        if (empty($packet['gallery_images']) || !is_array($packet['gallery_images']) || count($packet['gallery_images']) < 3) {
            $missing[] = 'gallery_images';
        }
        if (empty($packet['detail_images']) || !is_array($packet['detail_images']) || count($packet['detail_images']) < 2) {
            $missing[] = 'detail_images';
        }
        return array_values(array_unique($missing));
    }

    private function first_product_text(array $product, array $keys) {
        foreach ($keys as $key) {
            if (!array_key_exists($key, $product)) {
                continue;
            }
            $value = $product[$key];
            if (is_array($value)) {
                $value = implode(', ', array_filter(array_map('strval', $value)));
            }
            $text = trim(sanitize_textarea_field((string) $value));
            if ($text !== '') {
                return $text;
            }
        }
        return '';
    }

    private function normalize_product_list($value) {
        $items = array();
        if (is_array($value)) {
            foreach ($value as $key => $item) {
                if (is_array($item)) {
                    $item = $item['description'] ?? $item['label'] ?? $item['name'] ?? wp_json_encode($item);
                }
                $text = trim(sanitize_text_field((string) $item));
                if ($text !== '') {
                    $items[] = $text;
                }
            }
        } else {
            $parts = preg_split('/[\n;|]+/', (string) $value);
            foreach ((array) $parts as $part) {
                $text = trim(sanitize_text_field($part));
                if ($text !== '') {
                    $items[] = $text;
                }
            }
        }
        return array_values(array_unique($items));
    }

    private function normalize_product_detail_map($value) {
        $details = array();
        if (is_array($value)) {
            foreach ($value as $key => $item) {
                if (is_array($item)) {
                    $label = sanitize_text_field((string) ($item['label'] ?? $item['name'] ?? $key));
                    $detail = sanitize_text_field((string) ($item['value'] ?? $item['description'] ?? wp_json_encode($item)));
                } else {
                    $label = is_string($key) ? sanitize_text_field($key) : 'Detail ' . ((int) $key + 1);
                    $detail = sanitize_text_field((string) $item);
                }
                if ($label !== '' && $detail !== '') {
                    $details[$label] = $detail;
                }
            }
        }
        return $details;
    }

    private function build_variant_option_descriptions(array $product) {
        $variants = is_array($product['variants'] ?? null) ? $product['variants'] : (is_array($product['options'] ?? null) ? $product['options'] : array());
        $descriptions = array();
        foreach ($variants as $index => $variant) {
            if (is_array($variant)) {
                $name = sanitize_text_field((string) ($variant['name'] ?? $variant['label'] ?? 'Option ' . ($index + 1)));
                $desc = sanitize_textarea_field((string) ($variant['description'] ?? $variant['value'] ?? $variant['price_text'] ?? ''));
                $descriptions[] = trim($name . ($desc !== '' ? ': ' . $desc : ''));
            } else {
                $text = sanitize_text_field((string) $variant);
                if ($text !== '') {
                    $descriptions[] = $text;
                }
            }
        }
        if (!$descriptions) {
            $descriptions[] = 'Standard configuration: includes the core product exactly as described.';
        }
        return array_values(array_unique($descriptions));
    }

    private function build_product_alt_text_map($title, $business_name) {
        $title = sanitize_text_field((string) $title);
        $business_name = sanitize_text_field((string) $business_name);
        return array(
            'product_card_image' => $title . ' product card image for ' . $business_name,
            'product_hero_image' => $title . ' hero product image with clear visual detail',
            'gallery_image_1' => $title . ' gallery image showing the primary angle',
            'gallery_image_2' => $title . ' gallery image showing alternate angle or packaging',
            'gallery_image_3' => $title . ' gallery image showing scale and presentation',
            'detail_image_1' => $title . ' detail image showing materials and finish',
            'detail_image_2' => $title . ' close detail image showing quality and texture',
            'product_lifestyle_image' => $title . ' lifestyle image showing intended use',
            'mobile_fallback_image' => $title . ' mobile-safe product image crop',
            'video_poster' => $title . ' product video poster frame',
        );
    }

    private function build_product_faq(array $product, $title, $shipping_notes, array $variant_descriptions) {
        $faq = is_array($product['faq'] ?? null) ? $product['faq'] : array();
        $normalized = array();
        foreach ($faq as $item) {
            if (is_array($item)) {
                $question = sanitize_text_field((string) ($item['question'] ?? $item['q'] ?? ''));
                $answer = sanitize_textarea_field((string) ($item['answer'] ?? $item['a'] ?? ''));
                if ($question !== '' && $answer !== '') {
                    $normalized[] = array('question' => $question, 'answer' => $answer);
                }
            }
        }
        if (!$normalized) {
            $normalized = array(
                array('question' => 'Who is ' . $title . ' best for?', 'answer' => $title . ' is best for customers who want the product benefits described above with a clear checkout and fulfillment path.'),
                array('question' => 'Are there options or variants?', 'answer' => implode(' ', array_slice($variant_descriptions, 0, 3))),
                array('question' => 'How is this fulfilled?', 'answer' => $shipping_notes),
            );
        }
        return array_slice($normalized, 0, 6);
    }

    private function build_product_upsell_copy(array $product, array $all_products, $title) {
        $existing = $this->normalize_product_list($product['upsell_cross_sell_copy'] ?? ($product['upsells'] ?? array()));
        if ($existing) {
            return array_slice($existing, 0, 4);
        }
        $suggestions = array();
        foreach ($all_products as $candidate) {
            if (!is_array($candidate)) {
                continue;
            }
            $candidate_title = $this->first_product_text($candidate, array('title', 'name', 'product_name'));
            if ($candidate_title !== '' && strtolower($candidate_title) !== strtolower((string) $title)) {
                $suggestions[] = 'Pairs well with ' . $candidate_title . ' for customers comparing related options.';
            }
        }
        if (!$suggestions) {
            $suggestions[] = 'Add complementary products or services from the catalog to increase average order value.';
            $suggestions[] = 'Recommend the best-fit next purchase after customers choose ' . $title . '.';
        }
        return array_slice($suggestions, 0, 4);
    }

    private function build_product_offer_schema(array $product) {
        $price = $this->product_price_number($product);
        $currency = strtoupper(sanitize_text_field((string) ($product['currency'] ?? 'USD')));
        $availability = !empty($product['out_of_stock']) ? 'https://schema.org/OutOfStock' : 'https://schema.org/InStock';
        return array_filter(array(
            '@type' => 'Offer',
            'price' => $price > 0 ? (string) $price : null,
            'priceCurrency' => $currency,
            'availability' => $availability,
            'url' => sanitize_text_field((string) ($product['url'] ?? '')),
        ));
    }

    private function product_price_number(array $product) {
        if (isset($product['price_cents']) && is_numeric($product['price_cents'])) {
            return round(((float) $product['price_cents']) / 100, 2);
        }
        foreach (array('price', 'amount', 'regular_price') as $key) {
            if (isset($product[$key]) && is_numeric($product[$key])) {
                return round((float) $product[$key], 2);
            }
            if (isset($product[$key]) && preg_match('/([0-9]+(?:\.[0-9]{1,2})?)/', (string) $product[$key], $m)) {
                return round((float) $m[1], 2);
            }
        }
        return 0;
    }

    private function format_product_price(array $product) {
        $price = $this->product_price_number($product);
        $currency = strtoupper(sanitize_text_field((string) ($product['currency'] ?? 'USD')));
        return $price > 0 ? $currency . ' ' . number_format($price, 2) : 'Price configured in checkout.';
    }

    private function collect_product_media_urls(array $product, array $media_assets) {
        $urls = array();
        $role_urls = $this->collect_product_media_urls_by_role($product, $media_assets);
        foreach ($this->required_product_media_roles($this->product_video_needed($product, array())) as $role) {
            if (!empty($role_urls[$role])) {
                $urls[] = $role_urls[$role];
            }
        }

        foreach (array('image', 'main_image', 'image_url', 'thumbnail', 'hero_image', 'mobile_fallback_image') as $key) {
            if (!empty($product[$key]) && is_string($product[$key])) {
                $urls[] = esc_url_raw($product[$key]);
            }
        }
        foreach (array('images', 'gallery_images', 'media') as $key) {
            if (!is_array($product[$key] ?? null)) {
                continue;
            }
            foreach ($product[$key] as $item) {
                $url = is_array($item) ? (string) ($item['url'] ?? $item['src'] ?? '') : (string) $item;
                if ($url !== '') {
                    $urls[] = esc_url_raw($url);
                }
            }
        }

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

    private function collect_product_media_urls_by_role(array $product, array $media_assets) {
        $role_urls = array();
        $this->assign_product_media_url($role_urls, 'product_card_image', $product['image'] ?? ($product['main_image'] ?? ($product['image_url'] ?? ($product['thumbnail'] ?? ''))));
        $this->assign_product_media_url($role_urls, 'product_hero_image', $product['hero_image'] ?? ($product['main_image'] ?? ''));
        $this->assign_product_media_url($role_urls, 'mobile_fallback_image', $product['mobile_fallback_image'] ?? '');

        $gallery_index = 1;
        foreach (array('images', 'gallery_images', 'media') as $key) {
            if (!is_array($product[$key] ?? null)) {
                continue;
            }
            foreach ($product[$key] as $item) {
                $url = is_array($item) ? (string) ($item['url'] ?? $item['src'] ?? '') : (string) $item;
                $role = is_array($item) ? sanitize_key((string) ($item['placement_role'] ?? $item['role'] ?? '')) : '';
                if ($role === '') {
                    $role = $gallery_index <= 3 ? 'gallery_image_' . $gallery_index : 'detail_image_' . max(1, $gallery_index - 3);
                }
                $this->assign_product_media_url($role_urls, $role, $url);
                $gallery_index++;
            }
        }

        foreach ($media_assets as $asset) {
            if (!is_array($asset) || sanitize_key((string) ($asset['type'] ?? '')) !== 'image') {
                continue;
            }
            if (!$this->uploaded_media_asset_targets_product($asset, $product)) {
                continue;
            }
            $role = $this->product_media_role_from_asset($asset);
            if ($role === '') {
                continue;
            }
            $this->assign_product_media_url($role_urls, $role, $asset['url'] ?? '');
        }

        return $role_urls;
    }

    private function assign_product_media_url(array &$role_urls, $role, $url) {
        $role = sanitize_key((string) $role);
        $url = esc_url_raw((string) $url);
        if ($role === '' || $url === '' || !empty($role_urls[$role])) {
            return;
        }
        $role_urls[$role] = $url;
    }

    private function product_media_role_from_asset(array $asset) {
        $valid_roles = $this->required_product_media_roles(true);
        $candidates = array(
            $asset['placement_role'] ?? '',
            $asset['business_role'] ?? '',
            $asset['usage_context'] ?? '',
        );
        foreach ((array) ($asset['roles'] ?? array()) as $role) {
            $candidates[] = $role;
        }

        $aliases = array(
            'product_image' => 'product_card_image',
            'product_media' => 'product_card_image',
            'main_product_image' => 'product_card_image',
            'hero_image' => 'product_hero_image',
            'product_gallery' => 'gallery_image_1',
            'gallery_image' => 'gallery_image_1',
            'detail_image' => 'detail_image_1',
            'lifestyle_image' => 'product_lifestyle_image',
            'product_video' => 'video_poster',
        );

        foreach ($candidates as $candidate) {
            $candidate = sanitize_key((string) $candidate);
            if (in_array($candidate, $valid_roles, true)) {
                return $candidate;
            }
            if (!empty($aliases[$candidate])) {
                return $aliases[$candidate];
            }
        }

        return '';
    }

    private function product_identity_tokens(array $product) {
        $tokens = array();
        foreach (array('id', 'product_id', 'sku', 'slug', 'name', 'title', 'product_name') as $key) {
            if (empty($product[$key]) || is_array($product[$key])) {
                continue;
            }
            $value = trim((string) $product[$key]);
            if ($value === '') {
                continue;
            }
            $tokens[] = strtolower($value);
            $tokens[] = sanitize_title($value);
            $tokens[] = sanitize_key($value);
        }
        return array_values(array_unique(array_filter($tokens)));
    }

    private function uploaded_media_asset_targets_product(array $asset, array $product) {
        $product_tokens = $this->product_identity_tokens($product);
        if (!$product_tokens) {
            return false;
        }

        $asset_tokens = array();
        foreach (array('product_id', 'product_name', 'title', 'user_label') as $key) {
            if (empty($asset[$key]) || is_array($asset[$key])) {
                continue;
            }
            $value = trim((string) $asset[$key]);
            if ($value === '') {
                continue;
            }
            $asset_tokens[] = strtolower($value);
            $asset_tokens[] = sanitize_title($value);
            $asset_tokens[] = sanitize_key($value);
        }

        $context = is_array($asset['context'] ?? null) ? $asset['context'] : array();
        foreach (array('product_id', 'product_name', 'product_title', 'product_slug', 'user_label') as $key) {
            if (!empty($context[$key]) && !is_array($context[$key])) {
                $value = trim((string) $context[$key]);
                $asset_tokens[] = strtolower($value);
                $asset_tokens[] = sanitize_title($value);
                $asset_tokens[] = sanitize_key($value);
            }
        }

        $asset_tokens = array_values(array_unique(array_filter($asset_tokens)));
        if (!$asset_tokens) {
            return false;
        }

        foreach ($asset_tokens as $asset_token) {
            foreach ($product_tokens as $product_token) {
                if ($asset_token === $product_token || ($product_token !== '' && strpos($asset_token, $product_token) !== false)) {
                    return true;
                }
            }
        }

        return false;
    }

    private function product_video_needed(array $product, array $business_data) {
        if (!empty($product['video_required']) || !empty($product['requires_video']) || !empty($product['video']) || !empty($product['videos'])) {
            return true;
        }
        $serialized = strtolower(wp_json_encode(array($product, $business_data['industry'] ?? '', $business_data['business_type'] ?? '')));
        return (bool) preg_match('/course|class|demo|demonstration|fitness|equipment|motion|animated|tutorial|installation/', $serialized);
    }

    private function build_product_media_asset(array $product, array $content_packet, array $business_data, $role, $index, $url = '') {
        $role = sanitize_key((string) $role);
        $title = sanitize_text_field((string) ($content_packet['title'] ?? $product['name'] ?? 'Product'));
        $slug = sanitize_title((string) ($content_packet['slug'] ?? $product['slug'] ?? $title));
        $business_name = sanitize_text_field((string) ($business_data['business_name'] ?? 'this business'));
        $alt_map = is_array($content_packet['media_alt_text'] ?? null) ? $content_packet['media_alt_text'] : $this->build_product_alt_text_map($title, $business_name);
        $manifest_asset_id = sanitize_key('product_' . $slug . '_' . $role);
        $prompt = $this->product_media_prompt_for_role($role, $title, $business_name, $business_data, $index);
        $url = esc_url_raw((string) $url);

        return array(
            'role' => $role,
            'type' => 'image',
            'manifest_asset_id' => $manifest_asset_id,
            'media_id' => 'manifest_' . $manifest_asset_id,
            'required_before_preview' => true,
            'source' => $url !== '' ? 'provided_user_media' : 'media_director_generation',
            'status' => $url !== '' ? 'provided' : 'needs_generation',
            'url' => $url,
            'prompt' => $prompt,
            'alt_text' => sanitize_text_field((string) ($alt_map[$role] ?? ($title . ' product image'))),
            'product_title' => $title,
            'product_slug' => $slug,
            'placement' => $role,
            'group' => 'product_media_packet',
            'desktop_variant_required' => !in_array($role, array('mobile_fallback_image'), true),
            'mobile_variant_required' => in_array($role, array('product_hero_image', 'mobile_fallback_image', 'product_card_image'), true),
            'poster_required' => $role === 'video_poster',
        );
    }

    private function product_media_prompt_for_role($role, $title, $business_name, array $business_data, $index) {
        $industry = sanitize_text_field((string) ($business_data['industry'] ?? $business_data['business_type'] ?? ''));
        $base = $title . ' for ' . $business_name . ($industry ? ' in ' . $industry : '');
        $map = array(
            'product_card_image' => 'Premium ecommerce product card image of ' . $base . ', clean studio composition, accurate product focus, white-space safe for card layout',
            'product_hero_image' => 'Large inspectable hero image of ' . $base . ', editorial product lighting, clear subject, premium store detail',
            'gallery_image_1' => 'Product gallery image one for ' . $base . ', primary angle, clean background, realistic product detail',
            'gallery_image_2' => 'Product gallery image two for ' . $base . ', alternate angle or packaging view, premium retail photography',
            'gallery_image_3' => 'Product gallery image three for ' . $base . ', scale and context view, polished ecommerce presentation',
            'detail_image_1' => 'Close detail image for ' . $base . ', materials, texture, finish, quality cues, macro-style clarity',
            'detail_image_2' => 'Second detail image for ' . $base . ', functional details, craftsmanship, compatibility or usage detail',
            'product_lifestyle_image' => 'Lifestyle image of ' . $base . ' in use by the intended customer, natural setting, brand-ready, conversion-focused',
            'mobile_fallback_image' => 'Mobile-safe vertical crop of ' . $base . ', centered subject, readable at small sizes, no cropped text',
            'video_poster' => 'Clean poster frame for a short product demonstration video of ' . $base . ', motion-ready composition, clear focal product',
        );
        return $map[$role] ?? ('Premium product visual for ' . $base . ', role ' . sanitize_text_field($role) . ', asset ' . ((int) $index + 1));
    }

    private function render_product_media_slot($asset, $class_name = '') {
        $asset = is_array($asset) ? $asset : array();
        $class_name = trim((string) $class_name);
        $alt = sanitize_text_field((string) ($asset['alt_text'] ?? 'Product image'));
        $media_id = sanitize_text_field((string) ($asset['media_id'] ?? ''));
        $manifest_asset_id = sanitize_key((string) ($asset['manifest_asset_id'] ?? ''));
        $url = esc_url_raw((string) ($asset['url'] ?? ''));
        if ($url !== '') {
            return '<img class="' . esc_attr($class_name) . '" src="' . esc_url($url) . '" alt="' . esc_attr($alt) . '" loading="lazy" data-media-id="' . esc_attr($media_id) . '" data-vmo-manifest-asset="' . esc_attr($manifest_asset_id) . '" />';
        }
        $prompt = sanitize_textarea_field((string) ($asset['prompt'] ?? $alt));
        return '<div class="' . esc_attr(trim($class_name . ' vmo-media-placeholder vmo-media-placeholder-image')) . '" data-type="image" data-media-id="' . esc_attr($media_id) . '" data-vmo-manifest-asset="' . esc_attr($manifest_asset_id) . '" data-vmo-generate="true" data-vmo-prompt="' . esc_attr($prompt) . '" aria-label="' . esc_attr($alt) . '"></div>';
    }

    private function render_packet_list($items) {
        $items = $this->normalize_product_list($items);
        if (!$items) {
            return '<ul><li>Details are included in the product packet.</li></ul>';
        }
        return '<ul>' . implode('', array_map(function($item) { return '<li>' . esc_html($item) . '</li>'; }, $items)) . '</ul>';
    }

    private function render_packet_detail_map($items) {
        $items = is_array($items) ? $items : array();
        if (!$items) {
            return '<dl><dt>Details</dt><dd>Included in the product packet.</dd></dl>';
        }
        $html = '<dl>';
        foreach ($items as $key => $value) {
            if (is_array($value)) {
                $value = $value['value'] ?? $value['description'] ?? wp_json_encode($value);
            }
            $label = is_string($key) ? $key : ('Detail ' . ((int) $key + 1));
            $html .= '<dt>' . esc_html($label) . '</dt><dd>' . esc_html((string) $value) . '</dd>';
        }
        return $html . '</dl>';
    }

    private function render_packet_faq($faq) {
        $faq = is_array($faq) ? $faq : array();
        if (!$faq) {
            return '<div class="pd-faq"><details open><summary>What should customers know?</summary><p>The product packet includes buyer-ready details, fulfillment notes, and checkout guidance.</p></details></div>';
        }
        $html = '<div class="pd-faq">';
        foreach ($faq as $item) {
            if (!is_array($item)) {
                continue;
            }
            $question = sanitize_text_field((string) ($item['question'] ?? $item['q'] ?? 'Question'));
            $answer = sanitize_textarea_field((string) ($item['answer'] ?? $item['a'] ?? ''));
            if ($answer === '') {
                continue;
            }
            $html .= '<details><summary>' . esc_html($question) . '</summary><p>' . esc_html($answer) . '</p></details>';
        }
        return $html . '</div>';
    }

    private function trim_text($text, $limit) {
        $text = trim(preg_replace('/\s+/', ' ', sanitize_textarea_field((string) $text)));
        if (function_exists('mb_strlen') && mb_strlen($text) > $limit) {
            return rtrim(mb_substr($text, 0, max(0, $limit - 3))) . '...';
        }
        if (strlen($text) > $limit) {
            return rtrim(substr($text, 0, max(0, $limit - 1))) . '...';
        }
        return $text;
    }
    
    /**
     * Generate comprehensive business package
     * ✅ BULLETPROOF: Wrapped in try-catch with fallback to prevent HTTP 500 errors
     */
    private function generate_business_package($business_data) {
        try {
            $package = array();
            $session_id = (string) ($business_data['session_id'] ?? '');

            // Prefer the canonical wizard bridge so the live Phase 7 flow reaches
            // the full consultation-driven provisioning pipeline.
            if (method_exists($this->business_wizard, 'generate_comprehensive_business_package_from_business_data')) {
                $package = $this->business_wizard->generate_comprehensive_business_package_from_business_data($business_data, $session_id);
            } elseif (method_exists($this->business_wizard, 'generate_comprehensive_business_package_simple')) {
                $package = $this->business_wizard->generate_comprehensive_business_package_simple($business_data, $session_id);
            } else {
                // Generate a basic package locally as fallback
                $package = $this->generate_basic_business_package($business_data);
            }

            if (function_exists('vmo_get_agent_orchestrator')) {
                $user_id = (int) ($business_data['user_id'] ?? get_current_user_id());

                if ($user_id > 0 && $session_id !== '') {
                    $responses = $this->build_orchestration_responses($business_data);
                    $consultation_state = $this->build_orchestration_consultation_state($business_data);
                    $orchestrated_package = vmo_get_agent_orchestrator()->build_execution_package(
                        $user_id,
                        $session_id,
                        $responses,
                        array(),
                        is_array($package) ? $package : array(),
                        $consultation_state
                    );
                    if (!empty($orchestrated_package)) {
                        $package = array_merge(is_array($package) ? $package : array(), $orchestrated_package);
                        $package['orchestration_enabled'] = true;
                    }
                }
            }

            return $package;
        } catch (\Throwable $e) {
            error_log('VMO generate_business_package error: ' . $e->getMessage());
            // Return a basic package instead of crashing
            return $this->generate_basic_business_package($business_data);
        }
    }
    
    /**
     * ✅ NEW: Generate basic business package (fallback when enhanced method fails)
     */
    private function generate_basic_business_package($business_data) {
        $business_name = $business_data['business_name'] ?? 'Your Business';
        $industry = $business_data['industry'] ?? 'your industry';
        $location = $business_data['location'] ?? 'your location';
        
        return array(
            'business_strategy' => array(
                'summary' => "Business strategy for {$business_name} in {$industry}. See detailed recommendations in your dashboard.",
                'generated_at' => current_time('mysql')
            ),
            'marketing_plan' => array(
                'summary' => "Marketing plan focused on local {$location} market with digital presence.",
                'generated_at' => current_time('mysql')
            ),
            'operational_plan' => array(
                'summary' => "Operational structure for smooth business execution.",
                'generated_at' => current_time('mysql')
            ),
            'success_metrics' => array(
                'summary' => "Key performance indicators to track growth.",
                'generated_at' => current_time('mysql')
            ),
            'is_fallback' => true
        );
    }

    private function build_orchestration_responses($business_data) {
        $services = is_array($business_data['services'] ?? null) ? $business_data['services'] : array();
        $goals = is_array($business_data['goals'] ?? null) ? $business_data['goals'] : array();
        $brand_values = is_array($business_data['brand_values'] ?? null) ? $business_data['brand_values'] : array();

        return array(
            'business_name' => $business_data['business_name'] ?? 'Your Business',
            'business_type' => $business_data['business_type'] ?? '',
            'industry' => $business_data['industry'] ?? '',
            'location' => $business_data['location'] ?? '',
            'target_audience' => is_array($business_data['target_audience'] ?? null)
                ? implode(', ', $business_data['target_audience'])
                : (string) ($business_data['target_audience'] ?? ''),
            'business_goals' => implode(', ', $goals),
            'style_preferences' => implode(', ', $brand_values),
            'features_needed' => implode(', ', array_filter(array(
                !empty($business_data['ecommerce']['collect_payments']) ? 'payments' : '',
                !empty($business_data['ecommerce']['intends_to_sell']) ? 'shop' : '',
            ))),
            'inspiration_websites' => is_array($business_data['consultation_snapshot']['extracted_information']['inspirationWebsites'] ?? null)
                ? $business_data['consultation_snapshot']['extracted_information']['inspirationWebsites']
                : array(),
            'uploaded_documents' => is_array($business_data['consultation_snapshot']['documents'] ?? null)
                ? $business_data['consultation_snapshot']['documents']
                : array(),
            'strategy' => array(
                'Core Offerings' => implode(', ', $services),
                'Primary Goals' => implode(', ', $goals),
            ),
        );
    }

    private function build_orchestration_consultation_state($business_data) {
        $snapshot = is_array($business_data['consultation_snapshot'] ?? null) ? $business_data['consultation_snapshot'] : array();
        $extracted = is_array($snapshot['extracted_information'] ?? null) ? $snapshot['extracted_information'] : array();
        $ecommerce = is_array($business_data['ecommerce'] ?? null) ? $business_data['ecommerce'] : array();
        $services = is_array($business_data['services'] ?? null) ? $business_data['services'] : array();

        return array(
            'business_profile' => array(
                'identity' => array(
                    'business_name' => $business_data['business_name'] ?? '',
                    'business_type' => $business_data['business_type'] ?? '',
                    'industry' => $business_data['industry'] ?? '',
                    'hq_location' => $business_data['location'] ?? '',
                ),
                'audience' => array(
                    'primary_segments' => is_array($business_data['target_audience'] ?? null)
                        ? $business_data['target_audience']
                        : array_filter(array((string) ($business_data['target_audience'] ?? ''))),
                ),
                'web_requirements' => array(
                    'reference_sites' => is_array($extracted['inspirationWebsites'] ?? null) ? $extracted['inspirationWebsites'] : array(),
                    'ecommerce' => !empty($ecommerce['collect_payments']) || !empty($ecommerce['intends_to_sell']) || !empty($ecommerce['sell_on_website']),
                    'donations' => (($ecommerce['collection_mode'] ?? '') === 'donations'),
                ),
            ),
            'extracted_information' => array_merge($extracted, array(
                'businessName' => $business_data['business_name'] ?? '',
                'businessType' => $business_data['business_type'] ?? '',
                'industry' => $business_data['industry'] ?? '',
                'location' => $business_data['location'] ?? '',
                'targetAudience' => is_array($business_data['target_audience'] ?? null)
                    ? implode(', ', $business_data['target_audience'])
                    : (string) ($business_data['target_audience'] ?? ''),
                'businessGoals' => is_array($business_data['goals'] ?? null) ? implode(', ', $business_data['goals']) : '',
                'offerings' => $services,
                'needsEcommerce' => !empty($ecommerce['collect_payments']) || !empty($ecommerce['intends_to_sell']) || !empty($ecommerce['sell_on_website']),
                'payment_collection_mode' => $ecommerce['collection_mode'] ?? 'inquiry',
            )),
            'documents' => is_array($snapshot['documents'] ?? null) ? $snapshot['documents'] : array(),
            'media_files' => array(),
            'consultation_flow' => array(
                'current_phase_index' => 1,
                'status' => 'completed',
            ),
            'approvals' => array(
                'phase1_profile_confirmed' => true,
            ),
        );
    }

    private function normalize_string_list($value) {
        $items = array();

        if (is_array($value)) {
            array_walk_recursive($value, function($item) use (&$items) {
                if (is_scalar($item)) {
                    $items[] = sanitize_text_field((string) $item);
                }
            });
        } elseif (is_string($value)) {
            $items = preg_split('/[\r\n,]+/', $value);
        }

        $normalized = array();
        $seen = array();
        foreach ($items as $item) {
            $text = trim((string) $item);
            if ($text === '') continue;
            $key = strtolower($text);
            if (isset($seen[$key])) continue;
            $seen[$key] = true;
            $normalized[] = $text;
        }

        return $normalized;
    }

    private function normalize_url_list($value) {
        $urls = array();
        foreach ($this->normalize_string_list($value) as $item) {
            $url = esc_url_raw((string) $item);
            if ($url !== '') {
                $urls[] = $url;
            }
        }
        return array_values(array_unique($urls));
    }

    private function normalize_page_key($value) {
        $slug = sanitize_title((string) $value);
        if ($slug === '') {
            return '';
        }

        $aliases = array(
            'home-page' => 'home',
            'homepage' => 'home',
            'about-us' => 'about',
            'our-story' => 'about',
            'our-services' => 'services',
            'service' => 'services',
            'contact-us' => 'contact',
            'book' => 'booking',
            'book-now' => 'booking',
            'book-online' => 'booking',
            'appointments' => 'booking',
            'appointment-booking' => 'booking',
            'schedule' => 'booking',
            'schedule-call' => 'booking',
            'shop-products' => 'shop',
            'products' => 'shop',
            'store' => 'shop',
            'features-page-features' => 'features',
            'pricing-page-pricing' => 'pricing',
            'faqs' => 'faq',
            'documentation-page-docs' => 'docs',
            'documentation-support' => 'docs',
            'documentation-getting-started-page-docs' => 'docs',
            'download-page-download' => 'download',
            'download-get-started-page-download' => 'download',
            'affiliate-program' => 'affiliates',
            'affiliate-program-page-affiliates' => 'affiliates',
            'affiliates-page' => 'affiliates',
            'case-studies' => 'case-studies',
            'case-studies-page' => 'case-studies',
            'affiliate-sign-up' => 'affiliate-signup',
            'affiliate-signup-page' => 'affiliate-signup',
            'affiliate-dashboard-page' => 'affiliate-dashboard',
            'privacy-policy-page-privacy-policy' => 'privacy-policy',
            'terms-of-service-page-terms' => 'terms',
            'cookie-policy-page-cookie-policy' => 'cookie-policy',
            'changelog-what-s-new-page-changelog' => 'changelog',
            'get-started-page-get-started-quicklaunch-onboarding' => 'get-started',
            'partner-portal' => 'partner-dashboard',
            'member-portal' => 'member-portal',
            'members-area' => 'member-portal',
        );

        return $aliases[$slug] ?? $slug;
    }

    private function normalize_page_descriptions($value) {
        if (!is_array($value)) {
            return array();
        }

        $normalized = array();
        foreach ($value as $page => $description) {
            if (!is_scalar($page) || !is_scalar($description)) {
                continue;
            }

            $slug = $this->normalize_page_key((string) $page);
            $text = trim(sanitize_textarea_field((string) $description));
            if ($slug === '' || $text === '') {
                continue;
            }

            $normalized[$slug] = $text;
        }

        return $normalized;
    }

    private function build_brand_profile($client_data, $extracted) {
        $brand = is_array($client_data['brand'] ?? null) ? $client_data['brand'] : array();
        $brand_assets = is_array($extracted['brandAssets'] ?? null) ? $extracted['brandAssets'] : array();
        $logo_url = '';

        foreach (array(
            $extracted['logoUrl'] ?? '',
            $brand['logo_url'] ?? '',
            $client_data['logo_url'] ?? '',
        ) as $candidate) {
            $candidate_url = esc_url_raw((string) $candidate);
            if ($candidate_url !== '') {
                $logo_url = $candidate_url;
                break;
            }
        }

        if ($logo_url === '' && !empty($brand_assets)) {
            foreach ($brand_assets as $asset) {
                if (!is_array($asset)) continue;
                $type = strtolower((string) ($asset['type'] ?? ''));
                $url = esc_url_raw((string) ($asset['url'] ?? ''));
                if ($type === 'logo' && $url !== '') {
                    $logo_url = $url;
                    break;
                }
            }
        }

        $primary_color = sanitize_hex_color((string) ($brand['primary_color'] ?? '')) ?: '#0f172a';
        $accent_color = sanitize_hex_color((string) ($brand['accent_color'] ?? '')) ?: '#0ea5e9';
        $palette = array();
        foreach ($this->normalize_string_list($client_data['brandColors'] ?? ($client_data['colors'] ?? ($extracted['brandColors'] ?? ($extracted['colors'] ?? array())))) as $color) {
            $palette[] = $color;
        }

        $brand['has_logo'] = !empty($extracted['hasLogo']) || $logo_url !== '';
        $brand['logo_url'] = $logo_url;
        $brand['primary_color'] = $primary_color;
        $brand['accent_color'] = $accent_color;
        $brand['palette'] = array_values(array_unique($palette));
        $brand['reference_sites'] = $this->normalize_url_list($extracted['inspirationWebsites'] ?? ($client_data['references'] ?? ($client_data['inspirationSites'] ?? array())));
        $brand['video_urls'] = $this->normalize_url_list($extracted['videoUrls'] ?? array());
        $brand['product_images'] = $this->normalize_url_list($extracted['productImages'] ?? array());
        $brand['assets'] = $brand_assets;

        return $brand;
    }

    private function humanize_page_title($page, $page_labels = array()) {
        $slug = $this->normalize_page_key($page);
        if ($slug === '') {
            return 'Page';
        }

        $label = sanitize_text_field((string) ($page_labels[$slug] ?? ''));
        if ($label === '') {
            $label = ucwords(str_replace(array('-', '_'), ' ', $slug));
        }

        $replacements = array(
            'Faq' => 'FAQ',
            'Ai' => 'AI',
            'Seo' => 'SEO',
            'Api' => 'API',
            'Cta' => 'CTA',
            '360' => '360',
        );

        return strtr($label, $replacements);
    }

    private function build_requested_page_blueprints($business_data) {
        $custom_pages = $this->normalize_string_list($business_data['custom_pages'] ?? array());
        $special_features = $this->normalize_string_list($business_data['special_features'] ?? array());
        $page_descriptions = is_array($business_data['page_descriptions'] ?? null) ? $business_data['page_descriptions'] : array();
        $pages = array();
        $labels = array();
        $descriptions = array();
        $self = $this;

        $add_page = function($label, $description = '') use (&$pages, &$labels, &$descriptions, $self) {
            $slug = $self->normalize_page_key($label);
            $text = trim(sanitize_textarea_field((string) $description));
            if ($slug === '') {
                return;
            }

            if (!in_array($slug, $pages, true)) {
                $pages[] = $slug;
            }

            if (empty($labels[$slug])) {
                $labels[$slug] = sanitize_text_field((string) $label);
            }

            if ($text !== '' && empty($descriptions[$slug])) {
                $descriptions[$slug] = $text;
            }
        };

        foreach ($custom_pages as $page_label) {
            $slug = $this->normalize_page_key($page_label);
            $add_page($page_label, $page_descriptions[$slug] ?? '');
        }

        foreach ($page_descriptions as $page_slug => $description) {
            $add_page($page_slug, $description);
        }

        $feature_blob = strtolower(implode(' | ', array_merge($custom_pages, $special_features)));
        if (preg_match('/affiliate|referral|partner/', $feature_blob)) {
            $add_page('affiliate-signup', 'Dedicated affiliate and referral sign-up page with program benefits, trust signals, FAQs, and a strong application CTA.');
        }
        if (preg_match('/affiliate-dashboard|partner-dashboard|member-dashboard|dashboard/', $feature_blob)) {
            $add_page('affiliate-dashboard', 'Private dashboard preview page outlining partner KPIs, referral tracking, commissions, and account actions.');
        }
        if (preg_match('/calculator|earnings/', $feature_blob)) {
            $add_page('referral-calculator', 'Interactive calculator page that helps partners estimate referral or commission earnings with clear examples.');
        }
        if (preg_match('/membership|portal|member-area|member/', $feature_blob)) {
            $add_page('member-portal', 'Member portal page with account access, benefits overview, gated resources, and onboarding guidance.');
        }
        if (preg_match('/pricing/', $feature_blob)) {
            $add_page('pricing', 'Transparent pricing page with tier comparisons, benefits, FAQs, and conversion-focused CTAs.');
        }
        if (preg_match('/faq/', $feature_blob)) {
            $add_page('faq', 'FAQ page that answers key objections, onboarding questions, and purchase concerns in a conversion-friendly layout.');
        }

        $add_page('faq', $page_descriptions['faq'] ?? 'FAQ page that answers common customer questions, pricing expectations, process details, support policies, and objections in clear, trustworthy language.');

        return array(
            'pages' => $pages,
            'labels' => $labels,
            'descriptions' => $descriptions,
        );
    }

    private function build_generated_builder_state($landing_html, $website_pages, $business_data) {
        $pages_map = is_array($website_pages) ? $website_pages : array();
        $page_keys = array_values(array_keys($pages_map));
        $current_page = in_array('home', $page_keys, true) ? 'home' : ($page_keys[0] ?? null);
        $primary_html = is_string($landing_html) ? $landing_html : '';

        if ($primary_html === '' && $current_page !== null && !empty($pages_map[$current_page])) {
            $page_payload = $pages_map[$current_page];
            if (is_string($page_payload)) {
                $primary_html = $page_payload;
            } elseif (is_array($page_payload)) {
                foreach (array('html', 'content', 'markup') as $candidate_key) {
                    if (!empty($page_payload[$candidate_key]) && is_string($page_payload[$candidate_key])) {
                        $primary_html = $page_payload[$candidate_key];
                        break;
                    }
                }
            }
        }

        $state = array(
            'blocks' => $primary_html !== '' ? array(array(
                'id' => 'consultation_home_' . time(),
                'html' => $primary_html,
                'type' => 'consultation_generated',
            )) : array(),
            'pagesMap' => $pages_map,
            'pageOrder' => $page_keys,
            'currentPage' => $current_page,
            'generatedFromConsultation' => true,
            'businessName' => $business_data['business_name'] ?? '',
            'customPages' => is_array($business_data['custom_pages'] ?? null) ? $business_data['custom_pages'] : array(),
            'pageDescriptions' => is_array($business_data['page_descriptions'] ?? null) ? $business_data['page_descriptions'] : array(),
            'navigation' => $this->create_navigation_structure($page_keys),
            'componentState' => array(
                'version' => 'v2.0-2026-04-30',
                'schema' => 'vmo_react_style_component_state_manifest',
                'sourceOfTruth' => 'react_style_component_state',
                'renderBridge' => 'standalone_html_css_js',
                'stateIntegrityHash' => md5(wp_json_encode($pages_map)),
                'ownership' => 'vmo_internal_builder_template_only',
                'editPolicy' => 'component_props_then_rendered_html',
                'publicRuntime' => 'standalone_html_css_js',
                'publicBuilderControlsAllowed' => false,
                'trackingContractRequired' => true,
                'previewGate' => 'block_until_media_complete_and_final_qa_passes',
                'components' => $this->build_component_state_components($pages_map, $business_data),
            ),
        );

        return $state;
    }

    private function build_component_state_components($pages_map, $business_data) {
        $components = array();
        foreach ((array) $pages_map as $page_key => $page_payload) {
            $page_key = sanitize_key((string) $page_key);
            $html = is_string($page_payload) ? $page_payload : (string) ($page_payload['html'] ?? $page_payload['content'] ?? '');
            $section_count = (int) preg_match_all('/<section\b/i', $html);
            $tracking_count = (int) preg_match_all('/data-vmo-track\s*=|data-vmo-event\s*=|data-vmo-checkout-url\s*=|data-vmo-product-id\s*=/i', $html);
            $media_count = (int) preg_match_all('/<img\b|<video\b|background-image|data-vmo-generate|data-vmo-sora/i', $html);
            $components[] = array(
                'id' => 'page_' . ($page_key ?: 'home'),
                'type' => 'generated_page',
                'page' => $page_key ?: 'home',
                'stateChecksum' => md5($html),
                'renderContract' => 'props_to_standalone_html_css_js',
                'props' => array(
                    'businessName' => (string) ($business_data['business_name'] ?? ''),
                    'sectionCount' => $section_count,
                    'hasHero' => (bool) preg_match('/hero|min-h-screen|100vh/i', $html),
                    'hasForms' => (bool) preg_match('/<form|data-vmo-form/i', $html),
                    'hasCommerce' => (bool) preg_match('/vmo_buy_product|checkout|cart|price|buy now/i', $html),
                    'hasMediaMarkers' => (bool) preg_match('/data-vmo-generate|data-vmo-sora|vmo-media-placeholder/i', $html),
                    'trackingHookCount' => $tracking_count,
                    'mediaSurfaceCount' => $media_count,
                ),
                'editableProps' => array('headline', 'body', 'cta', 'media', 'layout', 'mobileVariant', 'seo'),
                'mobileVariantRequired' => true,
                'motionAware' => (bool) preg_match('/gsap|scrolltrigger|parallax|data-vmo-animate/i', $html),
                'mediaReplaceable' => (bool) preg_match('/<img\b|<video\b|background-image|data-vmo-generate|data-vmo-sora/i', $html),
                'trackingRequired' => true,
                'publicBuilderControlsAllowed' => false,
            );
        }
        return $components;
    }

    private function extract_builder_image_library($business_data) {
        $urls = array();
        $brand = is_array($business_data['brand'] ?? null) ? $business_data['brand'] : array();

        foreach ($this->normalize_url_list(array_merge(
            array($brand['logo_url'] ?? ''),
            is_array($brand['product_images'] ?? null) ? $brand['product_images'] : array()
        )) as $url) {
            $urls[] = $url;
        }

        $products = is_array($business_data['ecommerce']['products'] ?? null) ? $business_data['ecommerce']['products'] : array();
        foreach ($products as $product) {
            if (!is_array($product)) continue;
            $candidate_urls = array();
            if (!empty($product['image'])) {
                $candidate_urls[] = $product['image'];
            }
            if (!empty($product['images']) && is_array($product['images'])) {
                $candidate_urls = array_merge($candidate_urls, $product['images']);
            }
            foreach ($this->normalize_url_list($candidate_urls) as $url) {
                $urls[] = $url;
            }
        }

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

    private function get_vmo_plugin_default_pages() {
        return array(
            'home',
            'features',
            'pricing',
            'about',
            'affiliates',
            'contact',
            'blog',
            'download',
            'docs',
            'faq',
            'changelog',
            'privacy-policy',
            'terms',
            'cookie-policy',
            'get-started',
        );
    }

    private function looks_like_vmo_plugin_blueprint($business_data) {
        if (!is_array($business_data)) {
            return false;
        }

        $signals = array(
            (string) ($business_data['business_name'] ?? ''),
            (string) ($business_data['business_type'] ?? ''),
            (string) ($business_data['industry'] ?? ''),
            (string) ($business_data['location'] ?? ''),
            (string) ($business_data['target_audience'] ?? ''),
            (string) ($business_data['tagline'] ?? ''),
        );

        foreach (array('services', 'goals', 'custom_pages', 'special_features') as $key) {
            $value = $business_data[$key] ?? null;
            if (is_array($value)) {
                $signals[] = implode(' ', array_map('strval', $value));
            } elseif (is_scalar($value)) {
                $signals[] = (string) $value;
            }
        }

        if (is_array($business_data['page_descriptions'] ?? null)) {
            $signals[] = wp_json_encode($business_data['page_descriptions']);
        }

        $products = is_array($business_data['ecommerce']['products'] ?? null) ? $business_data['ecommerce']['products'] : array();
        foreach ($products as $product) {
            if (!is_array($product)) {
                continue;
            }
            $signals[] = (string) ($product['name'] ?? '');
            $signals[] = (string) ($product['description'] ?? '');
        }

        $signal = strtolower(implode(' | ', array_filter(array_map('strval', $signals))));
        if ($signal === '') {
            return false;
        }

        foreach (array('wordpress plugin', 'plugin for wordpress', 'quicklaunch', 'download vmo', 'getvmo', '12 ai agents', 'vmo pay', 'vmo phone agent', 'ai business partner') as $term) {
            if (strpos($signal, $term) !== false) {
                return true;
            }
        }

        $vmo_hits = substr_count($signal, 'vmo');
        $product_hits = 0;
        foreach (array('plugin', 'wordpress', 'download', 'pricing', 'affiliate', 'saas', 'software', 'plan', 'plans') as $term) {
            if (strpos($signal, $term) !== false) {
                $product_hits++;
            }
        }

        return $vmo_hits > 0 && $product_hits >= 2;
    }

    private function get_vmo_plan_seed_catalog() {
        $descriptions = array(
            'tester' => 'Best for a first hands-on VMO experience when someone wants to try the plugin on a real business idea without committing to heavier ongoing automation.',
            'starter' => 'Built for solo operators and side hustles who want a practical launch path with core AI-guided website and business setup.',
            'pro' => 'Recommended for serious business owners who want stronger operating capacity, deeper automation, and room to grow.',
            'elite' => 'Designed for heavy users running more aggressive operations, richer media generation, and broader business activity.',
            'enterprise' => 'For agencies, advanced teams, and high-volume operators who need the deepest capacity and the broadest room to scale.',
        );

        $summaries = array(
            'tester' => 'A low-friction way to experience VMO on a real project.',
            'starter' => 'Launch one business with the essential VMO workflow.',
            'pro' => 'The recommended plan for serious, growth-oriented use.',
            'elite' => 'For heavier usage and more aggressive operating demands.',
            'enterprise' => 'Maximum capacity for advanced teams and agencies.',
        );

        $fallback_prices = array(
            'tester' => 49.99,
            'starter' => 99.99,
            'pro' => 199.99,
            'elite' => 499.99,
            'enterprise' => 999.99,
        );

        $catalog = array();
        $all_plans = class_exists('VMO_Plan_Policy') ? VMO_Plan_Policy::get_all() : array();
        foreach (array('tester', 'starter', 'pro', 'elite', 'enterprise') as $slug) {
            $plan = is_array($all_plans[$slug] ?? null) ? $all_plans[$slug] : array();
            $catalog[] = array(
                'name' => sanitize_text_field((string) ($plan['name'] ?? ucfirst($slug))),
                'slug' => $slug,
                'description' => $descriptions[$slug],
                'summary' => $summaries[$slug],
                'price' => (float) ($plan['price'] ?? $fallback_prices[$slug]),
                'currency' => 'usd',
                'images' => array(),
                'image' => '',
                'type' => 'service',
            );
        }

        return $catalog;
    }

    private function get_vmo_plugin_feature_cards() {
        return array(
            array(
                'title' => 'AI Business Consultation',
                'description' => 'Tell Jerreme about your business and get a structured plan for the website, positioning, operations, and launch path.',
                'image_prompt' => 'futuristic AI business consultation scene for a WordPress plugin website, holographic interface, dark navy background, cyan and silver accents',
            ),
            array(
                'title' => 'Website Generation',
                'description' => 'Generate a real multi-page WordPress website with strong copy, media prompts, navigation, and conversion-focused structure.',
                'image_prompt' => 'premium website generation visual for a WordPress plugin, multiple web pages assembling with glowing interface elements',
            ),
            array(
                'title' => 'VMO Phone Agent',
                'description' => 'Set up an AI-powered business phone experience with call handling, receptionist behavior, and customer-facing workflows.',
                'image_prompt' => 'cinematic AI business phone system visual with futuristic smartphone, holographic call waves, dark premium lighting',
            ),
            array(
                'title' => 'VMO Pay',
                'description' => 'Support subscriptions, one-time payments, and business monetization flows without forcing the user through a patchwork setup.',
                'image_prompt' => 'futuristic payment interface for a premium SaaS plugin website, subscription cards, secure checkout, cyan glow',
            ),
            array(
                'title' => 'Lead and Growth Tools',
                'description' => 'Surface leads, email workflows, and strategic opportunities inside the same operating environment as the website builder.',
                'image_prompt' => 'lead generation and growth dashboard visual for a premium business plugin, holographic charts and prospect cards',
            ),
            array(
                'title' => 'Strategic Dashboard',
                'description' => 'Manage the website, contacts, payments, and operating signals from one dashboard instead of stitching together separate tools.',
                'image_prompt' => 'futuristic strategic dashboard for an AI business plugin, floating charts, phone logs, CRM panels, dark cinematic background',
            ),
        );
    }

    private function fallback_vmo_product_page_generation($page_type, $context) {
        $business_data = is_array($context['business_data'] ?? null) ? $context['business_data'] : array();
        $business_name = sanitize_text_field((string) ($business_data['business_name'] ?? 'VMO AI Web Business Builder'));
        if ($business_name === '') {
            $business_name = 'VMO AI Web Business Builder';
        }

        $plans = $this->get_vmo_plan_seed_catalog();
        $features = $this->get_vmo_plugin_feature_cards();
        $support_email = sanitize_email((string) (($business_data['contact_info']['email'] ?? '') ?: 'support@getvmo.com'));
        $hero_media_id = 'media_' . wp_generate_password(8, false);
        $hero_prompt = 'premium hero visual for a WordPress plugin marketing website that proves the plugin built the site itself, dark futuristic glass interface, cinematic lighting';

        $feature_cards_html = '';
        foreach ($features as $feature) {
            $feature_cards_html .= '<article class="vmo-plugin-card" data-vmo-reveal>'
                . '<div class="vmo-plugin-card-media vmo-media-placeholder vmo-media-placeholder-image" data-type="image" data-media-id="media_' . esc_attr(wp_generate_password(8, false)) . '" data-vmo-generate="true" data-vmo-prompt="' . esc_attr($feature['image_prompt']) . '"></div>'
                . '<h3>' . esc_html($feature['title']) . '</h3>'
                . '<p>' . esc_html($feature['description']) . '</p>'
                . '</article>';
        }

        $plan_cards_html = '';
        foreach ($plans as $plan) {
            $plan_cards_html .= '<article class="vmo-plan-card" data-vmo-reveal>'
                . '<p class="vmo-plan-name">' . esc_html($plan['name']) . '</p>'
                . '<p class="vmo-plan-price">$' . esc_html(number_format((float) $plan['price'], 2)) . '<small>/month</small></p>'
                . '<p class="vmo-plan-summary">' . esc_html($plan['summary']) . '</p>'
                . '</article>';
        }

        $hero_map = array(
            'home' => array(
                'kicker' => 'WordPress Plugin',
                'headline' => 'Build, run, and grow your business with one WordPress plugin.',
                'subtitle' => 'VMO is the plugin behind this very website. It helps users plan a business, generate a website, and operate core systems from one environment.',
                'primary_label' => 'Download VMO',
                'primary_href' => '#download',
                'secondary_label' => 'Begin with QuickLaunch',
                'secondary_href' => '#quicklaunch',
            ),
            'features' => array(
                'kicker' => 'What VMO Does',
                'headline' => 'A single plugin that covers website generation, business setup, and day-to-day operations.',
                'subtitle' => 'Every section should explain what the plugin does, who it helps, and why using it is simpler than stitching together separate tools.',
                'primary_label' => 'See Pricing',
                'primary_href' => '#pricing',
                'secondary_label' => 'Download VMO',
                'secondary_href' => '#download',
            ),
            'pricing' => array(
                'kicker' => 'Transparent Pricing',
                'headline' => 'Choose the plan that matches how you want to experience VMO.',
                'subtitle' => 'The plugin is free to install. Users choose a paid plan when they are ready to actually build and operate with VMO.',
                'primary_label' => 'Download to Start',
                'primary_href' => '#download',
                'secondary_label' => 'Begin with QuickLaunch',
                'secondary_href' => '#quicklaunch',
            ),
            'affiliates' => array(
                'kicker' => 'Affiliate Program',
                'headline' => 'Earn 5% promoting VMO to people who need a real business-building tool.',
                'subtitle' => 'Affiliates are a secondary but important path: after visitors understand the product, they can also choose to promote it and earn recurring commission.',
                'primary_label' => 'Sign Up as an Affiliate',
                'primary_href' => '#affiliate-signup',
                'secondary_label' => 'See the Calculator',
                'secondary_href' => '#affiliate-calculator',
            ),
            'download' => array(
                'kicker' => 'Get Started',
                'headline' => 'Two ways to start: install the plugin or launch with guided setup.',
                'subtitle' => 'Existing WordPress users can download the plugin directly. Beginners can start with QuickLaunch and get guided onboarding.',
                'primary_label' => 'Download VMO',
                'primary_href' => '#download-options',
                'secondary_label' => 'Launch My Business',
                'secondary_href' => '#quicklaunch',
            ),
            'get-started' => array(
                'kicker' => 'QuickLaunch',
                'headline' => 'Start with guided onboarding when you do not want to touch the technical setup yourself.',
                'subtitle' => 'QuickLaunch is the direct path for non-technical visitors who want hosting, setup, and VMO ready to go with as little friction as possible.',
                'primary_label' => 'Launch My Business',
                'primary_href' => '#quicklaunch',
                'secondary_label' => 'Download Instead',
                'secondary_href' => '#download',
            ),
        );

        $hero = $hero_map[$page_type] ?? array(
            'kicker' => 'VMO',
            'headline' => 'A premium website that makes it obvious what VMO is and why someone should start using it now.',
            'subtitle' => 'This fallback exists to keep the site aligned with the product blueprint when generation quality drops.',
            'primary_label' => 'Download VMO',
            'primary_href' => '#download',
            'secondary_label' => 'See Pricing',
            'secondary_href' => '#pricing',
        );

        switch ($page_type) {
            case 'features':
                $main_content = '<section class="vmo-plugin-section" data-vmo-reveal><h2>What VMO helps users do</h2><div class="vmo-plugin-grid vmo-plugin-grid-three">' . $feature_cards_html . '</div></section>'
                    . '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>What this site must never feel like</h2><p>This is not a consulting agency brochure. It is a plugin product site that should make the visitor understand the product quickly, feel the urgency to try it, and know exactly how to get started.</p></section>';
                break;

            case 'pricing':
                $main_content = '<section class="vmo-plugin-section" id="pricing" data-vmo-reveal><h2>The five plans sold by VMO</h2><div class="vmo-plugin-grid vmo-plugin-grid-five">' . $plan_cards_html . '</div><p class="vmo-plugin-note">Only these plan names are valid here: Tester, Starter, Pro, Elite, Enterprise. Do not invent packages or alternate plan families.</p></section>'
                    . '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Why the Tester plan matters</h2><p>The tester plan exists so people can experience the plugin on a real project. It is not meant to make the product feel free forever; it is the entry point for paid hands-on evaluation.</p></section>';
                break;

            case 'about':
                $main_content = '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Why this website exists</h2><p>This website is supposed to prove what the plugin can do. A visitor should understand that the impressive site they are viewing was itself built by the same plugin they are being encouraged to adopt.</p><p>That makes the website both a sales asset and a working demonstration of VMO capability.</p></section>';
                break;

            case 'affiliates':
                $main_content = '<section class="vmo-plugin-section" id="affiliate-signup" data-vmo-reveal><h2>Affiliate signup</h2><p>After understanding the value of VMO, visitors should be able to join the affiliate program and promote the plugin to others while earning 5% commission.</p><form class="vmo-plugin-form" data-vmo-form="affiliate_signup" data-vmo-webhook="affiliate_signup"><label>Name<input name="name" type="text" required></label><label>Email<input name="email" type="email" required></label><button class="vmo-btn" type="submit">Apply to join</button></form></section>'
                    . '<section class="vmo-plugin-section" id="affiliate-calculator" data-vmo-reveal><h2>Commission calculator</h2><p>The calculator belongs on the affiliate experience, but it should always reinforce the real plan family used by VMO.</p><div class="vmo-plugin-calculator" data-vmo-calculator="affiliate_commission"><span>Example monthly referrals</span><strong>10 referrals can become recurring partner revenue.</strong></div></section>';
                break;

            case 'download':
                $main_content = '<section class="vmo-plugin-section" id="download-options" data-vmo-reveal><h2>Choose your path</h2><div class="vmo-plugin-grid vmo-plugin-grid-two"><article class="vmo-plugin-card"><h3>Option A: I already have WordPress</h3><p>Download the plugin, install it, choose a plan, and start building with Jerreme.</p><a class="vmo-btn" href="#download">Download the plugin</a></article><article class="vmo-plugin-card"><h3>Option B: I want the guided setup</h3><p>Use QuickLaunch to start from a guided onboarding flow when you want the easier path.</p><a class="vmo-btn vmo-btn-secondary" href="#quicklaunch">Begin with QuickLaunch</a></article></div></section>';
                break;

            case 'docs':
                $main_content = '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Quick start</h2><ol class="vmo-step-list"><li>Install and activate the plugin.</li><li>Open VMO in the WordPress admin.</li><li>Talk to Jerreme through consultation.</li><li>Generate the website and business setup.</li><li>Review, refine, and publish.</li></ol></section>';
                break;

            case 'faq':
                $main_content = '<section class="vmo-plugin-section" data-vmo-reveal><h2>FAQ</h2><div class="vmo-plugin-grid vmo-plugin-grid-two"><article class="vmo-plugin-card"><h3>What is VMO?</h3><p>VMO is a WordPress plugin that helps users plan, build, and operate a business website with AI-guided workflows.</p></article><article class="vmo-plugin-card"><h3>Is it a service company?</h3><p>No. The site sells a plugin product. The product may perform services for the user, but the business itself is the plugin.</p></article><article class="vmo-plugin-card"><h3>How do I try it?</h3><p>Download the plugin or begin with QuickLaunch, then choose a plan and start building.</p></article><article class="vmo-plugin-card"><h3>Can I promote it as an affiliate?</h3><p>Yes. Visitors can also join the affiliate program after they understand the value of the plugin.</p></article></div></section>';
                break;

            case 'blog':
                $main_content = '<section class="vmo-plugin-section" data-vmo-reveal><h2>Suggested launch posts</h2><div class="vmo-plugin-grid vmo-plugin-grid-two"><article class="vmo-plugin-card"><h3>Introducing VMO</h3><p>The plugin that builds and helps run your business.</p></article><article class="vmo-plugin-card"><h3>From consultation to website</h3><p>How Jerreme turns a business brief into a live site.</p></article><article class="vmo-plugin-card"><h3>Why a plugin beats tool sprawl</h3><p>How one system replaces a pile of disconnected subscriptions.</p></article><article class="vmo-plugin-card"><h3>How affiliates earn with VMO</h3><p>Explain the program, the calculator, and the long-term incentive.</p></article></div></section>';
                break;

            case 'contact':
                $main_content = '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Support</h2><p>Email: <a href="mailto:' . esc_attr($support_email) . '">' . esc_html($support_email) . '</a></p><p>Point visitors to support, docs, and WordPress-friendly onboarding help rather than selling generic consulting retainers.</p></section>';
                break;

            case 'privacy-policy':
                $main_content = '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Privacy disclosures</h2><p>This page should disclose the real third-party providers and data flows used by the product, even though the public marketing copy stays white-labeled elsewhere.</p></section>';
                break;

            case 'terms':
                $main_content = '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Terms of service</h2><p>This page should explain that VMO is a WordPress plugin and that paid plans cover access to AI-powered services around the product.</p></section>';
                break;

            case 'cookie-policy':
                $main_content = '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Cookie policy</h2><p>This page should cover essential cookies and affiliate tracking cookies without burying the user in unnecessary jargon.</p></section>';
                break;

            case 'changelog':
                $main_content = '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>What is new</h2><p>Use this page to show active maintenance, new features, fixes, and ongoing progress so visitors and reviewers see that the plugin is being cared for.</p></section>';
                break;

            case 'get-started':
                $main_content = '<section class="vmo-plugin-section" id="quicklaunch" data-vmo-reveal><h2>QuickLaunch onboarding</h2><p>Use this page for the guided path that gets non-technical visitors moving immediately.</p><form class="vmo-plugin-form" data-vmo-form="quicklaunch" data-vmo-webhook="quicklaunch_start"><label>Business name<input name="business_name" type="text" required></label><label>Email<input name="email" type="email" required></label><button class="vmo-btn" type="submit">Start QuickLaunch</button></form></section>';
                break;

            case 'home':
            default:
                $main_content = '<section class="vmo-plugin-section" data-vmo-reveal><h2>What VMO helps users do</h2><div class="vmo-plugin-grid vmo-plugin-grid-three">' . $feature_cards_html . '</div></section>'
                    . '<section class="vmo-plugin-section" id="pricing" data-vmo-reveal><h2>Real VMO plans</h2><div class="vmo-plugin-grid vmo-plugin-grid-five">' . $plan_cards_html . '</div></section>'
                    . '<section class="vmo-plugin-section vmo-plugin-panel" data-vmo-reveal><h2>Why this site must feel urgent</h2><p>The goal is not to sound like a vague team of experts. The goal is to make a visitor understand what the plugin is, see why it matters, and feel that starting now is the smartest move.</p></section>'
                    . '<section class="vmo-plugin-section vmo-plugin-panel" id="download" data-vmo-reveal><h2>Start now</h2><p>Download the plugin if you already have WordPress, or begin with QuickLaunch if you want the guided setup path.</p><div class="vmo-cta-row"><a class="vmo-btn" href="#download">Download VMO</a><a class="vmo-btn vmo-btn-secondary" href="#quicklaunch">Begin with QuickLaunch</a></div></section>';
                break;
        }

        $html = '<div class="vmo-plugin-fallback-page vmo-internal-fallback" data-vmo-internal-fallback="blocked" data-vmo-preview-blocked="true" data-vmo-fallback-reason="premium_generation_required">'
            . '<header class="vmo-plugin-hero">'
            . '<div class="vmo-plugin-hero-media vmo-media-placeholder vmo-media-placeholder-image" data-type="image" data-media-id="' . esc_attr($hero_media_id) . '" data-vmo-generate="true" data-vmo-prompt="' . esc_attr($hero_prompt) . '"></div>'
            . '<div class="vmo-plugin-hero-overlay"></div>'
            . '<div class="vmo-plugin-hero-content" data-vmo-reveal>'
            . '<p class="vmo-plugin-kicker">' . esc_html($hero['kicker']) . '</p>'
            . '<h1>' . esc_html($hero['headline']) . '</h1>'
            . '<p>' . esc_html($hero['subtitle']) . '</p>'
            . '<div class="vmo-cta-row"><a class="vmo-btn" href="' . esc_attr($hero['primary_href']) . '">' . esc_html($hero['primary_label']) . '</a><a class="vmo-btn vmo-btn-secondary" href="' . esc_attr($hero['secondary_href']) . '">' . esc_html($hero['secondary_label']) . '</a></div>'
            . '</div>'
            . '</header>'
            . $main_content
            . '<footer class="vmo-plugin-footer" data-vmo-reveal><p>&copy; ' . esc_html(date('Y')) . ' ' . esc_html($business_name) . '. This site exists to prove what the plugin can do for the next user.</p></footer>'
            . '</div>';

        $html .= '<style>
        .vmo-plugin-fallback-page{max-width:1280px;margin:0 auto;padding:0 16px 32px;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:radial-gradient(circle at top,#10263a 0%,#07121a 50%,#060c12 100%);color:#e6f3ff}
        .vmo-plugin-hero{position:relative;overflow:hidden;border-radius:24px;min-height:68vh;margin:18px 0 28px;border:1px solid rgba(15,225,255,.16);box-shadow:0 24px 60px -28px rgba(0,0,0,.55)}
        .vmo-plugin-hero-media,.vmo-plugin-hero-overlay{position:absolute;inset:0}
        .vmo-plugin-hero-media{background:linear-gradient(135deg,rgba(15,225,255,.12),rgba(124,92,255,.18));display:flex;align-items:center;justify-content:center;color:rgba(230,243,255,.72)}
        .vmo-plugin-hero-overlay{background:linear-gradient(135deg,rgba(7,18,26,.28),rgba(7,18,26,.82))}
        .vmo-plugin-hero-content{position:relative;z-index:2;max-width:720px;padding:72px 40px}
        .vmo-plugin-kicker,.vmo-plan-name{letter-spacing:.12em;text-transform:uppercase;font-size:12px;color:#65f1ff}
        .vmo-plugin-hero h1{font-size:clamp(34px,6vw,68px);line-height:1.02;margin:0 0 16px}
        .vmo-plugin-hero p{max-width:62ch;font-size:18px;line-height:1.7;color:#c4d9ec}
        .vmo-plugin-section{padding:18px 0 8px}
        .vmo-plugin-section h2{font-size:clamp(28px,4vw,42px);margin:0 0 18px}
        .vmo-plugin-grid{display:grid;gap:18px}
        .vmo-plugin-grid-two{grid-template-columns:repeat(2,minmax(0,1fr))}
        .vmo-plugin-grid-three{grid-template-columns:repeat(3,minmax(0,1fr))}
        .vmo-plugin-grid-five{grid-template-columns:repeat(5,minmax(0,1fr))}
        .vmo-plugin-card,.vmo-plan-card,.vmo-plugin-panel{background:rgba(255,255,255,.05);border:1px solid rgba(15,225,255,.12);backdrop-filter:blur(18px);border-radius:20px;padding:22px}
        .vmo-plugin-card h3,.vmo-plan-card h3{margin:0 0 12px;font-size:22px}
        .vmo-plugin-card p,.vmo-plan-card p,.vmo-plugin-panel p{color:#c4d9ec;line-height:1.7}
        .vmo-plugin-card-media{height:180px;border-radius:16px;margin-bottom:14px;background:linear-gradient(135deg,rgba(15,225,255,.08),rgba(124,92,255,.12));display:flex;align-items:center;justify-content:center;color:rgba(230,243,255,.7)}
        .vmo-plan-price{font-size:32px;font-weight:700;margin:0 0 10px}
        .vmo-plan-price small{font-size:14px;color:#9fb2c8;margin-left:4px}
        .vmo-plan-summary{margin:0}
        .vmo-btn{display:inline-flex;align-items:center;justify-content:center;padding:14px 20px;border-radius:999px;background:linear-gradient(135deg,#0fe1ff,#7c5cff);color:#07121a;text-decoration:none;font-weight:700;border:none}
        .vmo-btn-secondary{background:transparent;color:#e6f3ff;border:1px solid rgba(15,225,255,.28)}
        .vmo-cta-row{display:flex;gap:12px;flex-wrap:wrap;margin-top:18px}
        .vmo-plugin-note{margin-top:16px;color:#9fb2c8}
        .vmo-plugin-form,.vmo-plugin-calculator{display:grid;gap:14px;padding:18px;border-radius:16px;border:1px solid rgba(15,225,255,.3);background:rgba(6,12,18,.45);color:#c7fbff}
        .vmo-plugin-form label{display:grid;gap:6px;font-weight:700}.vmo-plugin-form input{width:100%;border:1px solid rgba(255,255,255,.16);border-radius:10px;background:rgba(255,255,255,.08);color:#fff;padding:12px}
        .vmo-step-list{display:grid;gap:12px;padding-left:22px;color:#c4d9ec}
        .vmo-plugin-footer{padding:28px 0 12px;color:#9fb2c8;text-align:center}
        @media (max-width:1120px){.vmo-plugin-grid-five{grid-template-columns:repeat(2,minmax(0,1fr))}.vmo-plugin-grid-three{grid-template-columns:repeat(2,minmax(0,1fr))}}
        @media (max-width:720px){.vmo-plugin-grid-two,.vmo-plugin-grid-three,.vmo-plugin-grid-five{grid-template-columns:1fr}.vmo-plugin-hero-content{padding:48px 24px}.vmo-btn{width:100%}.vmo-cta-row{flex-direction:column}}
        </style>';

        return $html;
    }
    
    /**
     * Create website architecture based on business needs
     * ✅ BULLETPROOF: Returns minimal valid architecture even if inputs are missing
     */
    private function create_website_architecture($business_data, $media_assets) {
        // ✅ Defensive: Ensure business_data is an array
        if (!is_array($business_data)) {
            $business_data = array();
        }

        $plugin_blueprint_mode = $this->looks_like_vmo_plugin_blueprint($business_data);
        $pages = $plugin_blueprint_mode ? $this->get_vmo_plugin_default_pages() : array('home', 'about', 'services', 'faq', 'contact');
        $requested_pages = $this->build_requested_page_blueprints($business_data);
        
        try {
            // Add specialized pages based on business type
            if (!$plugin_blueprint_mode && !empty($business_data['services']) && is_array($business_data['services']) && count($business_data['services']) > 3) {
                $pages[] = 'portfolio';
            }
            
            // Add blog if content marketing is a goal
            $goals = is_array($business_data['goals'] ?? null) ? $business_data['goals'] : array();
            if (in_array('content_marketing', $goals)) {
                $pages[] = 'blog';
            }
            
            // Add booking page if appointment-based business
            $business_type = $business_data['business_type'] ?? '';
            if (!$plugin_blueprint_mode && is_string($business_type) && strpos(strtolower($business_type), 'service') !== false) {
                $pages[] = 'booking';
            }
            
            // Add shop/products pages if e-commerce goal is present
            $ecommerce = is_array($business_data['ecommerce'] ?? null) ? $business_data['ecommerce'] : array();
            $products = is_array($ecommerce['products'] ?? null) ? $ecommerce['products'] : array();
            $mode = (string) ($ecommerce['collection_mode'] ?? 'products');
            if (!$plugin_blueprint_mode && in_array('e-commerce', $goals) && $mode === 'donations') {
                $pages[] = 'donate';
            } elseif (!$plugin_blueprint_mode && in_array('e-commerce', $goals) && !empty($products)) {
                $pages[] = 'shop';
                foreach ($products as $p) {
                    if (!empty($p['name'])) {
                        $slug = 'product-' . sanitize_title($p['name']);
                        if (!in_array($slug, $pages)) $pages[] = $slug;
                    }
                }
            } elseif (!$plugin_blueprint_mode && in_array('e-commerce', $goals)) {
                // Ensure a payment collection page exists even if product list is empty yet
                $pages[] = ($mode === 'donations') ? 'donate' : 'shop';
            }

            foreach ($requested_pages['pages'] as $requested_page) {
                if (!in_array($requested_page, $pages, true)) {
                    $pages[] = $requested_page;
                }
            }
        } catch (\Throwable $e) {
            error_log('VMO create_website_architecture warning: ' . $e->getMessage());
            // Continue with basic pages
        }

        // ✅ Honor the user-approved website FORMAT (single-page / hybrid / multi-page).
        // For single-page sites we collapse the standard informational pages into the home page
        // (rendered as inline anchor sections), but keep pages that REQUIRE their own URL
        // (e-commerce shop/product/donate). Hybrid keeps a one-page core plus essential sub-pages.
        $design_concept = is_array($business_data['website_design_concept'] ?? null) ? $business_data['website_design_concept'] : array();
        $site_format = (string) ($design_concept['site_format'] ?? 'multi_page');
        $single_page_sections = array();
        if (!$plugin_blueprint_mode && in_array($site_format, array('single_page', 'hybrid'), true)) {
            // Pages that must remain standalone because they need their own URL / purchasing flow.
            $url_required = array('blog', 'booking');
            $ecommerce_pages = array();
            foreach ($pages as $p) {
                if ($p === 'shop' || $p === 'donate' || strpos($p, 'product-') === 0) {
                    $ecommerce_pages[] = $p;
                }
            }

            // Sections that become inline anchors on the one-page home experience.
            $inline_candidates = array('about', 'services', 'portfolio', 'faq', 'contact');
            foreach ($pages as $p) {
                if (in_array($p, $inline_candidates, true) && !in_array($p, $single_page_sections, true)) {
                    $single_page_sections[] = $p;
                }
            }
            // Any custom requested pages that are not URL-required also fold into the one-pager.
            foreach ($requested_pages['pages'] as $rp) {
                if (!in_array($rp, $url_required, true) && $rp !== 'shop' && $rp !== 'donate' && strpos($rp, 'product-') !== 0) {
                    if ($rp !== 'home' && !in_array($rp, $single_page_sections, true)) {
                        $single_page_sections[] = $rp;
                    }
                }
            }

            if ($site_format === 'single_page') {
                // Pure one-pager: home + (ecommerce + url-required) pages only.
                $kept = array('home');
                foreach ($pages as $p) {
                    if ($p === 'home') { continue; }
                    if (in_array($p, $ecommerce_pages, true) || in_array($p, $url_required, true)) {
                        $kept[] = $p;
                    }
                }
                $pages = array_values(array_unique($kept));
            } else {
                // Hybrid: one-page core + essential sub-pages (contact + url-required + ecommerce).
                $essential_subpages = array('contact');
                $kept = array('home');
                foreach ($pages as $p) {
                    if ($p === 'home') { continue; }
                    if (in_array($p, $ecommerce_pages, true) || in_array($p, $url_required, true) || in_array($p, $essential_subpages, true)) {
                        $kept[] = $p;
                    }
                }
                $pages = array_values(array_unique($kept));
                // Sections still inlined exclude those we kept as real sub-pages.
                $single_page_sections = array_values(array_diff($single_page_sections, $kept));
            }
        }

        return array(
            'pages' => $pages,
            'page_labels' => $requested_pages['labels'],
            'page_descriptions' => $requested_pages['descriptions'],
            'navigation' => $this->create_navigation_structure($pages, $requested_pages['labels']),
            'media_placement' => $this->plan_media_placement($pages, $media_assets ?? array()),
            'conversion_points' => $this->identify_conversion_points($business_data),
            'site_format' => $site_format,
            'single_page_sections' => $single_page_sections,
            'design_direction' => (string) ($design_concept['design_direction'] ?? ''),
        );
    }
    
    /**
     * Generate complete website pages
     */
    private function generate_website_pages($architecture, $business_data, $media_assets) {
        $pages = array();
        
        foreach ($architecture['pages'] as $page_type) {
            if ($page_type === 'shop') {
                $pages[$page_type] = $this->generate_shop_page($business_data, $media_assets);
            } elseif ($page_type === 'donate') {
                $pages[$page_type] = $this->generate_donate_page($business_data, $media_assets);
            } elseif (strpos($page_type, 'product-') === 0) {
                $product = $this->find_product_by_slug($business_data, $page_type);
                $pages[$page_type] = $this->generate_product_page($product, $business_data, $media_assets);
            } else {
                $pages[$page_type] = $this->generate_single_page($page_type, $business_data, $media_assets, $architecture);
            }
        }
        
        return $pages;
    }

    private function find_product_by_slug($business_data, $slug) {
        $name = trim(str_replace('product-', '', $slug));
        foreach (($business_data['ecommerce']['products'] ?? array()) as $p) {
            if (sanitize_title($p['name'] ?? '') === $name) return $p;
        }
        return null;
    }

    private function ensure_gsap_loader_snippet($scope_id = 'vmo-anim-scope') {
        // CDN refs below are injected into generated website HTML output, not WP admin.
        $sid = sanitize_html_class($scope_id);
        return '<script>(function(){
            var scopeId = ' . wp_json_encode($sid) . ';
            function loadScriptOnce(url){
                return new Promise(function(resolve,reject){
                    if (document.querySelector("script[data-vmo-src=\""+url+"\"]")) return resolve();
                    var s=document.createElement("script");
                    s.src=url; s.async=true; s.setAttribute("data-vmo-src", url);
                    s.onload=function(){resolve();}; s.onerror=function(){reject(new Error("Failed to load "+url));};
                    document.head.appendChild(s);
                });
            }
            window.__vmoLoadGsap = window.__vmoLoadGsap || (function(){
                return Promise.resolve()
                  .then(function(){
                      if (window.gsap && window.ScrollTrigger) return;
                      return loadScriptOnce("https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js")
                        .then(function(){ return loadScriptOnce("https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"); });
                  })
                  .catch(function(){ /* graceful no-op */ });
            });

            // Kick off loading early to reduce jank; pages may still work without it.
            try { window.__vmoLoadGsap(); } catch(e) {}
        })();</script>';
    }

    private function build_variant_family_section($product, $business_data) {
        if (!is_array($product) || !is_array($business_data)) return '';
        $products = is_array($business_data['ecommerce']['products'] ?? null) ? $business_data['ecommerce']['products'] : array();
        if (!$products) return '';

        $base_key = '';
        if (!empty($product['variant_of'])) {
            $base_key = sanitize_title((string) $product['variant_of']);
        } else {
            $base_key = sanitize_title((string) ($product['slug'] ?? ($product['name'] ?? '')));
        }
        if ($base_key === '') return '';

        $siblings = array();
        foreach ($products as $p) {
            if (!is_array($p)) continue;
            $p_base = !empty($p['variant_of']) ? sanitize_title((string) $p['variant_of']) : sanitize_title((string) ($p['slug'] ?? ($p['name'] ?? '')));
            if ($p_base !== $base_key) continue;
            $siblings[] = $p;
        }

        if (count($siblings) <= 1) return '';

        $cards = '';
        foreach ($siblings as $sib) {
            $sib_title = esc_html((string) ($sib['name'] ?? 'Variant'));
            $sib_label = esc_html((string) ($sib['variant_label'] ?? 'Standard'));
            $sib_slug = sanitize_title((string) ($sib['slug'] ?? ($sib['name'] ?? '')));
            $href = esc_url((string) home_url('/product-' . $sib_slug));
            $active = (sanitize_title((string) ($product['slug'] ?? ($product['name'] ?? ''))) === $sib_slug) ? ' is-active' : '';
            $price = isset($sib['price']) ? floatval($sib['price']) : 0.0;
            $price_txt = $price > 0 ? esc_html('$' . number_format($price, 2)) : '';
            $thumb = '';
            if (!empty($sib['image'])) {
                $thumb = '<img src="' . esc_url((string) $sib['image']) . '" alt="' . esc_attr(strip_tags($sib_title)) . '" />';
            } else {
                $thumb = '[GENERATED_IMAGE: product thumbnail for ' . $sib_title . ']';
            }

            $cards .= '<a class="vf-card' . $active . '" href="' . $href . '">'
                . '<div class="vf-thumb">' . $thumb . '</div>'
                . '<div class="vf-meta">'
                . '<div class="vf-label">' . $sib_label . '</div>'
                . '<div class="vf-title">' . $sib_title . '</div>'
                . ($price_txt ? '<div class="vf-price">' . $price_txt . '</div>' : '')
                . '</div>'
                . '</a>';
        }

        return '<section class="variant-family" data-vmo-reveal data-vmo-ecommerce-preset="product_flavor_selector quick_view_modal">'
            . '<div class="vf-head">'
            . '<h2>Available variants</h2>'
            . '<p>Choose the option that best fits your needs.</p>'
            . '</div>'
            . '<div class="vf-grid">' . $cards . '</div>'
            . '</section>';
    }

    private function generate_shop_page($business_data, $media_assets) {
        $business_data = $this->ensure_ecommerce_product_packets($business_data, $media_assets);
        $products = ($business_data['ecommerce']['products'] ?? array());
        $merchant_id = get_current_user_id();
        $biz = esc_html($business_data['business_name'] ?? 'Your Store');

        $cards = '';
        foreach ($products as $p) {
            if (!is_array($p)) {
                continue;
            }
            $content_packet = is_array($p['content_packet'] ?? null) ? $p['content_packet'] : (is_array($p['product_content_packet'] ?? null) ? $p['product_content_packet'] : $this->build_product_content_packet($p, $business_data, $products, 0));
            $media_packet = is_array($p['media_packet'] ?? null) ? $p['media_packet'] : (is_array($p['product_media_packet'] ?? null) ? $p['product_media_packet'] : $this->build_product_media_packet($p, $content_packet, $business_data, $media_assets, 0));
            $price = $this->product_price_number($p);
            $price_cents = intval(round($price * 100));
            $title_raw = sanitize_text_field((string) ($content_packet['title'] ?? $p['name'] ?? 'Product'));
            $title = esc_html($title_raw);
            $desc_raw = sanitize_textarea_field((string) ($content_packet['short_description'] ?? $p['summary'] ?? ''));
            $desc  = esc_html($desc_raw);

            $slug = sanitize_title((string) ($content_packet['slug'] ?? $p['slug'] ?? $title_raw));
            $product_url = esc_url((string) home_url('/product-' . $slug));

            $product_id = isset($p['id']) ? intval($p['id']) : 0; // if known from catalog
            $currency = strtolower(sanitize_text_field((string) ($p['currency'] ?? 'usd')));
            $buy   = '<button class="vmo-buy-button pc-buy" type="button" data-vmo-buy data-merchant="' . intval($merchant_id) . '" data-product-id="' . intval($product_id) . '" data-amount="' . intval($price_cents) . '" data-name="' . esc_attr($title_raw) . '" data-desc="' . esc_attr($desc_raw) . '" data-currency="' . esc_attr($currency) . '">Buy Now</button>';
            $imgTag = $this->render_product_media_slot($media_packet['product_card_image'] ?? array(), 'vmo-zoomable pc-product-image');

            $priceTag = $price > 0 ? '<div class="pc-price">' . esc_html('$' . number_format($price, 2)) . '</div>' : '';
            $benefits = is_array($content_packet['key_benefits'] ?? null) ? array_slice($content_packet['key_benefits'], 0, 2) : array();
            $benefit_html = $benefits ? '<ul class="pc-benefits">' . implode('', array_map(function($benefit) { return '<li>' . esc_html($benefit) . '</li>'; }, $benefits)) . '</ul>' : '';

            $cards .= '<article class="product-card product-story-card" data-vmo-reveal data-vmo-product-card data-product-detail="' . esc_attr($slug) . '">'
                . '<a class="pc-media" href="'.$product_url.'">'.$imgTag.'</a>'
                . '<div class="pc-body">'
                . '<a class="pc-title" href="'.$product_url.'"><h3>'.$title.'</h3></a>'
                . $priceTag
                . ($desc ? '<p class="pc-desc">'.$desc.'</p>' : '')
                . $benefit_html
                . '<p class="pc-story">' . esc_html($content_packet['product_story'] ?? 'Review the product story, detail packet, fulfillment notes, and checkout path before buying.') . '</p>'
                . '<div class="pc-actions">'.$buy.'<a class="pc-link" data-vmo-quick-view="' . esc_attr($slug) . '" href="'.$product_url.'">View details</a></div>'
                . '</div>'
                . '</article>';
        }

        if (!$cards) {
            // Empty state with CTA; still shows high-end layout and an image placeholder.
                        $cards = '<article class="product-card product-story-card" data-vmo-reveal data-vmo-product-card data-product-detail="featured-product">'
                . '<div class="pc-media">[GENERATED_IMAGE: premium featured product photo for an online store]</div>'
                . '<div class="pc-body"><h3>Featured Product</h3><p class="pc-desc">Your store is ready. Add products to start selling.</p></div>'
                . '</article>';
        }

                $html = '<div class="shop-page ecommerce-storytelling" id="vmo-shop-scope" data-vmo-visual-preset="ecommerce_storytelling" data-vmo-ecommerce-preset="product_story_scroll scroll_reveal_product_cards quick_view_modal">'
                            . '<header class="shop-hero product-story-hero" data-vmo-hero-preset="product-first-hero">'
              .   '<div class="shop-hero-bg vmo-media-placeholder vmo-media-placeholder-image" data-type="image" data-media-id="media_' . wp_generate_password(8, false) . '" data-vmo-generate="true" data-vmo-prompt="abstract premium ecommerce hero background, soft depth, clean modern" aria-hidden="true"></div>'
              .   '<div class="shop-hero-inner" data-vmo-reveal>'
                            .     '<p class="shop-kicker">Product Story Collection</p>'
              .     '<h1>Shop ' . $biz . '</h1>'
                            .     '<p>Premium products, curated with clear detail paths, quick buying, and story-led collection browsing.</p>'
              .   '</div>'
              . '</header>'
                            . '<section class="shop-story-section" data-vmo-reveal data-vmo-ecommerce-preset="product_story_scroll">'
                            .   '<div class="story-lead"><p>Collection Story</p><h2>Browse by outcome, inspect the detail, then buy without losing context.</h2></div>'
                            . '</section>'
                            . '<section class="shop-story-collection product-story-section" data-vmo-ecommerce-preset="scroll_reveal_product_cards">'
                            .   '<div class="shop-grid">'.$cards.'</div>'
                            . '</section>'
                            . '<section class="quick-view-modal product-detail-panel" data-vmo-ecommerce-preset="quick_view_modal" aria-label="Product quick view" hidden>'
                            .   '<div class="quick-view-content"><h2>Product details</h2><p>Select a product to inspect media, benefits, and purchase details.</p></div>'
                            . '</section>'
              . '</div>';

        // Background image placeholder is an explicit media layer so the queue can resolve it reliably.
        $html .= '<style>
        .shop-page{max-width:1400px;margin:0 auto;padding:0 16px}
        .shop-hero{position:relative;border-radius:16px;overflow:hidden;margin:16px 0 24px;border:1px solid #eee;box-shadow:0 4px 20px rgba(0,0,0,.06)}
        .shop-hero-bg{position:absolute;inset:0;background-size:cover;background-position:center;transform:translateZ(0)}
        .shop-hero-bg:after{content:"";position:absolute;inset:0;background:rgba(255,255,255,.6)}
        .shop-hero-inner{position:relative;padding:42px 24px}
        .shop-kicker{margin:0 0 10px 0;text-transform:uppercase;font-size:12px;letter-spacing:.08em;font-weight:700}
        .shop-hero h1{margin:0 0 8px 0;line-height:1.05}
        .shop-hero p{margin:0;max-width:680px}
        .shop-story-section{border:1px solid #eee;border-radius:14px;padding:18px;margin:0 0 18px;background:#fff;box-shadow:0 4px 20px rgba(0,0,0,.05)}
        .story-lead p{margin:0 0 6px;text-transform:uppercase;font-size:12px;letter-spacing:.08em;font-weight:700}
        .story-lead h2{margin:0;max-width:760px;line-height:1.12}
        .shop-story-collection{margin:0 0 20px}
        .shop-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:20px}
        .product-card{background:#fff;border:1px solid #eee;border-radius:14px;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,.06);transform:translateZ(0)}
        .pc-media{display:block;position:relative;overflow:hidden}
        .pc-media img{width:100%;height:auto;display:block;transition:transform .55s ease}
        .product-card:hover .pc-media img{transform:scale(1.06)}
        .pc-body{padding:14px 14px 16px 14px}
        .pc-title{text-decoration:none;color:inherit;display:block}
        .pc-title h3{margin:0 0 6px 0}
        .pc-price{margin:0 0 10px 0}
        .pc-desc{margin:0 0 12px 0}
        .pc-benefits{margin:0 0 12px 0;padding-left:18px;color:#334155;font-size:.9rem;line-height:1.45}
        .pc-story{margin:0 0 12px;color:#475569;font-size:.92rem;line-height:1.45}
        .pc-actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
        .pc-buy{border:0;border-radius:999px;padding:9px 14px;background:#0f172a;color:#fff;font:inherit;font-weight:700;cursor:pointer}
        .pc-link{text-decoration:none;border:1px solid #eee;border-radius:999px;padding:8px 12px;display:inline-block}
        .quick-view-modal[hidden]{display:none}
        .quick-view-modal{position:fixed;inset:auto 24px 24px auto;max-width:360px;background:#fff;border:1px solid #eee;border-radius:14px;padding:16px;box-shadow:0 18px 55px rgba(15,23,42,.18);z-index:20}
        [data-vmo-reveal]{opacity:0;transform:translateY(16px)}
        @media(max-width:900px){.shop-hero-inner{padding:28px 18px}}
        </style>';

        $html .= $this->ensure_gsap_loader_snippet('vmo-shop-scope');

        $html .= '<script>(function(){
            var root = document.getElementById("vmo-shop-scope");
            if(!root) return;

            // Image zoom viewer
            root.querySelectorAll(".vmo-zoomable").forEach(function(img){
                img.addEventListener("click",function(e){
                    e.preventDefault();
                    var w=window.open("","_blank");
                    if(w){ w.document.write("<img src=\\\""+img.src+"\\\" style=\\\"width:100%;height:auto\\\" />"); }
                });
            });

            var quickView = root.querySelector(".quick-view-modal");
            root.querySelectorAll("[data-vmo-quick-view]").forEach(function(link){
                link.addEventListener("click",function(event){
                    if(!quickView) return;
                    event.preventDefault();
                    var card = link.closest("[data-vmo-product-card]");
                    if(!card) return;
                    var title = card.querySelector("h3") ? card.querySelector("h3").textContent : "Product details";
                    var desc = card.querySelector(".pc-desc") ? card.querySelector(".pc-desc").textContent : "Review product details before buying.";
                    quickView.hidden = false;
                    quickView.querySelector(".quick-view-content").innerHTML = "<h2>"+title+"</h2><p>"+desc+"</p><p>Click through for the full product story and checkout path.</p>";
                });
            });

            function revealFallback(){
                var els = root.querySelectorAll("[data-vmo-reveal]");
                var io = new IntersectionObserver(function(entries){
                    entries.forEach(function(en){
                        if(!en.isIntersecting) return;
                        en.target.style.transition = "opacity .6s ease, transform .6s ease";
                        en.target.style.opacity = "1";
                        en.target.style.transform = "translateY(0)";
                        io.unobserve(en.target);
                    });
                }, {threshold:0.12});
                els.forEach(function(el){ io.observe(el); });
            }

            try {
                window.__vmoLoadGsap && window.__vmoLoadGsap().then(function(){
                    if(!window.gsap || !window.ScrollTrigger){ revealFallback(); return; }
                    gsap.registerPlugin(ScrollTrigger);

                    gsap.utils.toArray(root.querySelectorAll("[data-vmo-reveal]")).forEach(function(el){
                        gsap.to(el,{
                            opacity:1,
                            y:0,
                            duration:0.7,
                            ease:"power2.out",
                            scrollTrigger:{trigger:el,start:"top 85%"}
                        });
                    });

                    var bg = root.querySelector(".shop-hero-bg");
                    if(bg){
                        gsap.to(bg,{
                            y: -40,
                            ease:"none",
                            scrollTrigger:{trigger:root.querySelector(".shop-hero"),start:"top top",end:"bottom top",scrub:true}
                        });
                    }
                });
            } catch(e){ revealFallback(); }
        })();</script>';

        return $this->process_generated_page_html($html, array(
            'page_type' => 'shop',
            'session_id' => $business_data['session_id'] ?? '',
            'user_id' => get_current_user_id(),
            'project_id' => 0
        ));
    }

    /**
     * Auto-provision Stripe Connect account and upsert products into catalog.
     * Collects minimal KYC/business info from consultation data to prefill the account,
     * then requires Account Link completion in background when Stripe requires additional fields.
     */
    private function auto_setup_payments_and_store($business_data, $skip_products = false) {
        $merchant_user_id = get_current_user_id();
        // 1) Ensure Stripe SDK configured and create/retrieve connected account
        try {
            if (defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED && get_option('vmo_stripe_secret_key')) {
                if (!class_exists('Stripe\\Stripe')) {
                    require_once VMO_AI_BUILDER_DIR . 'stripe-sdk/init.php';
                }
                \Stripe\Stripe::setApiKey(get_option('vmo_stripe_secret_key'));
                $this->ensure_connected_account($merchant_user_id, array(
                    'email' => $this->extract_owner_email($business_data),
                    'business_type' => $this->infer_business_type($business_data),
                    'business_name' => $business_data['business_name'] ?? '',
                    'product_description' => $skip_products ? $this->summarize_collection_reason($business_data) : $this->summarize_products($business_data)
                ));
            }
        } catch (\Throwable $e) {
            error_log('VMO auto-setup: Stripe account provision failed: ' . $e->getMessage());
        }

        // 2) Upsert catalog directly into DB and return products with IDs attached
        if ($skip_products) return array();
        $products = is_array($business_data['ecommerce']['products'] ?? null) ? $business_data['ecommerce']['products'] : array();
        if (!$products) return array();
        $with_ids = $this->upsert_products_direct($merchant_user_id, $products);
        return $with_ids;
    }

    private function summarize_collection_reason($business_data) {
        $mode = (string) ($business_data['ecommerce']['collection_mode'] ?? 'products');
        if ($mode === 'donations') {
            $funds = is_array($business_data['ecommerce']['donation_funds'] ?? null) ? $business_data['ecommerce']['donation_funds'] : array();
            $names = array();
            foreach ($funds as $f) {
                if (is_array($f) && !empty($f['name'])) $names[] = (string) $f['name'];
            }
            $names = array_slice($names, 0, 5);
            return $names ? ('Collects donations for ' . implode(', ', $names) . (count($names) >= 5 ? '…' : '')) : 'Collects donations and contributions via VMO';
        }
        return 'Collects payments via VMO';
    }

    private function ensure_connected_account($user_id, $kyc) {
        global $wpdb; $table = $wpdb->prefix . 'vmo_connected_accounts';
        $existing = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE user_id = %d", $user_id), ARRAY_A);
        if ($existing && !empty($existing['stripe_account_id'])) return $existing['stripe_account_id'];

        // Create minimal Custom account; fuller onboarding via Account Link happens later
        $country = 'US';
        $email = sanitize_email($kyc['email'] ?? (wp_get_current_user()->user_email ?? ''));
        $business_type = in_array(($kyc['business_type'] ?? 'individual'), array('individual','company')) ? $kyc['business_type'] : 'individual';
        $acct = \Stripe\Account::create(array(
            'type' => 'custom',
            'country' => $country,
            'email' => $email,
            'business_type' => $business_type,
            'capabilities' => array(
                'card_payments' => array('requested' => true),
                'transfers' => array('requested' => true)
            ),
            'business_profile' => array(
                'name' => sanitize_text_field($kyc['business_name'] ?? ''),
                'product_description' => sanitize_text_field($kyc['product_description'] ?? 'Online store via VMO')
            )
        ));
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table, array(
            'user_id' => $user_id,
            'stripe_account_id' => $acct->id,
            'account_type' => 'custom',
            'details_submitted' => intval($acct->details_submitted),
            'payouts_enabled' => intval($acct->payouts_enabled),
            'charges_enabled' => intval($acct->charges_enabled),
            'capabilities' => wp_json_encode($acct->capabilities)
        ));
        return $acct->id;
    }

    private function upsert_products_direct($user_id, $products) {
        global $wpdb; $table = $wpdb->prefix . 'vmo_store_products';
        $out = array();
        foreach ($products as $p) {
            $name = sanitize_text_field($p['name'] ?? 'Product');
            $slug = sanitize_title($p['slug'] ?? $name);
            $desc = wp_kses_post($p['description'] ?? ($p['summary'] ?? ''));
            $type = $this->map_product_type($p);
            $price_cents = intval(round((float)($p['price'] ?? 0) * 100));
            $currency = strtolower(sanitize_text_field($p['currency'] ?? 'usd'));
            $sku = sanitize_text_field($p['sku'] ?? '');
            $stock_tracking = !empty($p['track_stock']) ? 1 : 0;
            $stock_qty = intval($p['stock_qty'] ?? ($p['inventory'] ?? 0));
            $shipping_required = !empty($p['shipping_required']) ? 1 : 0;
            $shipping_profile = isset($p['shipping_profile']) ? wp_json_encode($p['shipping_profile']) : null;
            $download_url = isset($p['download_url']) ? esc_url_raw($p['download_url']) : null;
            $download_limit = isset($p['download_limit']) ? intval($p['download_limit']) : null;
            $interval = in_array(($p['interval'] ?? ($p['recurring_interval'] ?? null)), array('day','week','month','year')) ? ($p['interval'] ?? $p['recurring_interval']) : null;
            $interval_count = isset($p['interval_count']) ? max(1, intval($p['interval_count'])) : (isset($p['recurring_interval_count']) ? max(1, intval($p['recurring_interval_count'])) : null);
            $images = isset($p['images']) && is_array($p['images']) ? wp_json_encode(array_values(array_map('esc_url_raw', $p['images']))) : (isset($p['image']) ? wp_json_encode(array(esc_url_raw($p['image']))) : null);

            $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM $table WHERE user_id=%d AND slug=%s", $user_id, $slug));
            if ($exists) {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->update($table, array(
                    'name'=>$name,'description'=>$desc,'product_type'=>$type,'price_cents'=>$price_cents,'currency'=>$currency,
                    'sku'=>$sku,'stock_tracking'=>$stock_tracking,'stock_qty'=>$stock_qty,'shipping_required'=>$shipping_required,
                    'shipping_profile'=>$shipping_profile,'download_url'=>$download_url,'download_limit'=>$download_limit,
                    'recurring_interval'=>$interval,'recurring_interval_count'=>$interval_count,'images'=>$images
                ), array('id'=>$exists));
                $id = (int)$exists;
            } else {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->insert($table, array(
                    'user_id'=>$user_id,'name'=>$name,'slug'=>$slug,'description'=>$desc,'product_type'=>$type,
                    'price_cents'=>$price_cents,'currency'=>$currency,'sku'=>$sku,'stock_tracking'=>$stock_tracking,
                    'stock_qty'=>$stock_qty,'shipping_required'=>$shipping_required,'shipping_profile'=>$shipping_profile,
                    'download_url'=>$download_url,'download_limit'=>$download_limit,'recurring_interval'=>$interval,
                    'recurring_interval_count'=>$interval_count,'images'=>$images
                ));
                $id = (int)$wpdb->insert_id;
            }
            // return product enriched with ID for page generation
            $p['id'] = $id; $p['slug'] = $slug; $p['price'] = isset($p['price']) ? $p['price'] : ($price_cents/100);
            $out[] = $p;
        }
        return $out;
    }

    private function map_product_type($p) {
        if (!empty($p['download_url']) || (!empty($p['type']) && $p['type']==='downloadable')) return 'downloadable';
        if (!empty($p['interval']) || (!empty($p['type']) && $p['type']==='subscription')) return 'subscription';
        if (!empty($p['type']) && in_array($p['type'], ['service'])) return 'service';
        return 'item';
    }

    private function infer_business_type($business_data) {
        $t = strtolower((string)($business_data['business_type'] ?? ''));
        return (strpos($t,'llc')!==false || strpos($t,'company')!==false) ? 'company' : 'individual';
    }

    private function extract_owner_email($business_data) {
        $ci = (array)($business_data['contact_info'] ?? array());
        foreach (['business_email','owner_email','email'] as $k) { if (!empty($ci[$k])) return $ci[$k]; }
        return wp_get_current_user()->user_email ?? '';
    }

    private function summarize_products($business_data) {
        $names = array();
        foreach ((array)($business_data['ecommerce']['products'] ?? array()) as $p) { if (!empty($p['name'])) $names[] = $p['name']; }
        return $names ? ('Sells '.implode(', ', array_slice($names,0,5)).(count($names)>5?'…':'')) : 'Online store via VMO';
    }

    private function call_internal_rest($route, $data) {
        // Minimal internal call using WordPress HTTP API
        $url = rest_url($route);
        $args = array(
            'headers' => array('Content-Type' => 'application/json'),
            'body' => wp_json_encode($data),
            'timeout' => 30, // ✅ BOTTLENECK FIX: was 8s — fails on shared/slow hosting loopback
            'method' => 'POST'
        );
        // Use current cookies for auth if needed
        $resp = wp_remote_request($url, $args);
        return $resp;
    }

    private function generate_product_page($product, $business_data, $media_assets) {
        $merchant_id = get_current_user_id();
        $product = is_array($product) ? $product : array();
        $content_packet = is_array($product['content_packet'] ?? null) ? $product['content_packet'] : (is_array($product['product_content_packet'] ?? null) ? $product['product_content_packet'] : $this->build_product_content_packet($product, $business_data, is_array($business_data['ecommerce']['products'] ?? null) ? $business_data['ecommerce']['products'] : array($product), 0));
        $media_packet = is_array($product['media_packet'] ?? null) ? $product['media_packet'] : (is_array($product['product_media_packet'] ?? null) ? $product['product_media_packet'] : $this->build_product_media_packet($product, $content_packet, $business_data, $media_assets, 0));
        $title_raw = sanitize_text_field((string) ($content_packet['title'] ?? $product['name'] ?? 'Product'));
        $title = esc_html($title_raw);
        $price = $this->product_price_number($product);
        $price_cents = intval(round($price * 100));
        $price_txt = $price > 0 ? esc_html('$' . number_format($price, 2)) : '';
        $summary_raw = sanitize_textarea_field((string) ($content_packet['short_description'] ?? $product['summary'] ?? ''));
        $summary = esc_html($summary_raw);
        $details = esc_html((string) ($content_packet['long_description'] ?? ''));
        $story = esc_html((string) ($content_packet['product_story'] ?? $content_packet['marketing_description'] ?? ''));
        $benefitList = $this->render_packet_list($content_packet['key_benefits'] ?? array());
        $technicalList = $this->render_packet_detail_map($content_packet['technical_details'] ?? array());
        $relevantList = $this->render_packet_detail_map($content_packet['relevant_details'] ?? array());
        $variantList = $this->render_packet_list($content_packet['variant_option_descriptions'] ?? array());
        $faqHtml = $this->render_packet_faq($content_packet['faq'] ?? $content_packet['product_faq'] ?? array());
        $upsellList = $this->render_packet_list($content_packet['upsell_cross_sell_copy'] ?? array());
        $shipping_notes = esc_html((string) ($content_packet['shipping_fulfillment_notes'] ?? ''));
        $gallery_assets = array_merge(
            array($media_packet['product_hero_image'] ?? array()),
            is_array($media_packet['gallery_images'] ?? null) ? $media_packet['gallery_images'] : array(),
            is_array($media_packet['detail_images'] ?? null) ? $media_packet['detail_images'] : array(),
            array($media_packet['product_lifestyle_image'] ?? array(), $media_packet['mobile_fallback_image'] ?? array())
        );
        $gallery = '';
        foreach (array_filter($gallery_assets) as $asset) {
            $gallery .= '<div class="pg-item">' . $this->render_product_media_slot($asset, 'vmo-zoomable pg-image') . '</div>';
        }
        $product_id = isset($product['id']) ? intval($product['id']) : 0;
        $currency = strtolower(sanitize_text_field((string) ($product['currency'] ?? 'usd')));
        $buy = '<button class="vmo-buy-button pd-buy-button" type="button" data-vmo-buy data-merchant="' . intval($merchant_id) . '" data-product-id="' . intval($product_id) . '" data-amount="' . intval($price_cents) . '" data-name="' . esc_attr($title_raw) . '" data-desc="' . esc_attr($summary_raw) . '" data-currency="' . esc_attr($currency) . '">Buy Now</button>';

        $variant_section = $this->build_variant_family_section($product, $business_data);
        $schema = is_array($content_packet['product_schema'] ?? null) ? $content_packet['product_schema'] : array('@context' => 'https://schema.org', '@type' => 'Product', 'name' => $title_raw);
        $schema['offers'] = is_array($content_packet['offer_schema'] ?? null) ? $content_packet['offer_schema'] : $this->build_product_offer_schema($product);
        $schema_json = wp_json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        $seo_title = esc_html((string) ($content_packet['seo_title'] ?? $title_raw));
        $meta_description = esc_attr((string) ($content_packet['meta_description'] ?? $summary_raw));

          $html = '<div class="product-page product-story-scroll" id="vmo-product-scope" data-vmo-visual-preset="product_story_scroll" data-vmo-ecommerce-preset="product_story_scroll click-to-detail quick_view_modal">'
              . '<title>' . $seo_title . '</title><meta name="description" content="' . $meta_description . '"><script type="application/ld+json">' . esc_html($schema_json) . '</script>'
              . '<section class="product-hero" data-vmo-hero-preset="scroll_zoom_product_reveal">'
              .   $this->render_product_media_slot($media_packet['mobile_fallback_image'] ?? array(), 'ph-bg')
              .   '<div class="product-detail" data-product-detail-panel data-vmo-ecommerce-preset="product-detail detail-panel">'
              .     '<div class="pg product-story-gallery" data-vmo-reveal>'.$gallery.'</div>'
              .     '<aside class="pd product-detail-panel" data-vmo-reveal>'
              .       '<div class="pd-top">'
              .         '<p class="pd-kicker">Click-to-detail product story</p>'
              .         '<h1>'.$title.'</h1>'
              .         ($price_txt ? '<div class="pd-price">'.$price_txt.'</div>' : '')
              .         '<p class="summary">'.$summary.'</p>'
              .       '</div>'
              .       '<div class="pd-buy">'.$buy.'</div>'
              .       '<div class="pd-panels">'
              .         '<div class="pd-panel"><h3>Benefits</h3>'.$benefitList.'</div>'
              .         '<div class="pd-panel"><h3>Specifications</h3>'.$technicalList.'</div>'
              .       '</div>'
              .     '</aside>'
              .   '</div>'
              . '</section>'
              . $variant_section
              . '<section class="product-story-section ingredient-float-layers" data-vmo-reveal data-vmo-ecommerce-preset="product_story_scroll product_ingredient_float_layers">'
              .   '<div class="story-lead"><p>Product Story</p><h2>Why this belongs in the customer journey</h2><p>' . $story . '</p></div>'
              .   '<div class="story-layers">'
              .     '<article><span>01</span><h3>Inspect</h3><p>' . $details . '</p></article>'
              .     '<article><span>02</span><h3>Compare Options</h3>' . $variantList . '</article>'
              .     '<article><span>03</span><h3>Fulfillment</h3><p>' . $shipping_notes . '</p></article>'
              .   '</div>'
              . '</section>'
              . '<section class="pd-details" data-vmo-reveal>'
              .   '<div><h2>Product Details</h2>' . $relevantList . '</div>'
              .   '<div><h2>FAQ</h2>' . $faqHtml . '</div>'
              .   '<div><h2>Recommended Next Steps</h2>' . $upsellList . '</div>'
              . '</section>'
              . '</div>';

        $html .= '<style>
        .product-page{max-width:1400px;margin:0 auto;padding:0 16px}
        .product-hero{position:relative;border-radius:16px;overflow:hidden;margin:16px 0 24px;border:1px solid #eee;box-shadow:0 4px 20px rgba(0,0,0,.06)}
        .ph-bg{position:absolute;inset:0;background-size:cover;background-position:center;transform:translateZ(0)}
        img.ph-bg{width:100%;height:100%;object-fit:cover;display:block}
        .ph-bg:after{content:"";position:absolute;inset:0;background:rgba(255,255,255,.7)}
        .product-detail{position:relative;display:grid;grid-template-columns:1.2fr 1fr;gap:24px;align-items:start;padding:18px}
        .pg{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
        .pg .pg-item{border-radius:12px;overflow:hidden;transform:translateZ(0)}
        .pg img,.pg .vmo-media-placeholder{width:100%;min-height:220px;display:block;cursor:zoom-in;background:#f8fafc}
        .pd{position:sticky;top:18px;align-self:start;background:#fff;border:1px solid #eee;border-radius:14px;padding:16px;box-shadow:0 4px 20px rgba(0,0,0,.06)}
        .pd h1{margin:0 0 6px 0}
        .pd-kicker{margin:0 0 8px;text-transform:uppercase;font-size:12px;letter-spacing:.08em;font-weight:700;color:#475569}
        .pd-price{margin:0 0 10px 0}
        .pd .summary{margin:0 0 12px 0}
        .pd-buy{margin:12px 0 14px 0}
        .pd-buy-button{border:0;border-radius:999px;padding:11px 16px;background:#0f172a;color:#fff;font:inherit;font-weight:700;cursor:pointer;width:100%}
        .pd ul{padding-left:18px;margin:10px 0 0 0}
        .pd h3{margin:0}
        .pd-panels{display:grid;gap:12px}
        .pd-panel{border:1px solid #eee;border-radius:12px;padding:12px}
        .pd-details{background:#fff;border:1px solid #eee;border-radius:14px;padding:16px;box-shadow:0 4px 20px rgba(0,0,0,.06);margin:0 0 24px 0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
        .pd-details h2{margin:0 0 10px 0}
        .product-story-section{background:#fff;border:1px solid #eee;border-radius:14px;padding:18px;box-shadow:0 4px 20px rgba(0,0,0,.06);margin:0 0 18px 0}
        .story-lead p{margin:0 0 6px;text-transform:uppercase;font-size:12px;letter-spacing:.08em;font-weight:700;color:#475569}
        .story-lead h2{margin:0 0 16px;line-height:1.15}
        .story-layers{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
        .story-layers article{border:1px solid #eee;border-radius:12px;padding:14px;background:#f8fafc}
        .story-layers span{display:inline-flex;width:30px;height:30px;border-radius:999px;background:#0f172a;color:#fff;align-items:center;justify-content:center;font-size:12px;font-weight:700;margin-bottom:10px}
        .story-layers h3{margin:0 0 6px}.story-layers p{margin:0;color:#475569;line-height:1.55}
        .pd-details ul,.pd-panel ul,.story-layers ul{margin:0;padding-left:18px;color:#475569;line-height:1.55}
        .pd-details dl,.pd-panel dl{margin:0;display:grid;gap:8px}.pd-details dt,.pd-panel dt{font-weight:700}.pd-details dd,.pd-panel dd{margin:0;color:#475569}
        .pd-faq{display:grid;gap:10px}.pd-faq details{border:1px solid #eee;border-radius:10px;padding:10px}.pd-faq summary{cursor:pointer;font-weight:700}
        .variant-family{background:#fff;border:1px solid #eee;border-radius:14px;padding:16px;box-shadow:0 4px 20px rgba(0,0,0,.06);margin:0 0 18px 0}
        .vf-head h2{margin:0 0 6px 0}
        .vf-head p{margin:0 0 14px 0}
        .vf-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}
        .vf-card{display:grid;grid-template-columns:68px 1fr;gap:10px;align-items:center;text-decoration:none;color:inherit;border:1px solid #eee;border-radius:12px;padding:10px;transform:translateZ(0);transition:transform .25s ease}
        .vf-card:hover{transform:translateY(-2px)}
        .vf-card.is-active{border-width:2px}
        .vf-thumb{width:68px;height:68px;border-radius:10px;overflow:hidden}
        .vf-thumb img{width:100%;height:100%;object-fit:cover;display:block}
        .vf-label{font-size:12px}
        .vf-title{font-weight:600;line-height:1.2}
        .vf-price{margin-top:4px}
        [data-vmo-reveal]{opacity:0;transform:translateY(16px)}
        @media(max-width:900px){.product-detail{grid-template-columns:1fr}.pd{position:relative;top:auto}.story-layers,.pd-details{grid-template-columns:1fr}}
        </style>';

        $html .= $this->ensure_gsap_loader_snippet('vmo-product-scope');

        $html .= '<script>(function(){
            var root = document.getElementById("vmo-product-scope");
            if(!root) return;

            // Image zoom viewer
            root.querySelectorAll(".vmo-zoomable").forEach(function(img){
                img.addEventListener("click",function(e){
                    e.preventDefault();
                    var w=window.open("","_blank");
                    if(w){ w.document.write("<img src=\\\""+img.src+"\\\" style=\\\"width:100%;height:auto\\\" />"); }
                });
            });

            function revealFallback(){
                var els = root.querySelectorAll("[data-vmo-reveal]");
                var io = new IntersectionObserver(function(entries){
                    entries.forEach(function(en){
                        if(!en.isIntersecting) return;
                        en.target.style.transition = "opacity .6s ease, transform .6s ease";
                        en.target.style.opacity = "1";
                        en.target.style.transform = "translateY(0)";
                        io.unobserve(en.target);
                    });
                }, {threshold:0.12});
                els.forEach(function(el){ io.observe(el); });
            }

            try {
                window.__vmoLoadGsap && window.__vmoLoadGsap().then(function(){
                    if(!window.gsap || !window.ScrollTrigger){ revealFallback(); return; }
                    gsap.registerPlugin(ScrollTrigger);

                    gsap.utils.toArray(root.querySelectorAll("[data-vmo-reveal]")).forEach(function(el){
                        gsap.to(el,{
                            opacity:1,
                            y:0,
                            duration:0.75,
                            ease:"power2.out",
                            scrollTrigger:{trigger:el,start:"top 85%"}
                        });
                    });

                    var bg = root.querySelector(".ph-bg");
                    if(bg){
                        gsap.to(bg,{
                            y: -50,
                            ease:"none",
                            scrollTrigger:{trigger:root.querySelector(".product-hero"),start:"top top",end:"bottom top",scrub:true}
                        });
                    }
                });
            } catch(e){ revealFallback(); }
        })();</script>';

        return $this->process_generated_page_html($html, array(
            'page_type' => 'product',
            'session_id' => $business_data['session_id'] ?? '',
            'user_id' => get_current_user_id(),
            'project_id' => 0
        ));
    }

    private function generate_donate_page($business_data, $media_assets) {
        $merchant_id = get_current_user_id();
        $biz = esc_html($business_data['business_name'] ?? 'Support');
        $funds = is_array($business_data['ecommerce']['donation_funds'] ?? null) ? $business_data['ecommerce']['donation_funds'] : array();
        if (!$funds) {
            $funds = array(
                array('name' => 'General Donation', 'amounts' => array(2000, 5000, 10000)),
                array('name' => 'Community Support', 'amounts' => array(2500, 5000, 10000)),
            );
        }

        $cards = '';
        foreach ($funds as $idx => $f) {
            if (!is_array($f)) continue;
            $fname = sanitize_text_field((string) ($f['name'] ?? 'Donation'));
            if ($fname === '') $fname = 'Donation';
            $amounts = is_array($f['amounts'] ?? null) ? $f['amounts'] : array(2000, 5000, 10000);
            $amounts = array_values(array_filter(array_map('intval', $amounts)));
            if (!$amounts) $amounts = array(2000, 5000, 10000);

            $btns = '';
            foreach (array_slice($amounts, 0, 3) as $a) {
                $label = '$' . number_format($a / 100, 0);
                $btns .= '<button class="don-btn" data-vmo-buy data-merchant="' . intval($merchant_id) . '" data-amount="' . intval($a) . '" data-name="' . esc_attr($fname) . '" data-desc="Donation to ' . esc_attr($biz) . ': ' . esc_attr($fname) . '" data-currency="usd">Give ' . esc_html($label) . '</button>';
            }

            $custom_id = 'vmo-donate-custom-' . $idx;
            $custom_btn_id = 'vmo-donate-custom-btn-' . $idx;
            $cards .= '<article class="don-card" data-vmo-reveal>'
                . '<div class="don-card-media">[GENERATED_IMAGE: premium donation card illustration for ' . esc_html($fname) . ', minimal, modern, clean]</div>'
                . '<div class="don-card-body">'
                . '<h3>' . esc_html($fname) . '</h3>'
                . '<p>Choose an amount below or enter a custom amount.</p>'
                . '<div class="don-actions">' . $btns . '</div>'
                . '<div class="don-custom">'
                . '<label for="' . esc_attr($custom_id) . '">Other amount</label>'
                . '<div class="don-custom-row">'
                . '<input id="' . esc_attr($custom_id) . '" type="number" min="1" step="1" inputmode="numeric" placeholder="25" />'
                . '<button id="' . esc_attr($custom_btn_id) . '" class="don-btn-outline" type="button">Give</button>'
                . '</div>'
                . '</div>'
                . '</div>'
                . '</article>';
        }

        $html = '<div class="donate-page" id="vmo-donate-scope">'
            . '<header class="don-hero">'
            . '<div class="don-hero-bg vmo-media-placeholder vmo-media-placeholder-image" data-type="image" data-media-id="media_' . wp_generate_password(8, false) . '" data-vmo-generate="true" data-vmo-prompt="premium donation hero background, modern minimal, soft depth, clean" aria-hidden="true"></div>'
            . '<div class="don-hero-inner" data-vmo-reveal>'
            . '<h1>Support ' . $biz . '</h1>'
            . '<p>Your giving fuels impact. Choose a fund and contribute securely in seconds.</p>'
            . '</div>'
            . '</header>'
            . '<section class="don-grid">' . $cards . '</section>'
            . '<section class="don-trust" data-vmo-reveal>'
            . '<div class="don-trust-card">'
            . '<h2>Secure, fast, and transparent</h2>'
            . '<p>Payments are processed securely. Keep a record of your contribution for your reference.</p>'
            . '</div>'
            . '</section>'
            . '</div>';

        $html .= '<style>
        .donate-page{max-width:1400px;margin:0 auto;padding:0 16px}
        .don-hero{position:relative;border-radius:16px;overflow:hidden;margin:16px 0 24px;border:1px solid #eee;box-shadow:0 4px 20px rgba(0,0,0,.06)}
        .don-hero-bg{position:absolute;inset:0;background-size:cover;background-position:center;transform:translateZ(0)}
        .don-hero-bg:after{content:"";position:absolute;inset:0;background:rgba(255,255,255,.65)}
        .don-hero-inner{position:relative;padding:42px 24px}
        .don-hero h1{margin:0 0 8px 0;line-height:1.05}
        .don-hero p{margin:0;max-width:760px}
        .don-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px}
        .don-card{background:#fff;border:1px solid #eee;border-radius:14px;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,.06)}
        .don-card-media img{width:100%;height:auto;display:block}
        .don-card-body{padding:14px 14px 16px 14px}
        .don-card-body h3{margin:0 0 6px 0}
        .don-card-body p{margin:0 0 12px 0}
        .don-actions{display:flex;gap:10px;flex-wrap:wrap;margin:0 0 12px 0}
        .don-btn{border:1px solid #eee;border-radius:999px;padding:10px 14px;cursor:pointer;transition:transform .2s ease}
        .don-btn:hover{transform:translateY(-1px)}
        .don-btn-outline{border:1px solid #eee;border-radius:999px;padding:10px 14px;cursor:pointer}
        .don-custom label{display:block;margin:0 0 6px 0}
        .don-custom-row{display:flex;gap:10px;align-items:center}
        .don-custom-row input{flex:1;border:1px solid #eee;border-radius:12px;padding:10px 12px}
        .don-trust{margin:18px 0 28px 0}
        .don-trust-card{background:#fff;border:1px solid #eee;border-radius:14px;padding:16px;box-shadow:0 4px 20px rgba(0,0,0,.06)}
        [data-vmo-reveal]{opacity:0;transform:translateY(16px)}
        @media(max-width:900px){.don-hero-inner{padding:28px 18px}}
        </style>';

        $html .= $this->ensure_gsap_loader_snippet('vmo-donate-scope');

        $html .= '<script>(function(){
            var root = document.getElementById("vmo-donate-scope");
            if(!root) return;

            // Custom amount handler
            root.querySelectorAll(".don-card").forEach(function(card, idx){
                var input = card.querySelector(".don-custom-row input");
                var btn = card.querySelector(".don-custom-row button");
                if(!input || !btn) return;
                btn.addEventListener("click", function(){
                    var v = parseFloat(input.value || "0");
                    if(!v || v <= 0) return;
                    var cents = Math.round(v * 100);
                    // Create a temporary vmo-buy button and click it
                    var tmp = document.createElement("button");
                    tmp.setAttribute("data-vmo-buy", "");
                    tmp.setAttribute("data-merchant", btn.closest(".don-card").querySelector("[data-merchant]")?.getAttribute("data-merchant") || "");
                    tmp.setAttribute("data-amount", String(cents));
                    tmp.setAttribute("data-name", btn.closest(".don-card").querySelector("h3")?.textContent || "Donation");
                    tmp.setAttribute("data-desc", "Custom donation");
                    tmp.setAttribute("data-currency", "usd");
                    document.body.appendChild(tmp);
                    tmp.click();
                    tmp.remove();
                });
            });

            function revealFallback(){
                var els = root.querySelectorAll("[data-vmo-reveal]");
                var io = new IntersectionObserver(function(entries){
                    entries.forEach(function(en){
                        if(!en.isIntersecting) return;
                        en.target.style.transition = "opacity .6s ease, transform .6s ease";
                        en.target.style.opacity = "1";
                        en.target.style.transform = "translateY(0)";
                        io.unobserve(en.target);
                    });
                }, {threshold:0.12});
                els.forEach(function(el){ io.observe(el); });
            }

            try {
                window.__vmoLoadGsap && window.__vmoLoadGsap().then(function(){
                    if(!window.gsap || !window.ScrollTrigger){ revealFallback(); return; }
                    gsap.registerPlugin(ScrollTrigger);

                    gsap.utils.toArray(root.querySelectorAll("[data-vmo-reveal]")).forEach(function(el){
                        gsap.to(el,{opacity:1,y:0,duration:0.75,ease:"power2.out",scrollTrigger:{trigger:el,start:"top 85%"}});
                    });

                    var bg = root.querySelector(".don-hero-bg");
                    if(bg){
                        gsap.to(bg,{y:-40,ease:"none",scrollTrigger:{trigger:root.querySelector(".don-hero"),start:"top top",end:"bottom top",scrub:true}});
                    }
                });
            } catch(e){ revealFallback(); }
        })();</script>';

        return $this->process_generated_page_html($html, array(
            'page_type' => 'donate',
            'session_id' => $business_data['session_id'] ?? '',
            'user_id' => get_current_user_id(),
            'project_id' => 0
        ));
    }
    
    /**
     * Generate a single page with integrated content and media
     */
    private function generate_single_page($page_type, $business_data, $media_assets, $architecture) {
        // Use existing VMO content generation with enhanced context
        $context = array(
            'page_type' => $page_type,
            'business_data' => $business_data,
            'media_assets' => $media_assets,
            'conversion_points' => $architecture['conversion_points'] ?? array(),
            'architecture' => $architecture,
            'navigation' => $architecture['navigation'] ?? array(),
            'page_title' => $architecture['page_labels'][$page_type] ?? $this->humanize_page_title($page_type, $architecture['page_labels'] ?? array()),
            'page_description' => $architecture['page_descriptions'][$page_type] ?? ''
        );
        
        // Call the enhanced content generation function
    $html = $this->call_enhanced_content_generation($page_type, $context);
    $html = $this->normalize_generated_page_markup($html);
    $html = $this->enforce_page_quality($html, $page_type, $context);

    // After generation, replace any [GENERATED_IMAGE: ...] placeholders with real images via GPT Image 1
    $processed = $this->process_generated_page_html($html, array(
            'page_type' => $page_type,
            'session_id' => $business_data['session_id'] ?? ($business_data['conversation_context']['session_id'] ?? ''),
            'user_id' => get_current_user_id(),
            'project_id' => 0,
            'business_location' => $business_data['location'] ?? ''
        ));
    $processed = $this->normalize_generated_page_markup($processed ?: $html);
    $processed = $this->enforce_page_quality($processed, $page_type, $context);

    return $processed;
    }

    private function get_page_generation_token_budget($page_type) {
        $page = is_string($page_type) ? strtolower($page_type) : '';
        if (strpos($page, 'product-') === 0) {
            return 3000; // ✅ BOTTLENECK FIX: Increased from 1700 — Tailwind utility classes need ~2-3x more tokens
        }

        // ✅ BOTTLENECK FIX v9.1: Budgets increased ~60-80% for Tailwind CSS utility-class output.
        // Tailwind classes like 'bg-gradient-to-r from-blue-500 to-purple-600 py-24 px-8' are far more
        // verbose than traditional CSS. Previous budgets caused truncated HTML output.
        $budgets = array(
            'home' => 4500,      // was 2600 — hero + services + testimonials + CTA sections
            'shop' => 4000,      // was 2300 — product grid + filters + cart integration
            'donate' => 3800,    // was 2200 — donation tiers + progress + payment forms
            'about' => 3500,     // was 2000 — team grid + timeline + mission sections
            'services' => 3500,  // was 2000 — service cards + pricing + comparison
            'portfolio' => 3700, // was 2100 — gallery grid + lightbox + categories
            'contact' => 3200,   // was 1800 — form + map + business info + social
            'booking' => 3200,   // was 1800 — calendar + time slots + confirmation
            'blog' => 3400,      // was 1900 — article grid + sidebar + pagination
        );

        return $budgets[$page] ?? 1900;
    }

    private function get_page_generation_request_overrides($page_type) {
        $token_budget = $this->get_page_generation_token_budget($page_type);

        $model = function_exists('vmo_get_optimal_model')
            ? vmo_get_optimal_model('website_building')
            : (function_exists('vmo_get_model') ? vmo_get_model('fast') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5-mini'));

        $token_params = function_exists('vmo_get_token_param')
            ? vmo_get_token_param($model, $token_budget)
            : array('max_completion_tokens' => $token_budget);

        if (!is_array($token_params) || empty($token_params)) {
            $token_params = array('max_completion_tokens' => $token_budget);
        }

        if (is_string($model) && (
            strpos($model, 'gpt-5') !== false ||
            strpos($model, 'o1') !== false ||
            strpos($model, 'o3') !== false
        )) {
            $token_params['reasoning_effort'] = 'low';
        }

        return $token_params;
    }
    
    /**
     * Enhanced content generation with media integration
     */
    private function call_enhanced_content_generation($page_type, $context) {
        $prompt = $this->build_page_generation_prompt($page_type, $context);
        $request_overrides = $this->get_page_generation_request_overrides($page_type);
        
        // Use the existing VMO content generation system
        if (function_exists('vmo_call_specialized_ai')) {
            try {
                $result = vmo_call_specialized_ai($prompt, 'website_building', $request_overrides);
            } catch (\Throwable $e) {
                error_log('VMO page generation AI error (' . $page_type . '): ' . $e->getMessage());
                $result = null;
            }

            if (is_array($result)) {
                $candidate_keys = array('content', 'html', 'output', 'text');
                foreach ($candidate_keys as $key) {
                    if (!empty($result[$key]) && is_string($result[$key])) {
                        return $this->normalize_generated_page_markup((string) $result[$key]);
                    }
                }

                if (!empty($result['data']) && is_array($result['data'])) {
                    foreach ($candidate_keys as $key) {
                        if (!empty($result['data'][$key]) && is_string($result['data'][$key])) {
                            return $this->normalize_generated_page_markup((string) $result['data'][$key]);
                        }
                    }
                }

                return $this->fallback_page_generation($page_type, $context);
            }

            if (is_string($result) && trim($result) !== '') {
                return $this->normalize_generated_page_markup($result);
            }
        }
        
        return $this->fallback_page_generation($page_type, $context);
    }

    /**
     * Normalize generated markup to remove wrappers/fences and prevent malformed output.
     */
    private function normalize_generated_page_markup($html) {
        if (!is_string($html)) {
            return '';
        }

        $normalized = preg_replace('/^\xEF\xBB\xBF/', '', $html);
        $normalized = trim((string) $normalized);

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

        $normalized = preg_replace('/^\s*```(?:html|HTML)?\s*/', '', $normalized);
        $normalized = preg_replace('/\s*```\s*$/', '', $normalized);

        return trim((string) $normalized);
    }

    /**
     * Determine if generated page HTML meets a minimum premium-quality threshold.
     */
    private function is_high_quality_page_html($html) {
        if (!is_string($html)) {
            return false;
        }

        $candidate = trim($html);
        if ($candidate === '' || strlen($candidate) < 900) {
            return false;
        }

        if (preg_match('/content generation in progress|document not available|<p>\s*<\/p>/i', $candidate)) {
            return false;
        }

        $has_heading = (bool) preg_match('/<h1\b|<h2\b/i', $candidate);
        $section_count = preg_match_all('/<(section|article)\b/i', $candidate, $section_matches);
        $has_styling = (bool) preg_match('/<style\b|class\s*=|data-vmo-reveal/i', $candidate);

        return $has_heading && $has_styling && ($section_count >= 2 || stripos($candidate, 'data-vmo-reveal') !== false);
    }

    /**
     * Enforce page quality and replace weak output with a premium fallback template.
     */
    private function enforce_page_quality($html, $page_type, $context) {
        $normalized = $this->normalize_generated_page_markup($html);
        $business_data = is_array($context['business_data'] ?? null) ? $context['business_data'] : array();

        if ($this->looks_like_vmo_plugin_blueprint($business_data)) {
            if (preg_match('/what we do best|strategy consulting|implementation services|ongoing support|premium digital experiences engineered for measurable growth|premium professional service experience/i', $normalized)) {
                error_log('VMO Quality Gate: Replacing generic service-site drift on ' . $page_type . ' with plugin-aware fallback.');
                return $this->fallback_page_generation($page_type, $context);
            }

            if (in_array($page_type, array('home', 'features', 'pricing', 'download', 'get-started'), true)
                && !preg_match('/wordpress plugin|plugin for wordpress/i', $normalized)) {
                error_log('VMO Quality Gate: Replacing plugin page missing explicit plugin positioning on ' . $page_type . '.');
                return $this->fallback_page_generation($page_type, $context);
            }
        }

        if ($this->is_high_quality_page_html($normalized)) {
            return $normalized;
        }

        error_log('VMO Quality Gate: Replacing low-quality ' . $page_type . ' output with fallback template.');
        return $this->fallback_page_generation($page_type, $context);
    }
    
    /**
     * Build comprehensive page generation prompt
     * ✅ ENHANCED v9.0.2: Silicon Valley-tier visual quality with modern animations and glassmorphism
     */
    private function build_page_generation_prompt($page_type, $context) {
        $business_data = $context['business_data'];
        $media_assets = $context['media_assets'];
        $plugin_blueprint_mode = $this->looks_like_vmo_plugin_blueprint($business_data);
        $services_list = is_array($business_data['services']) ? implode(', ', $business_data['services']) : ($business_data['services'] ?? '');
        $goals_list = is_array($business_data['goals']) ? implode(', ', $business_data['goals']) : ($business_data['goals'] ?? '');
        $architecture = is_array($context['architecture'] ?? null) ? $context['architecture'] : array();
        $page_title = sanitize_text_field((string) ($context['page_title'] ?? $this->humanize_page_title($page_type)));
        $page_description = trim(sanitize_textarea_field((string) ($context['page_description'] ?? '')));
        $navigation_items = is_array($architecture['navigation'] ?? null) ? $architecture['navigation'] : array();
        $required_pages = is_array($architecture['pages'] ?? null) ? $architecture['pages'] : array();
        $custom_pages = is_array($business_data['custom_pages'] ?? null) ? $business_data['custom_pages'] : array();
        $special_features = is_array($business_data['special_features'] ?? null) ? $business_data['special_features'] : array();
        $page_descriptions = is_array($business_data['page_descriptions'] ?? null) ? $business_data['page_descriptions'] : array();
        $brand = is_array($business_data['brand'] ?? null) ? $business_data['brand'] : array();
        $reference_sites = is_array($brand['reference_sites'] ?? null) ? $brand['reference_sites'] : array();
        $video_urls = is_array($brand['video_urls'] ?? null) ? $brand['video_urls'] : array();
        $logo_url = esc_url_raw((string) ($brand['logo_url'] ?? ''));
        $brand_palette = is_array($brand['palette'] ?? null) ? $brand['palette'] : array();
        $site_navigation = array();
        foreach ($navigation_items as $item) {
            if (!is_array($item)) continue;
            $site_navigation[] = ($item['title'] ?? $this->humanize_page_title($item['slug'] ?? '')) . ' (' . ($item['slug'] ?? '') . ')';
        }
        
        // Get page-specific design requirements
        $page_specific = $this->get_page_specific_design_requirements($page_type);
        
        if ($plugin_blueprint_mode) {
            $prompt = "Create a 'Silicon Valley' tier, stunning, premium {$page_title} page for '{$business_data['business_name']}' - a WordPress plugin product being sold through its own public website.\n\n";
        } else {
            $prompt = "Create a 'Silicon Valley' tier, stunning, premium {$page_title} page for '{$business_data['business_name']}' - a {$business_data['business_type']} business in the {$business_data['industry']} industry.\n\n";
        }

        // ✅ v9.2.0: Inject design mode visual grammar for mode-aware generation
        if (class_exists('VMO_Elite_Consultation_Prompts')) {
            $elite = VMO_Elite_Consultation_Prompts::get_instance();
            $design_mode = $elite->get_design_mode_for_business(
                $business_data['business_type'] ?? '',
                $business_data['industry'] ?? ''
            );
            $prompt .= $elite->get_visual_grammar($design_mode);
            if ($page_type === 'home') {
                $prompt .= $elite->get_hero_specialist_rules($design_mode);
            }
            $prompt .= $elite->get_motion_grammar($design_mode);
            $prompt .= $elite->get_conversion_copy_rules($design_mode);
            $prompt .= "\n";
        }

        if (function_exists('vmo_get_visual_preset_registry')) {
            $canonical_context = is_array($business_data['canonical_generation_context'] ?? null)
                ? $business_data['canonical_generation_context']
                : vmo_get_visual_preset_registry()->build_generation_context($business_data, 'runtime_generation');
            $prompt .= vmo_get_visual_preset_registry()->render_generation_brief($canonical_context);
        }

        // ✅ Premium ~18-agent design directive: executed once per generation as
        // real LLM calls, cached per session, and injected into every page so
        // the agents actually drive the build. Fail-safe: empty brief => the
        // existing template/single-call path is used unchanged.
        if (function_exists('vmo_get_website_agent_runtime')) {
            try {
                $design_directive_brief = vmo_get_website_agent_runtime()->get_directive_brief($business_data);
                if (is_string($design_directive_brief) && trim($design_directive_brief) !== '') {
                    $prompt .= "\n" . $design_directive_brief . "\n";
                }
            } catch (\Throwable $e) {
                error_log('VMO design directive injection skipped: ' . $e->getMessage());
            }
        }
        
        $prompt .= "BUSINESS CONTEXT:\n";
        $prompt .= "- Business: {$business_data['business_name']}\n";
        $prompt .= "- Industry: {$business_data['industry']}\n";
        $prompt .= "- Location: {$business_data['location']}\n";
        $prompt .= "- Target Audience: {$business_data['target_audience']}\n";
        $prompt .= "- Services: {$services_list}\n";
        $prompt .= "- Goals: {$goals_list}\n";
        if (!empty($brand_palette)) {
            $prompt .= "- Brand Palette / Color Direction: " . implode(', ', $brand_palette) . "\n";
        }
        $prompt .= "\nSITE ARCHITECTURE (MANDATORY):\n";
        $prompt .= "- Current Page Slug: {$page_type}\n";
        $prompt .= "- Current Page Title: {$page_title}\n";
        $default_required_pages = $plugin_blueprint_mode ? implode(', ', $this->get_vmo_plugin_default_pages()) : 'home, about, services, faq, contact';
        $default_navigation = $plugin_blueprint_mode ? 'Home | Features | Pricing | About | Affiliates | Download | Docs | FAQ | Contact' : 'Home | About | Services | FAQ | Contact';
        $prompt .= "- Required Pages Across The Site: " . (!empty($required_pages) ? implode(', ', $required_pages) : $default_required_pages) . "\n";
        $prompt .= "- Required Navigation Menu: " . (!empty($site_navigation) ? implode(' | ', $site_navigation) : $default_navigation) . "\n";
        if ($page_description !== '') {
            $prompt .= "- Exact Brief For This Page: {$page_description}\n";
        }
        if (!empty($custom_pages)) {
            $prompt .= "- Requested Custom Pages: " . implode(', ', $custom_pages) . "\n";
        }
        if (!empty($special_features)) {
            $prompt .= "- Requested Special Features: " . implode(', ', $special_features) . "\n";
        }
        if (!empty($page_descriptions)) {
            $prompt .= "- Known Page Briefs: " . wp_json_encode($page_descriptions) . "\n";
        }
        if (!empty($reference_sites) || $logo_url !== '' || !empty($video_urls)) {
            $prompt .= "\nBRAND & ASSET REQUIREMENTS:\n";
            if ($logo_url !== '') {
                $prompt .= "- Official Logo URL: {$logo_url}\n";
            }
            if (!empty($reference_sites)) {
                $prompt .= "- Reference / Inspiration Sites: " . implode(', ', $reference_sites) . "\n";
            }
            if (!empty($video_urls)) {
                $prompt .= "- Provided Video URLs: " . implode(', ', $video_urls) . "\n";
            }
        }
        $prompt .= "\n";

        // ✅ USER-APPROVED DESIGN CONCEPT: honor the exact website format + visual direction the user
        // approved in Phase 4. For single-page / hybrid formats the HOME page becomes a complete
        // one-page site with anchor navigation covering every section.
        $site_format = (string) ($architecture['site_format'] ?? ($business_data['website_design_concept']['site_format'] ?? 'multi_page'));
        $single_page_sections = is_array($architecture['single_page_sections'] ?? null) ? $architecture['single_page_sections'] : array();
        $approved_direction = trim((string) ($architecture['design_direction'] ?? ($business_data['website_design_concept']['design_direction'] ?? '')));
        $design_refinements = is_array($business_data['website_design_concept']['refinements'] ?? null) ? $business_data['website_design_concept']['refinements'] : array();
        $competitor_design_research = trim((string) ($business_data['competitor_design_research'] ?? ''));

        if ($approved_direction !== '' || !empty($design_refinements) || $competitor_design_research !== '') {
            $prompt .= "USER-APPROVED DESIGN CONCEPT (MANDATORY — build exactly this):\n";
            if ($approved_direction !== '') {
                $prompt .= "- Approved Visual Direction: {$approved_direction}\n";
            }
            if (!empty($design_refinements)) {
                $prompt .= "- User Refinements To Honor: " . implode(' | ', array_map('sanitize_text_field', $design_refinements)) . "\n";
            }
            if ($competitor_design_research !== '') {
                $prompt .= "- Competitor Website Benchmarking (beat these, do not copy): {$competitor_design_research}\n";
            }
            $prompt .= "\n";
        }

        if ($page_type === 'home' && in_array($site_format, array('single_page', 'hybrid'), true)) {
            $section_list = !empty($single_page_sections)
                ? implode(', ', array_map(array($this, 'humanize_page_title'), $single_page_sections))
                : 'About, Services, Why Choose Us, Testimonials, FAQ, Contact';
            $prompt .= "SINGLE-PAGE WEBSITE FORMAT (MANDATORY — the user approved a " . ($site_format === 'hybrid' ? 'HYBRID one-page core' : 'SINGLE-PAGE') . " website):\n";
            $prompt .= "- This HOME page MUST be a COMPLETE, self-contained one-page website — NOT just a hero + teaser.\n";
            $prompt .= "- Include ALL of these as full inline sections, each with an id anchor: {$section_list}.\n";
            $prompt .= "- Add a sticky/fixed top navigation whose links are in-page anchors (e.g. href=\"#about\", href=\"#services\", href=\"#contact\") that smooth-scroll to each section.\n";
            $prompt .= "- Each section must be substantial and fully fleshed out (real copy, visuals, CTAs) — equivalent in depth to a dedicated page.\n";
            $prompt .= "- End with a complete footer. The result must stand alone as the entire website experience.\n";
            if ($site_format === 'hybrid') {
                $prompt .= "- A few dedicated sub-pages also exist (e.g. shop/contact); still link to them in the nav alongside the in-page anchors.\n";
            }
            $prompt .= "\n";
        }

        if ($plugin_blueprint_mode) {
            $prompt .= "CRITICAL PRODUCT POSITIONING RULES:\n";
            $prompt .= "- This website sells a WordPress plugin, not consulting retainers or generic agency services.\n";
            $prompt .= "- Make it explicit, especially on the homepage, that VMO is a WordPress plugin people can download and start using.\n";
            $prompt .= "- Primary conversion goals are plugin download, plan selection, and QuickLaunch onboarding. Secondary conversion is affiliate signup.\n";
            $prompt .= "- Only valid public plan names are Tester, Starter, Pro, Elite, and Enterprise. Never invent package names such as Growth Package or Premium Package.\n";
            $prompt .= "- Never use generic headings or repeated filler such as 'What we do best', 'Strategy Consulting', 'Implementation Services', or 'Ongoing Support'.\n";
            $prompt .= "- Every feature description must be unique, concrete, and tied to what the plugin does for the user.\n";
            $prompt .= "- The page should create urgency to start using the plugin now, not feel like a brochure for a team of service experts.\n\n";
        }
        
        if (!empty($media_assets)) {
            $prompt .= "AVAILABLE MEDIA ASSETS (integrate appropriately):\n";
            foreach ($media_assets as $media) {
                $prompt .= "- {$media['type']}: {$media['title']} (suggested use: {$media['usage_context']})\n";
            }
            $prompt .= "\n";
        }
        
        // ✅ ENHANCED: Premium design requirements
        $prompt .= "TECHNICAL & VISUAL REQUIREMENTS (STRICT - MUST FOLLOW):\n\n";
        
        $prompt .= "1. **TECH STACK (REQUIRED):**\n";
        $prompt .= "   - Tailwind CSS utility classes for ALL layout and styling (class=\"...\" with Tailwind utilities)\n";
        $prompt .= "   - CSS Variables for brand color theming: var(--vmo-primary), var(--vmo-accent)\n";
        $prompt .= "   - GSAP + ScrollTrigger CDN is pre-loaded — add data-vmo-animate to sections for scroll reveal\n";
        $prompt .= "   - Lenis smooth scroll CDN is pre-loaded — works automatically\n";
        $prompt .= "   - React 18 CDN is pre-loaded for interactive components\n";
        $prompt .= "   - Swiper.js CDN is pre-loaded for carousels (use class=\"swiper vmo-testimonial-swiper\")\n\n";
        
        $prompt .= "2. **COLOR & TYPOGRAPHY (CRITICAL):**\n";
        $prompt .= "   - Use CSS Variables for primary/secondary/accent colors\n";
        $prompt .= "   - Modern font stack: Inter, SF Pro, or system fonts\n";
        $prompt .= "   - Use clamp() for responsive typography\n";
        $prompt .= "   - Headlines: Bold, large, with gradient text effects where appropriate\n";
        $prompt .= "   - Body: 16-18px, 1.6-1.8 line height for readability\n\n";
        
        $prompt .= "3. **VISUAL EFFECTS (MUST INCLUDE):**\n";
        $prompt .= "   - **Glassmorphism**: Use backdrop-filter: blur(20px) with semi-transparent backgrounds\n";
        $prompt .= "   - **Gradient Overlays**: Linear/radial gradients for depth\n";
        $prompt .= "   - **Subtle Shadows**: box-shadow with multiple layers for realism\n";
        $prompt .= "   - **Hover Effects**: Transform, scale, and color transitions\n";
        $prompt .= "   - **Micro-animations**: Button pulsing, icon rotations, text reveals\n\n";
        
        $prompt .= "4. **SCROLL ANIMATIONS (MUST IMPLEMENT):**\n";
        $prompt .= "   - Fade-up animations for content sections\n";
        $prompt .= "   - Staggered reveal for cards and list items\n";
        $prompt .= "   - Parallax scroll effects for background elements\n";
        $prompt .= "   - Progress indicators or scroll-linked animations\n\n";
        
        $prompt .= "5. **LAYOUT & SPACING:**\n";
        $prompt .= "   - Generous whitespace (min 4rem between sections)\n";
        $prompt .= "   - Asymmetric layouts for visual interest\n";
        $prompt .= "   - Full-width hero sections with min-height: 90vh\n";
        $prompt .= "   - CSS Grid for complex layouts\n";
        $prompt .= "   - Max-width: 1400px container with auto margins\n\n";
        
        $prompt .= "6. **INTERACTIVE ELEMENTS:**\n";
        $prompt .= "   - Magnetic buttons that follow cursor slightly\n";
        $prompt .= "   - Hover cards with 3D tilt effect\n";
        $prompt .= "   - Animated underlines on links\n";
        $prompt .= "   - Smooth scroll navigation\n";
        $prompt .= "   - Loading states and skeleton screens\n\n";
        
        // Page-specific requirements
        $prompt .= "7. **PAGE-SPECIFIC DESIGN ({$page_type}):**\n";
        $prompt .= $page_specific . "\n\n";
        
        $prompt .= "8. **CTA & CONVERSION ELEMENTS:**\n";
        $prompt .= "   - Primary CTA: Large, gradient background, subtle animation\n";
        $prompt .= "   - Secondary CTA: Outlined style with fill on hover\n";
        $prompt .= "   - Floating action buttons where appropriate\n";
        $prompt .= "   - Trust badges and social proof elements\n";
        $prompt .= "   - Exit-intent or scroll-triggered modals\n\n";
        
        $prompt .= "9. **NAVIGATION, MENU, AND BRANDING (MANDATORY):**\n";
        $prompt .= "   - Build a premium header/navigation component that includes ALL required navigation items for the site.\n";
        $prompt .= "   - The menu must feel intentional and premium: overlay navigation, glassmorphism navigation, or another high-end treatment. Never output a plain boring list.\n";
        $prompt .= "   - Use working internal links for the site structure: Home => '/', all other pages => '/page-slug'.\n";
        $prompt .= "   - If an official logo URL is provided, render that logo in the header and footer. Do not replace it with plain text.\n";
        $prompt .= "   - If this is a custom page, implement the exact page brief and requested feature set instead of a generic template.\n";
        if (!empty($video_urls)) {
            $prompt .= "   - At least one provided video asset must be integrated where it makes sense, especially on Home, About, Portfolio, landing, or custom feature pages.\n";
        }
        $prompt .= "\n";
        
        $prompt .= "10. **OUTPUT FORMAT (CRITICAL):**\n";
        $prompt .= "   - Complete HTML fragment using Tailwind CSS utility classes throughout\n";
        $prompt .= "   - Add data-vmo-animate attribute on each section for GSAP ScrollTrigger reveal\n";
        $prompt .= "   - Do NOT include <html>, <head>, or <body> tags\n";
        $prompt .= "   - Start with a wrapper <div> containing all content\n";
        $prompt .= "   - CDN libraries (Tailwind, GSAP, Lenis, Swiper, React 18) are ALREADY loaded — do NOT include any CDN links\n";
        $prompt .= "   - Fully responsive using Tailwind breakpoints (sm:, md:, lg:, xl:)\n\n";

        // Critical: require media placeholders that our pipeline can replace with generated images
        $prompt .= "11. **MEDIA INSTRUCTIONS (CRITICAL):**\n";
        $prompt .= "   - Do NOT use external image URLs.\n";
        if ($logo_url !== '') {
            $prompt .= "   - EXCEPTION: use the provided logo URL exactly as supplied for brand/logo placement.\n";
        }
        if (!empty($video_urls)) {
            $prompt .= "   - Use the provided video URLs directly for video sections instead of inventing fake embeds or skipping video entirely.\n";
        }
        $prompt .= "   - For every image needed, insert a queueable placeholder layer using data-vmo-generate and data-vmo-prompt attributes, for example: <div class=\"vmo-media-placeholder vmo-media-placeholder-image\" data-type=\"image\" data-vmo-generate=\"true\" data-vmo-prompt=\"clear, specific description\"></div>\n";
        $prompt .= "   - For hero/background visuals, use a dedicated empty background layer element with those same attributes; do NOT output CSS background-image:url([GENERATED_IMAGE: ...]) syntax\n";
        $prompt .= "   - Keep alt text descriptive using the same description\n\n";
        
        $prompt .= "EXAMPLE QUALITY BENCHMARK:\n";
        $prompt .= "Use Stripe/Linear/Vercel/Notion only as baseline polish references; the required VMO benchmark families are stronger and more specific.\n";
        $prompt .= "Mandatory VMO benchmark families to draw from: Capsules-style floating glass menu, Spylt-style boot/preloader and product hype, tinyPod-style scroll-controlled product reveal, layered Slider Revolution hero, persistent scene/subject swap, restaurant and food appetite sites, wine ecommerce click-to-detail, fashion editorial lookbook, multi-layer looping video hero, mouse-reactive parallax hero, glassmorphism campaign hero, fullscreen mini-site, and ecommerce sticky-buy/product-story flows.\n";
        $prompt .= "Every pixel should feel intentional. The website should look like it cost \$50,000 to build and must satisfy the selected VMO visual preset, not merely a generic SaaS template.\n\n";
        
        $prompt .= "OUTPUT ONLY THE RAW HTML CODE. NO EXPLANATIONS.\n";
        
        return $prompt;
    }
    
    /**
     * Get page-specific design requirements
     * ✅ NEW v9.0.2: Tailored requirements for each page type
     */
    private function get_page_specific_design_requirements($page_type) {
        $page_key = strtolower((string) $page_type);
        if (preg_match('/affiliate|referral|partner/', $page_key)) {
            return "   - Dedicated hero explaining the partner or referral value proposition\n" .
                   "   - Benefits grid showing commissions, payouts, and support\n" .
                   "   - Trust-building FAQ section and proof points\n" .
                   "   - Application or sign-up form section with clear CTA\n" .
                   "   - Dashboard teaser, earnings summary, or referral workflow preview";
        }
        if (preg_match('/dashboard|portal|member/', $page_key)) {
            return "   - Executive dashboard hero with KPI cards or account summary\n" .
                   "   - Modular panels for analytics, account actions, and resource access\n" .
                   "   - Sidebar or tab navigation that feels premium and app-like\n" .
                   "   - Security/trust messaging for account access\n" .
                   "   - CTA blocks for sign in, join, or contact support";
        }
        if (preg_match('/calculator/', $page_key)) {
            return "   - Interactive calculator layout with clear labeled inputs and outputs\n" .
                   "   - Explainer content showing how the calculation works\n" .
                   "   - Comparison cards or example scenarios\n" .
                   "   - Sticky CTA encouraging signup or consultation after calculation\n" .
                   "   - Trust notes explaining assumptions and payout logic";
        }
        if (preg_match('/faq/', $page_key)) {
            return "   - Search or jump navigation for quick answers\n" .
                   "   - Premium accordion layout with grouped questions\n" .
                   "   - Strategic CTAs after objection-heavy FAQ sections\n" .
                   "   - Contact escalation path for unanswered questions\n" .
                   "   - SEO-friendly semantic structure with headings and schema-ready layout";
        }
        if (preg_match('/pricing/', $page_key)) {
            return "   - Tiered pricing cards with clear feature differentiation\n" .
                   "   - Comparison matrix or add-on section\n" .
                   "   - ROI framing, FAQs, and guarantee or trust section\n" .
                   "   - Primary CTA per tier and a custom quote fallback\n" .
                   "   - Mobile-friendly stacked layout with strong visual hierarchy";
        }

        $requirements = array(
            'home' => "   - Immersive hero section: Full viewport, animated background, clear headline\n" .
                      "   - Feature grid: 3-4 key benefits with icons and subtle animations\n" .
                      "   - Social proof: Testimonials with photos, ratings, trust badges\n" .
                      "   - Services overview: Horizontal scroll or staggered grid\n" .
                      "   - Final CTA: Full-width section with compelling action",
                      
            'about' => "   - Hero: Large team/founder image with floating text\n" .
                       "   - Story timeline: Visual progression of company history\n" .
                       "   - Mission/values: Icon-driven cards with glassmorphism\n" .
                       "   - Team section: Hover-reveal bios, social links\n" .
                       "   - Statistics: Animated counters (years, clients, projects)",
                       
            'services' => "   - Service cards: Hover-lift effect, gradient accents\n" .
                          "   - Pricing tables: If applicable, clear tier differentiation\n" .
                          "   - Process timeline: Step-by-step visual flow\n" .
                          "   - Comparison section: Feature matrix or sliders\n" .
                          "   - FAQ accordion: Smooth expand/collapse animations",
                          
            'shop' => "   - Story-led product collection: product-first hero, collection narrative, and detail path before purchase\n" .
                      "   - Quick-view or click-to-detail interaction for every product; a flat grid alone is not acceptable\n" .
                      "   - Product cards: scroll-reveal motion, product media, ratings, price badges, and buy action\n" .
                      "   - Cart or purchase preview: sliding panel, sticky buy menu, or detail-side purchase CTA\n" .
                      "   - Trust elements: payment confidence, shipping/returns notes, and product proof",

            'donate' => "   - Donation hero: Clear mission + impact statement\n" .
                        "   - Fund cards: Tithes/Offering/Missions/Relief/etc with tier buttons\n" .
                        "   - Custom amount input: Smooth validation + animated feedback\n" .
                        "   - Trust + compliance: Secure payment note, transparency section\n" .
                        "   - Recurring option (if possible): Monthly giving highlight",
                      
            'portfolio' => "   - Masonry or grid gallery: Hover overlays with project info\n" .
                           "   - Filter tabs: Animated category switching\n" .
                           "   - Case study layout: Full-width images, side-by-side stats\n" .
                           "   - Before/after sliders: Drag to reveal comparisons\n" .
                           "   - Client logos: Animated marquee or grid",
                           
            'contact' => "   - Split layout: Form + map/contact info\n" .
                         "   - Floating labels: Smooth input animations\n" .
                         "   - Validation: Real-time feedback with icons\n" .
                         "   - Contact cards: Phone, email, social with hover effects\n" .
                         "   - Success state: Animated confirmation message\n" .
                         "   - IMPORTANT: You MUST include the exact text [VMO_MAP_SECTION] where the map should be displayed.",
                         
            'booking' => "   - Calendar: Visual date picker with availability\n" .
                         "   - Time slots: Grid layout with hover selection\n" .
                         "   - Service selection: Expandable cards or tabs\n" .
                         "   - Confirmation: Multi-step progress indicator\n" .
                         "   - Summary panel: Sticky sidebar with details",
                         
            'blog' => "   - Featured post: Large hero card with gradient overlay\n" .
                      "   - Post grid: Cards with excerpt, author, date\n" .
                      "   - Category tags: Pill-style with hover effects\n" .
                      "   - Search: Expandable input with icon\n" .
                      "   - Pagination: Stylized with current page indicator"
        );
        
        return $requirements[$page_key] ?? 
               "   - Unique, visually striking layout for this page type\n" .
               "   - Apply all premium design principles listed above\n" .
               "   - Focus on conversion and user engagement";
    }

    /**
     * Replace [GENERATED_IMAGE: ...] placeholders with real images via GPT Image 1
     */
    private function process_generated_page_html($html, $context = array()) {
        if (!is_string($html) || $html === '') return $html;
        // Only proceed if placeholders exist
        if (stripos($html, '[GENERATED_IMAGE') === false) return $html;

        // Ensure media handler is available
        if (!function_exists('vmo_generate_images_and_replace')) {
            $handler = plugin_dir_path(__FILE__) . 'media-handler.php';
            if (file_exists($handler)) {
                require_once $handler;
            }
        }
        if (!function_exists('vmo_generate_images_and_replace')) return $html;

        // Build OpenAI headers from available key options
        $api_key = get_option('vmo_openai_api_key');
        if (!$api_key) {
            $api_key = get_option('vmo_manager_openai_api_key');
        }
        if (!$api_key) return $html; // graceful no-op if not configured

        $headers = [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json'
        ];

        // Replace placeholders with generated images and return updated HTML
        try {
            $processed = vmo_generate_images_and_replace($html, $headers, $context);
            $working_html = $processed ?: $html;

            // [VMO-MAP-INTEGRATION] Inject Map if placeholder exists
            if (stripos($working_html, '[VMO_MAP_SECTION]') !== false && !empty($context['business_location'])) {
                
                // Fetch provisioned phone if available
                $provisioned_phone = '';
                $user_id = isset($context['user_id']) ? intval($context['user_id']) : 0;
                
                if ($user_id > 0) {
                    global $wpdb;
                    $table = $wpdb->prefix . 'vmo_user_phone_numbers';
                    // Silent check for phone number
                    try {
                        $phone_row = $wpdb->get_row($wpdb->prepare("SELECT phone_number FROM $table WHERE user_id = %d AND status = 'active' LIMIT 1", $user_id));
                        if ($phone_row && !empty($phone_row->phone_number)) {
                            $provisioned_phone = $phone_row->phone_number;
                        }
                    } catch (\Throwable $ex) {
                        // table might not exist
                    }
                }

                $map_code = $this->generate_map_embed_code($context['business_location'], $provisioned_phone);
                $working_html = str_ireplace('[VMO_MAP_SECTION]', $map_code, $working_html);
            }

            return $working_html;
        } catch (\Throwable $e) {
            error_log('VMO media replacement error: ' . $e->getMessage());
            return $html;
        }
    }
    
    private function generate_map_embed_code($location, $phone = '') {
        $encoded = urlencode($location);
        $embed_url = "https://maps.google.com/maps?q={$encoded}&t=&z=14&ie=UTF8&iwloc=&output=embed";
        
        $html = '<div class="vmo-map-integration" style="width: 100%; margin: 2rem 0;">';
        
        // Interactive Map
        $html .= '<div class="vmo-map-frame" style="width: 100%; height: 400px; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px -10px rgba(0,0,0,0.1); margin-bottom: 1.5rem;">';
        $html .= '<iframe width="100%" height="100%" src="' . esc_url($embed_url) . '" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>';
        $html .= '</div>';
        
        // Action Buttons
        $html .= '<div class="vmo-map-actions" style="display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center;">';
        
        // Auto-Provisioned Phone Button (if available)
        if (!empty($phone)) {
            $clean_phone = preg_replace('/[^0-9+]/', '', $phone);
            $html .= '<a href="tel:' . esc_attr($clean_phone) . '" style="display: inline-flex; align-items: center; padding: 0.75rem 1.5rem; background: #28a745; color: #ffffff; border-radius: 24px; text-decoration: none; font-family: sans-serif; font-weight: 500; font-size: 14px; transition: all 0.2s ease; box-shadow: 0 4px 6px rgba(40, 167, 69, 0.2);">';
            $html .= '<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; margin-right: 8px; fill: white;"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></svg>';
            $html .= 'Call Business: ' . esc_html($phone) . '</a>';
        }
        
        // Google Maps
        $html .= '<a href="https://www.google.com/maps/search/?api=1&query=' . $encoded . '" target="_blank" rel="noopener noreferrer" style="display: inline-flex; align-items: center; padding: 0.75rem 1.5rem; background: #ffffff; color: #3c4043; border: 1px solid #dadce0; border-radius: 24px; text-decoration: none; font-family: sans-serif; font-weight: 500; font-size: 14px; transition: all 0.2s ease;">';
        $html .= '<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; margin-right: 8px;"><path fill="#4285F4" d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"/><circle fill="#1967D2" cx="12" cy="9" r="2.5"/></svg>';
        $html .= 'Google Maps</a>';
        
        // Apple Maps
        $html .= '<a href="http://maps.apple.com/?q=' . $encoded . '" target="_blank" rel="noopener noreferrer" style="display: inline-flex; align-items: center; padding: 0.75rem 1.5rem; background: #000000; color: #ffffff; border-radius: 24px; text-decoration: none; font-family: sans-serif; font-weight: 500; font-size: 14px; transition: all 0.2s ease;">';
        $html .= '<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; margin-right: 8px; fill: white;"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-1.07 3.97-2.9 5.34z"/></svg>';
        $html .= 'Apple Maps</a>';
        
        // Waze
        $html .= '<a href="https://waze.com/ul?q=' . $encoded . '&navigate=yes" target="_blank" rel="noopener noreferrer" style="display: inline-flex; align-items: center; padding: 0.75rem 1.5rem; background: #33ccff; color: #ffffff; border-radius: 24px; text-decoration: none; font-family: sans-serif; font-weight: 500; font-size: 14px; transition: all 0.2s ease;">';
        $html .= '<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; margin-right: 8px; fill: white;"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-1.07 3.97-2.9 5.34z"/></svg>';
        $html .= 'Waze Directions</a>';
        
        $html .= '</div></div>';
        return $html;
    }
    
    /**
     * Integrate AI assistant into the generated website
     */
    private function integrate_ai_assistant($session_id, $business_data) {
        // Configure AI assistant with business context
        $assistant_config = array(
            'business_name' => $business_data['business_name'],
            'business_type' => $business_data['business_type'],
            'services' => $business_data['services'],
            'contact_info' => $business_data['contact_info']
        );
        
        // Save assistant configuration
        update_option('vmo_website_assistant_' . $session_id, $assistant_config);
    }
    
    /**
     * Save the generated website as DRAFT for preview/refinement
     * User will click Publish when ready
     * 
     * @return array Contains post_id, preview_url, and homepage_html for live preview
     */
    private function save_and_publish_website($website_pages, $business_data, $session_id, $business_package = array(), $media_assets = array()) {
        $media_assets = is_array($media_assets) ? $media_assets : array();
        $business_data = $this->ensure_ecommerce_product_packets($business_data, $media_assets);
        $business_package = $this->attach_ecommerce_product_packets_to_package($business_package, $business_data);
        $website_pages = $this->ensure_minimum_quality_pages($website_pages, $business_data);

        // Get homepage HTML for live preview
        $landing_html = $website_pages['home'] ?? reset($website_pages);
        if (is_array($landing_html)) { 
            $landing_html = $this->render_page_to_html($landing_html, $business_data); 
        }

        $landing_html = $this->prepare_landing_html_for_publish($landing_html, $business_data, $session_id);
        
        // Ensure it's valid HTML string
        if (!is_string($landing_html) || trim($landing_html) === '') {
            $landing_html = $this->generate_fallback_homepage($business_data);
        }

        // Premium runtime shell: wrap the generated HTML in the modern
        // front-end stack (Inter, Tailwind, GSAP/ScrollTrigger, Lenis,
        // Swiper, AOS) plus base styles + init scripts. Applied to EVERY
        // generated site unconditionally so output always renders with its
        // intended design system. (Previously this was gated behind a
        // GrapesJS editor key, which left key-less installs without a
        // runtime — that coupling has been removed.)
        $premium_shell_applied = false;
        if (class_exists('VMO_Premium_Shell')) {
            $landing_html = VMO_Premium_Shell::wrap($landing_html, array(
                'primary_color' => $business_data['brand']['primary_color'] ?? '#0f172a',
                'accent_color'  => $business_data['brand']['accent_color'] ?? '#0ea5e9',
                'tech_stack'    => array('Tailwind CSS', 'GSAP', 'Lenis', 'Swiper.js'),
                'business_name' => $business_data['business_name'] ?? '',
            ));
            $premium_shell_applied = true;
        }

        $builder_state = $this->build_generated_builder_state($landing_html, $website_pages, $business_data);
        $builder_images = $this->extract_builder_image_library($business_data);

        $post_data = array(
            'post_title'   => ($business_data['business_name'] ?? 'My Business') . ' - Website',
            'post_content' => $landing_html,
            'post_status'  => 'draft', // ✅ DRAFT - user will publish after refinement
            'post_type'    => 'vmo_project',
            'meta_input'   => array(
                'vmo_session_id'    => $session_id,
                'vmo_business_data' => wp_json_encode($business_data),
                'vmo_business_package' => wp_json_encode($business_package),
                'vmo_generated_date'=> current_time('mysql'),
                'vmo_website_type'  => 'consultation_generated',
                'vmo_pages_map'     => wp_json_encode($website_pages),
                'vmo_state'         => wp_json_encode($builder_state),
                'vmo_images'        => wp_json_encode($builder_images),
                'vmo_ready_to_publish' => 'no',
                'vmo_publish_ready'    => 'no',
                'vmo_render_engine'    => $premium_shell_applied ? 'premium_shell_v3' : 'raw_html',
                'vmo_tech_stack'       => json_encode(array('Tailwind CSS', 'GSAP', 'Lenis', 'Swiper.js')),
            )
        );
        
        $post_id = function_exists('vmo_with_generated_html_post_kses_disabled')
            ? vmo_with_generated_html_post_kses_disabled(function () use ($post_data) {
                return wp_insert_post($post_data);
            })
            : wp_insert_post($post_data);
        
        if ($post_id) {
            $user_id = (int) ($business_data['user_id'] ?? get_current_user_id());
            update_post_meta($post_id, 'vmo_project_raw_html', $landing_html);
            update_post_meta($post_id, 'vmo_project_html_storage_mode', 'generated_html_preserved');
            update_post_meta($post_id, 'vmo_media_assets', wp_json_encode($media_assets));
            $website_payload = array(
                'content' => $landing_html,
                'pages_map' => $website_pages,
                'homepage_html' => $landing_html,
            );
            $qa_report = array();
            $canonical_context = is_array($business_data['canonical_generation_context'] ?? null) ? $business_data['canonical_generation_context'] : array();
            $qa_context = array(
                'related_post_id' => $post_id,
                'draft_key' => (string) $post_id,
                'visual_preset' => $business_data['visual_preset'] ?? ($canonical_context['selected_visual_preset'] ?? ''),
                'canonical_generation_context' => $canonical_context,
                'required_premium_signals' => $canonical_context['required_premium_signals'] ?? array(),
                'business_data' => $business_data,
                'media_assets' => $media_assets,
                'product_generation_packets' => $business_data['ecommerce']['product_generation_packets'] ?? array(),
            );
            if ($user_id > 0 && function_exists('vmo_get_agent_orchestrator')) {
                $qa_report = vmo_get_agent_orchestrator()->run_website_qa($user_id, $session_id, $website_payload, is_array($business_package) ? $business_package : array(), $qa_context);
            } elseif ($user_id > 0 && function_exists('vmo_get_website_qa_service')) {
                $qa_report = vmo_get_website_qa_service()->run_qa($user_id, $session_id, $website_payload, is_array($business_package) ? $business_package : array(), $qa_context);
            }
            if ($user_id > 0 && function_exists('vmo_get_website_final_qa_gate')) {
                $final_qa_gate = vmo_get_website_final_qa_gate()->evaluate($user_id, $session_id, $website_payload, is_array($business_package) ? $business_package : array(), $qa_context);
                $qa_report = vmo_get_website_final_qa_gate()->merge_with_existing_report($qa_report, $final_qa_gate);
            }

            $media_queue_response = array();
            if (function_exists('vmo_get_project_media_queue_service')) {
                $media_queue = vmo_get_project_media_queue_service();
                if ($media_queue && method_exists($media_queue, 'enqueue_manifest_media_for_post')) {
                    $queue_result = $media_queue->enqueue_manifest_media_for_post($post_id, array(
                        'canonical_generation_context' => $canonical_context,
                        'visual_preset' => $business_data['visual_preset'] ?? ($canonical_context['selected_visual_preset'] ?? ''),
                        'business_name' => $business_data['business_name'] ?? '',
                        'industry' => $business_data['industry'] ?? ($business_data['business_type'] ?? ''),
                        'business_data' => $business_data,
                        'media_assets' => $media_assets,
                        'product_generation_packets' => $business_data['ecommerce']['product_generation_packets'] ?? array(),
                        'business_package' => is_array($business_package) ? $business_package : array(),
                        'qa_report' => is_array($qa_report) ? $qa_report : array(),
                    ));
                    if (is_wp_error($queue_result)) {
                        $media_queue_response = array('error' => $queue_result->get_error_message());
                    } elseif (is_array($queue_result)) {
                        $media_queue_response = $queue_result;
                    }
                }
            }

            $draft_assembled = true;
            $media_complete = true;
            if (is_array($qa_report) && isset($qa_report['checklist']) && is_array($qa_report['checklist']) && array_key_exists('media_complete', $qa_report['checklist'])) {
                $media_complete = !empty($qa_report['checklist']['media_complete']);
            } else {
                $media_complete = !preg_match('/data-vmo-generate=|data-vmo-sora=|\[(generated_image|generated_video):/i', $landing_html);
            }
            if (is_array($media_queue_response) && !empty($media_queue_response)) {
                $queue_counts = is_array($media_queue_response['counts'] ?? null) ? $media_queue_response['counts'] : array();
                if (!empty($queue_counts['pending']) || !empty($queue_counts['processing'])) {
                    $media_complete = false;
                }
                if (isset($media_queue_response['media_manifest']['media_complete']) && empty($media_queue_response['media_manifest']['media_complete'])) {
                    $media_complete = false;
                }
            }
            $uploaded_media_gate = $this->uploaded_media_assignment_gate($media_assets);
            if (empty($uploaded_media_gate['ready'])) {
                $media_complete = false;
                if (is_array($qa_report)) {
                    $qa_report['uploaded_media_gate'] = $uploaded_media_gate;
                    $qa_report['critical_issues'] = array_values(array_unique(array_merge(
                        (array) ($qa_report['critical_issues'] ?? array()),
                        array('Uploaded media must be assigned, mapped, and quality-cleared before preview release.')
                    )));
                }
            }
            $preview_ready = $draft_assembled && $media_complete && is_array($qa_report) && !empty($qa_report['preview_ready']);
            $generation_completed = $preview_ready;
            $publish_ready = $preview_ready && !empty($qa_report['publish_ready']);
            $final_preview_url = ($preview_ready && $media_complete) ? get_preview_post_link($post_id) : '';
            $operations_manifest = $this->build_website_operations_manifest(
                $post_id,
                $session_id,
                $website_pages,
                $business_data,
                is_array($business_package) ? $business_package : array(),
                is_array($qa_report) ? $qa_report : array(),
                $canonical_context,
                $builder_state,
                array(
                    'draft_assembled' => $draft_assembled,
                    'media_complete' => $media_complete,
                    'preview_ready' => $preview_ready,
                    'generation_completed' => $generation_completed,
                    'publish_ready' => $publish_ready,
                    'media_queue' => $media_queue_response,
                    'media_assets' => $media_assets,
                )
            );

            update_post_meta($post_id, 'vmo_publish_ready', $publish_ready ? 'yes' : 'no');
            update_post_meta($post_id, 'vmo_draft_assembled', $draft_assembled ? 'yes' : 'no');
            update_post_meta($post_id, 'vmo_media_complete', $media_complete ? 'yes' : 'no');
            update_post_meta($post_id, 'vmo_preview_ready', $preview_ready ? 'yes' : 'no');
            update_post_meta($post_id, 'vmo_preview_url', $final_preview_url);
            update_post_meta($post_id, 'vmo_generation_completed', $generation_completed ? 'yes' : 'no');
            update_post_meta($post_id, 'vmo_website_operations_manifest', wp_json_encode($operations_manifest));
            update_post_meta($post_id, 'vmo_canonical_pipeline_version', (string) ($canonical_context['pipeline_version'] ?? 'v2.0-2026-04-30'));
            update_post_meta($post_id, 'vmo_v2_agent_plan', wp_json_encode($operations_manifest['agent_plan'] ?? array()));
            update_post_meta($post_id, 'vmo_component_state_manifest', wp_json_encode($operations_manifest['website']['component_state'] ?? array()));
            update_post_meta($post_id, 'vmo_media_manifest', wp_json_encode($operations_manifest['media_manifest'] ?? array()));
            if ($user_id > 0) {
                update_user_meta($user_id, 'vmo_website_operations_manifest', $operations_manifest);
                update_user_meta($user_id, 'vmo_website_operations_manifest_' . $session_id, $operations_manifest);
                $dashboard_class_file = plugin_dir_path(__FILE__) . 'class-vmo-kpi-dashboard.php';
                if (file_exists($dashboard_class_file)) {
                    require_once $dashboard_class_file;
                    if (class_exists('VMO_KPI_Dashboard')) {
                        $kpi_dashboard = new VMO_KPI_Dashboard();
                        $website_status = $publish_ready
                            ? 'publish_ready'
                            : ($preview_ready ? 'draft_ready' : 'media_generation_in_progress');
                        $next_milestone = $media_complete
                            ? ($publish_ready ? 'Review and Publish Website' : 'Resolve Draft QA and Publish Website')
                            : 'Complete required media generation';
                        $kpi_dashboard->set_kpi($user_id, 'website_status', $website_status, array(
                            'category' => 'website',
                            'source' => 'consultation_website_manifest',
                        ));
                        $kpi_dashboard->set_kpi($user_id, 'pages_count', count($operations_manifest['website']['pages'] ?? array()), array(
                            'label' => 'Total Pages',
                            'type' => 'number',
                            'category' => 'website',
                            'source' => 'consultation_website_manifest',
                        ));
                        $kpi_dashboard->set_kpi($user_id, 'seo_score', (int) ($operations_manifest['dashboard']['indicators']['qa_score'] ?? 0), array(
                            'label' => 'Website QA Score',
                            'type' => 'score',
                            'category' => 'seo',
                            'source' => 'consultation_website_manifest',
                        ));
                        $kpi_dashboard->set_kpi($user_id, 'next_milestone', $next_milestone, array(
                            'category' => 'progress',
                            'source' => 'consultation_website_manifest',
                        ));
                    }
                }
            }
            update_post_meta($post_id, 'vmo_publish_checklist', wp_json_encode($qa_report['checklist'] ?? array()));
            update_post_meta($post_id, 'vmo_website_qa_report', wp_json_encode($qa_report));
            update_post_meta($post_id, 'vmo_final_qa_gate', wp_json_encode($qa_report['final_qa_gate'] ?? array()));
            if (!empty($canonical_context['selected_visual_preset'])) {
                update_post_meta($post_id, 'vmo_visual_preset', sanitize_key((string) $canonical_context['selected_visual_preset']));
            }
            if (!empty($business_package['business_brief_summary'])) {
                update_post_meta($post_id, 'vmo_business_brief_summary', (string) $business_package['business_brief_summary']);
            }

            // ✅ PERSISTENCE FIX: Store post_id→session mapping permanently
            // Transient kept for fast lookup, but ALSO store in permanent wp_option + consultation DB
            set_transient('vmo_draft_website_' . $session_id, $post_id, 86400); // Fast cache (24h)
            update_option('vmo_website_post_' . $session_id, $post_id, false); // Permanent backup
            
            // ✅ PERSISTENCE FIX: Also persist to consultation database for permanent record
            if ($user_id && class_exists('VMO_Consultation_Database')) {
                $consultation_db = new VMO_Consultation_Database();
                $existing = $consultation_db->get_consultation_by_session($session_id);
                if ($existing) {
                    $website_data_arr = json_decode($existing->website_data ?: '{}', true);
                    if (!is_array($website_data_arr)) $website_data_arr = [];
                    $website_data_arr['draft_post_id'] = $post_id;
                    $website_data_arr['draft_created_at'] = current_time('mysql');
                    $website_data_arr['preview_url'] = $final_preview_url;
                    $website_data_arr['homepage_html'] = $landing_html;
                    $website_data_arr['pages_map'] = $website_pages;
                    $website_data_arr['builder_state'] = $builder_state;
                    $website_data_arr['project_title'] = $post_data['post_title'];
                    $website_data_arr['qa_report'] = $qa_report;
                    $website_data_arr['final_qa_gate'] = $qa_report['final_qa_gate'] ?? array();
                    $website_data_arr['operations_manifest'] = $operations_manifest;
                    $website_data_arr['media_assets'] = $media_assets;
                    $website_data_arr['canonical_generation_context'] = $canonical_context;
                    $website_data_arr['selected_visual_preset'] = $canonical_context['selected_visual_preset'] ?? '';
                    $website_data_arr['draft_assembled'] = $draft_assembled;
                    $website_data_arr['media_complete'] = $media_complete;
                    $website_data_arr['preview_ready'] = $preview_ready;
                    $website_data_arr['draft_ready'] = $preview_ready;
                    $website_data_arr['generation_completed'] = $generation_completed;
                    $website_data_arr['generation_completed_at'] = $generation_completed ? current_time('mysql') : null;
                    $website_data_arr['publish_ready'] = $publish_ready;
                    $consultation_db->save_consultation($user_id, $session_id, [
                        'website_data' => wp_json_encode($website_data_arr),
                        'status' => $preview_ready ? 'completed' : 'generating_media',
                    ]);
                }
            }
            
            return array(
                'post_id' => $post_id,
                'preview_url' => $final_preview_url,
                'content' => $landing_html,
                'main_page' => $landing_html,
                'homepage_html' => $landing_html,
                'pages_map' => $website_pages,
                'builder_state' => $builder_state,
                'project_title' => $post_data['post_title'],
                'business_name' => $business_data['business_name'] ?? '',
                'status' => $preview_ready ? 'draft' : 'generating_media',
                'draft_assembled' => $draft_assembled,
                'media_complete' => $media_complete,
                'preview_ready' => $preview_ready,
                'draft_ready' => $preview_ready,
                'generation_completed' => $generation_completed,
                'generation_completed_at' => $generation_completed ? current_time('mysql') : null,
                'qa_report' => $qa_report,
                'final_qa_gate' => $qa_report['final_qa_gate'] ?? array(),
                'operations_manifest' => $operations_manifest,
                'media_manifest' => $operations_manifest['media_manifest'] ?? array(),
                'media_assets' => $media_assets,
                'media_queue' => $media_queue_response,
                'visual_preset' => $canonical_context['selected_visual_preset'] ?? '',
                'publish_ready' => $publish_ready
            );
        }
        
        throw new Exception('Failed to save website');
    }

    private function build_website_operations_manifest($post_id, $session_id, $website_pages, $business_data, $business_package, $qa_report, $canonical_context, $builder_state, $readiness) {
        $website_pages = is_array($website_pages) ? $website_pages : array();
        $business_data = is_array($business_data) ? $business_data : array();
        $business_package = is_array($business_package) ? $business_package : array();
        $qa_report = is_array($qa_report) ? $qa_report : array();
        $canonical_context = is_array($canonical_context) ? $canonical_context : array();
        $builder_state = is_array($builder_state) ? $builder_state : array();
        $readiness = is_array($readiness) ? $readiness : array();
        $business_manifest = is_array($business_data['business_manifest'] ?? null) ? $business_data['business_manifest'] : array();
        if (empty($business_manifest)) {
            $manifest_user_id = (int) ($business_data['user_id'] ?? get_current_user_id());
            if ($manifest_user_id > 0 && $session_id !== '') {
                $session_manifest = get_user_meta($manifest_user_id, 'vmo_business_manifest_' . $session_id, true);
                $business_manifest = is_array($session_manifest) ? $session_manifest : array();
            }
        }

        $page_summaries = array();
        $all_html = '';
        foreach ($website_pages as $page_key => $page_payload) {
            $page_html = '';
            $page_title = ucwords(str_replace(array('-', '_'), ' ', (string) $page_key));
            if (is_array($page_payload)) {
                $page_html = (string) ($page_payload['html'] ?? $page_payload['content'] ?? '');
                if (!empty($page_payload['title'])) {
                    $page_title = sanitize_text_field((string) $page_payload['title']);
                }
            } else {
                $page_html = (string) $page_payload;
            }
            $all_html .= "\n" . $page_html;
            $page_summaries[] = array(
                'key' => sanitize_key((string) $page_key),
                'title' => $page_title,
                'has_contact_form' => (bool) preg_match('/(<form|data-vmo-form|vmo-form|contact\s+form)/i', $page_html),
                'has_booking_form' => (bool) preg_match('/(appointment|booking|schedule|reservation)/i', $page_html),
                'has_checkout_or_payment' => (bool) preg_match('/(vmo_buy_product|checkout|payment|donate|cart|price)/i', $page_html),
                'has_map_or_location' => (bool) preg_match('/(google\.com\/maps|maps\.google|data-vmo-map|<iframe[^>]+map|address|location)/i', $page_html),
                'has_generated_media_queue' => (bool) preg_match('/(data-vmo-generate=|data-vmo-sora=|vmo-media-placeholder|\[(generated_image|generated_video):)/i', $page_html),
            );
        }

        $ecommerce = is_array($business_data['ecommerce'] ?? null) ? $business_data['ecommerce'] : array();
        $products = is_array($ecommerce['products'] ?? null) ? $ecommerce['products'] : array();
        $product_generation_packets = is_array($ecommerce['product_generation_packets'] ?? null) ? $ecommerce['product_generation_packets'] : $this->get_ecommerce_product_generation_packets($business_data);
        $donation_funds = is_array($ecommerce['donation_funds'] ?? null) ? $ecommerce['donation_funds'] : array();
        $demo_products_present = false;
        foreach ($products as $product) {
            if (is_array($product) && (!empty($product['demo_data']) || !empty($product['is_demo']))) {
                $demo_products_present = true;
                break;
            }
        }
        $collection_mode = (string) ($ecommerce['collection_mode'] ?? 'inquiry');
        $needs_payments = !empty($ecommerce['collect_payments'])
            || !empty($ecommerce['intends_to_sell'])
            || !empty($ecommerce['sell_on_website'])
            || $collection_mode === 'donations';
        $needs_ecommerce = !empty($ecommerce['intends_to_sell']) || !empty($ecommerce['sell_on_website']) || !empty($products);

        $shipping_required = false;
        $shipping_profiles = array();
        foreach ($products as $product) {
            if (!is_array($product)) {
                continue;
            }
            if (!empty($product['shipping_required']) || (($product['fulfillment_type'] ?? '') === 'physical')) {
                $shipping_required = true;
            }
            if (isset($product['shipping_profile']) && is_array($product['shipping_profile'])) {
                $shipping_profiles[] = $product['shipping_profile'];
            }
        }

        $contact_info = is_array($business_data['contact_info'] ?? null) ? $business_data['contact_info'] : array();
        $email_roles = is_array($business_data['email_roles'] ?? null) ? $business_data['email_roles'] : array();
        $email_roles_meta = is_array($business_data['email_roles_meta'] ?? null) ? $business_data['email_roles_meta'] : array();
        $location = trim((string) ($business_data['location'] ?? ($contact_info['address'] ?? '')));
        $has_map = $location !== '' || (bool) preg_match('/(google\.com\/maps|maps\.google|data-vmo-map|<iframe[^>]+map)/i', $all_html);
        $has_contact_form = (bool) preg_match('/(<form|data-vmo-form|vmo-form|contact\s+form)/i', $all_html);
        $has_booking_form = (bool) preg_match('/(appointment|booking|schedule|reservation)/i', $all_html);
        $generated_media_count = preg_match_all('/(data-vmo-generate=|data-vmo-sora=|\[(generated_image|generated_video):)/i', $all_html, $media_matches);
        $media_manifest = function_exists('vmo_get_website_media_manifest_service')
            ? vmo_get_website_media_manifest_service()->build_from_html($all_html, array(
                'canonical_generation_context' => $canonical_context,
                'business_data' => $business_data,
                'business_package' => $business_package,
                'product_generation_packets' => $business_data['ecommerce']['product_generation_packets'] ?? array(),
                'media_assets' => is_array($readiness['media_assets'] ?? null) ? $readiness['media_assets'] : array(),
            ))
            : array(
                'media_complete' => !((int) $generated_media_count),
                'queueable_placeholders' => (int) $generated_media_count,
            );
        $media_queue_response = is_array($readiness['media_queue'] ?? null) ? $readiness['media_queue'] : array();
        $media_queue_counts = is_array($media_queue_response['counts'] ?? null) ? $media_queue_response['counts'] : array();
        if (is_array($media_queue_response['media_manifest'] ?? null)) {
            $media_manifest = array_merge($media_manifest, $media_queue_response['media_manifest']);
        }
        if (!empty($media_queue_counts)) {
            $media_manifest['queue_counts'] = $media_queue_counts;
        }
        $queued_asset_count = max((int) $generated_media_count, (int) ($media_queue_counts['total'] ?? 0));
        $agent_plan = is_array($canonical_context['v2_agent_plan'] ?? null) ? $canonical_context['v2_agent_plan'] : array();
        $tracking_contract = is_array($agent_plan['agents']['website_kpi_wiring']['result']['tracking_contract'] ?? null)
            ? $agent_plan['agents']['website_kpi_wiring']['result']['tracking_contract']
            : array(
                'tracker' => 'vmo_analytics_tracker',
                'required_events' => array('page_view', 'cta_click', 'form_submit', 'phone_click', 'email_click', 'product_view', 'checkout_start', 'purchase', 'order_created', 'shipping_status', 'seo_status'),
                'dashboard_targets' => array('website_visitors', 'page_views', 'sources_referrers', 'utm_campaigns', 'conversion_rate', 'orders', 'revenue', 'seo_score', 'today_actions'),
            );
        $component_state = is_array($builder_state['componentState'] ?? null) ? $builder_state['componentState'] : array();
        $component_state_components = is_array($component_state['components'] ?? null) ? $component_state['components'] : array();
        $component_state_manifest = array(
            'version' => 'v2.0-2026-04-30',
            'schema' => 'vmo_react_style_component_state_manifest',
            'source_of_truth' => 'react_style_component_state',
            'builder_shell' => 'wordpress_plugin_can_stage_and_export_only',
            'runtime_output' => 'standalone_html_css_js',
            'builder_editing' => 'vmo_plugin_template_component_props_then_rendered_html',
            'wordpress_editor_usage' => 'disabled_for_generated_sites',
            'state_present' => !empty($component_state) && !empty($component_state_components),
            'state_integrity_hash' => (string) ($component_state['stateIntegrityHash'] ?? md5(wp_json_encode($builder_state))),
            'block_count' => is_array($builder_state['blocks'] ?? null) ? count($builder_state['blocks']) : 0,
            'page_count' => count($page_summaries),
            'component_count' => count($component_state_components),
            'components' => $component_state_components,
            'edit_policy' => (string) ($component_state['editPolicy'] ?? 'component_props_then_rendered_html'),
            'preview_gate' => (string) ($component_state['previewGate'] ?? 'block_until_media_complete_and_final_qa_passes'),
            'mobile_variant_required' => true,
            'tracking_contract_required' => true,
            'contract_enforced' => !empty($component_state_components) && (($component_state['sourceOfTruth'] ?? '') === 'react_style_component_state') && (($component_state['renderBridge'] ?? '') === 'standalone_html_css_js'),
            'public_builder_controls_allowed' => false,
        );
        $qa_score = 0;
        if (isset($qa_report['final_qa_gate']['score'])) {
            $qa_score = (int) $qa_report['final_qa_gate']['score'];
        } elseif (isset($qa_report['score'])) {
            $qa_score = (int) $qa_report['score'];
        }

        $user_id = (int) ($business_data['user_id'] ?? get_current_user_id());
        $connected_account = array();
        if ($user_id > 0) {
            global $wpdb;
            $accounts_table = $wpdb->prefix . 'vmo_connected_accounts';
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $accounts_table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $accounts_table));
            if ($accounts_table_exists === $accounts_table) {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $connected_account = $wpdb->get_row($wpdb->prepare("SELECT stripe_account_id, details_submitted, payouts_enabled, charges_enabled FROM $accounts_table WHERE user_id = %d", $user_id), ARRAY_A);
                if (!is_array($connected_account)) {
                    $connected_account = array();
                }
            }
        }

        $payment_configured = !$needs_payments || (!empty($connected_account['stripe_account_id']) || !empty($products) || $collection_mode === 'donations');
        $forms_connected = $has_contact_form || $has_booking_form;
        $seo_ready = !empty($business_package['seo_strategy']) || !empty($business_package['seo_roadmap']) || (bool) preg_match('/(<title>|meta name="description"|schema\.org|application\/ld\+json)/i', $all_html);
        $shortcode_dependency_detected = false;
        if (preg_match_all('/(^|[\s>])\[(?!generated_image|generated_video)([a-z][a-z0-9_:-]*)(?:\s+[^\]]*)?\](?:\s*\[\/\2\])?/i', $all_html, $shortcode_matches, PREG_SET_ORDER)) {
            foreach ($shortcode_matches as $shortcode_match) {
                $shortcode_name = strtolower((string) ($shortcode_match[2] ?? ''));
                if ($shortcode_name !== '' && strpos($shortcode_name, 'data-') !== 0 && strpos($shortcode_name, 'aria-') !== 0) {
                    $shortcode_dependency_detected = true;
                    break;
                }
            }
        }
        $standalone_portable = empty($qa_report['final_qa_gate']['checklist'])
            ? (!preg_match('/(admin-ajax\.php|\/wp-json\/|wp-content\/plugins|wp-content\/themes|wp-includes|window\.wp\b|wp\.apiFetch)/i', $all_html) && !$shortcode_dependency_detected)
            : !empty($qa_report['final_qa_gate']['checklist']['standalone_portable']);

        $next_actions = array();
        if (!$payment_configured && $needs_payments) {
            $next_actions[] = array('key' => 'complete_payment_setup', 'label' => 'Complete payment setup', 'owner' => 'user_or_ai_assisted');
        }
        if ($shipping_required && !$shipping_profiles) {
            $next_actions[] = array('key' => 'confirm_shipping_rates', 'label' => 'Confirm shipping rates and regions', 'owner' => 'user_or_ai_assisted');
        }
        if (!$has_contact_form) {
            $next_actions[] = array('key' => 'add_contact_capture', 'label' => 'Add or confirm contact capture', 'owner' => 'ai');
        }
        if (!empty($readiness['media_complete']) === false) {
            $next_actions[] = array('key' => 'complete_media_generation', 'label' => 'Finish required generated media', 'owner' => 'ai');
        }
        if (!$standalone_portable) {
            $next_actions[] = array('key' => 'make_site_standalone', 'label' => 'Remove WordPress-only runtime dependencies before export', 'owner' => 'ai');
        }
        if ($needs_ecommerce && empty($product_generation_packets['ready_before_preview'])) {
            $next_actions[] = array('key' => 'complete_product_packets', 'label' => 'Complete required product content and media packets', 'owner' => 'ai');
        }
        if (!empty($readiness['publish_ready']) === false) {
            $next_actions[] = array('key' => 'publish_review', 'label' => 'Review final QA before publishing', 'owner' => 'user_approval');
        }

        return array(
            'version' => 'v2.0-2026-04-30',
            'source' => 'consultation_canonical_pipeline',
            'generated_at' => current_time('mysql'),
            'post_id' => (int) $post_id,
            'session_id' => (string) $session_id,
            'business_name' => (string) ($business_data['business_name'] ?? ''),
            'business_manifest' => array(
                'present' => !empty($business_manifest),
                'manifest_id' => (string) ($business_manifest['manifest_id'] ?? ''),
                'version' => (string) ($business_manifest['version'] ?? ''),
                'source_of_truth' => (string) ($business_manifest['source_of_truth'] ?? 'vmo_business_manifest'),
            ),
            'pipeline' => array(
                'canonical' => true,
                'version' => (string) ($canonical_context['pipeline_version'] ?? 'v2.0-2026-04-30'),
                'preview_policy' => (string) ($canonical_context['preview_policy'] ?? 'block_until_media_complete_and_final_qa_passes'),
                'visual_preset' => (string) ($canonical_context['selected_visual_preset'] ?? ($business_data['visual_preset'] ?? '')),
                'agent_plan_present' => !empty($agent_plan),
                'agent_count' => is_array($agent_plan['agents'] ?? null) ? count($agent_plan['agents']) : 0,
            ),
            'website' => array(
                'type' => 'consultation_generated',
                'source_manifest' => 'vmo_business_manifest',
                'tech_stack' => array('Standalone HTML5', 'Scoped CSS', 'Vanilla JavaScript', 'GSAP-ready motion bundle', 'Lenis-ready smooth scroll', 'Swiper-ready carousels', 'VMO Canonical Website Pipeline (builder only)'),
                'standalone_contract' => array(
                    'required' => true,
                    'runtime_dependency' => 'none_wordpress_at_runtime',
                    'builder_shell' => 'wordpress_plugin_can_stage_and_export_only',
                    'editing_surface' => 'vmo_internal_builder_and_template_only',
                    'wordpress_editor_usage' => 'disabled_for_generated_sites',
                    'publishable_artifact' => 'plain_html_css_js_with_resolved_assets',
                    'portable' => $standalone_portable,
                    'forbidden_dependencies' => array('shortcodes', 'wp-admin', 'admin-ajax.php', 'wp-json', 'wp-content/plugins', 'wp-content/themes', 'wp-includes', 'window.wp'),
                    'asset_policy' => 'resolved media must be absolute public URLs or bundled relative assets before preview/publish/export',
                    'integration_policy' => 'forms, payments, maps, analytics, SEO, and ecommerce must use portable HTML attributes, public webhooks, hosted checkout/payment links, map links/embeds, and static metadata rather than WordPress-only callbacks',
                ),
                'visual_preset' => (string) ($canonical_context['selected_visual_preset'] ?? ($business_data['visual_preset'] ?? '')),
                'pages' => $page_summaries,
                'page_count' => count($page_summaries),
                'builder_state_blocks' => is_array($builder_state['blocks'] ?? null) ? count($builder_state['blocks']) : 0,
                'component_state' => $component_state_manifest,
            ),
            'agent_plan' => $agent_plan,
            'media_manifest' => $media_manifest,
            'integrations' => array(
                'payments' => array(
                    'required' => $needs_payments,
                    'enabled' => defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED,
                    'provider' => 'vmo_pay_stripe_connect',
                    'collection_mode' => $collection_mode,
                    'configured' => $payment_configured,
                    'stripe_account_id' => (string) ($connected_account['stripe_account_id'] ?? ''),
                    'charges_enabled' => !empty($connected_account['charges_enabled']),
                    'payouts_enabled' => !empty($connected_account['payouts_enabled']),
                ),
                'ecommerce' => array(
                    'required' => $needs_ecommerce,
                    'source_manifest' => 'vmo_business_manifest.ecommerce_config',
                    'product_count' => count($products),
                    'donation_fund_count' => count($donation_funds),
                    'demo_products_present' => $demo_products_present,
                    'product_content_packets_complete' => empty($needs_ecommerce) || !empty($product_generation_packets['content_packets_complete']),
                    'product_media_packets_complete' => empty($needs_ecommerce) || !empty($product_generation_packets['media_packets_complete']),
                    'product_generation_ready_before_preview' => empty($needs_ecommerce) || !empty($product_generation_packets['ready_before_preview']),
                    'product_generation_packets' => $product_generation_packets,
                    'store_url' => (string) home_url($collection_mode === 'donations' ? '/donate' : '/store'),
                    'checkout_pattern' => (string) add_query_arg(array('vmo_buy_product' => '{product_id}'), home_url('/')),
                ),
                'forms' => array(
                    'contact_form' => $has_contact_form,
                    'appointment_form' => $has_booking_form,
                    'form_mappings' => is_array($business_manifest['website_structure']['form_mappings'] ?? null) ? $business_manifest['website_structure']['form_mappings'] : array(),
                    'submission_handler' => 'vmo_handle_website_form_submission',
                    'crm_tags' => array_values(array_filter(array('website_form', $has_booking_form ? 'booking' : '', $has_contact_form ? 'contact' : ''))),
                ),
                'maps' => array(
                    'required' => $location !== '',
                    'location' => $location,
                    'embedded_or_ready' => $has_map,
                ),
                'shipping' => array(
                    'required' => $shipping_required,
                    'configured' => !$shipping_required || !empty($shipping_profiles),
                    'profile_count' => count($shipping_profiles),
                ),
                'seo' => array(
                    'enabled' => true,
                    'ready' => $seo_ready,
                    'roadmap_available' => !empty($business_package['seo_strategy']) || !empty($business_package['seo_roadmap']),
                ),
                'media_generation' => array(
                    'queued_asset_count' => $queued_asset_count,
                    'complete' => !empty($readiness['media_complete']) && !empty($media_manifest['media_complete']),
                    'manifest' => $media_manifest,
                    'queue_counts' => $media_queue_counts,
                    'desktop_mobile_variants_required' => !empty($media_manifest['desktop_mobile_variants_required']),
                    'poster_frames_required' => !empty($media_manifest['poster_frames_required']),
                    'queue_markers' => array('data-vmo-generate', 'data-vmo-prompt', 'data-vmo-sora', 'data-vmo-sora-prompt'),
                ),
                'analytics' => array(
                    'enabled' => true,
                    'tracker' => 'vmo_analytics_tracker',
                    'tracking_contract' => $tracking_contract,
                    'dashboard_metric_targets' => $tracking_contract['dashboard_targets'] ?? array('website_visitors', 'page_views', 'conversion_rate'),
                ),
                'email' => array(
                    'roles_confirmed' => !empty($email_roles),
                    'role_count' => count(array_filter($email_roles)),
                    'auto_reply_enabled' => !empty($email_roles_meta['ai_auto_reply_enabled']),
                ),
                'phone' => array(
                    'dashboard_connected' => true,
                    'tool' => 'vmo_phone_agent',
                ),
            ),
            'dashboard' => array(
                'connected' => true,
                'modules' => array('website', 'payments', 'store', 'orders', 'shipping', 'forms', 'appointments', 'seo', 'media_queue', 'analytics', 'crm', 'email', 'phone', 'tasks', 'strategic_dashboard'),
                'indicators' => array(
                    'draft_assembled' => !empty($readiness['draft_assembled']),
                    'media_complete' => !empty($readiness['media_complete']) && !empty($media_manifest['media_complete']),
                    'preview_ready' => !empty($readiness['preview_ready']),
                    'generation_completed' => !empty($readiness['generation_completed']),
                    'publish_ready' => !empty($readiness['publish_ready']),
                    'qa_score' => $qa_score,
                    'payments_ready' => $payment_configured,
                    'forms_connected' => $forms_connected,
                    'seo_ready' => $seo_ready,
                    'shipping_ready' => !$shipping_required || !empty($shipping_profiles),
                    'product_content_packets_complete' => empty($needs_ecommerce) || !empty($product_generation_packets['content_packets_complete']),
                    'product_media_packets_complete' => empty($needs_ecommerce) || !empty($product_generation_packets['media_packets_complete']),
                    'ecommerce_product_generation_ready' => empty($needs_ecommerce) || !empty($product_generation_packets['ready_before_preview']),
                    'tracking_contract_ready' => !empty($tracking_contract['required_events']),
                    'component_state_ready' => !empty($component_state_manifest['state_present']) && !empty($component_state_manifest['contract_enforced']) && ((int) ($component_state_manifest['component_count'] ?? 0) >= count($page_summaries)),
                    'standalone_portable' => $standalone_portable,
                ),
                'next_actions' => $next_actions,
            ),
        );
    }

    /**
     * Ensure core pages always exist so the generated website is complete.
     */
    private function ensure_minimum_quality_pages($website_pages, $business_data) {
        if (!is_array($website_pages)) {
            $website_pages = array();
        }

        $required_pages = array('home', 'about', 'services', 'faq', 'contact');
        foreach ($required_pages as $required_page) {
            if (empty($website_pages[$required_page])) {
                $website_pages[$required_page] = $this->fallback_page_generation($required_page, array(
                    'business_data' => $business_data,
                    'media_assets' => array(),
                    'conversion_points' => array()
                ));
            }
        }

        return $website_pages;
    }

    /**
     * Normalize, quality-check, and media-process homepage HTML before persisting.
     */
    private function prepare_landing_html_for_publish($landing_html, $business_data, $session_id) {
        $prepared = $this->normalize_generated_page_markup((string) $landing_html);

        if (!$this->is_high_quality_page_html($prepared)) {
            $prepared = $this->fallback_page_generation('home', array(
                'business_data' => $business_data,
                'media_assets' => array(),
                'conversion_points' => array()
            ));
        }

        $processed = $this->process_generated_page_html($prepared, array(
            'page_type' => 'home',
            'session_id' => (string) $session_id,
            'user_id' => get_current_user_id(),
            'project_id' => 0,
            'business_location' => $business_data['location'] ?? ''
        ));

        $processed = $this->normalize_generated_page_markup($processed ?: $prepared);

        if (!$this->is_high_quality_page_html($processed)) {
            $processed = $this->fallback_page_generation('home', array(
                'business_data' => $business_data,
                'media_assets' => array(),
                'conversion_points' => array()
            ));
        }

        return $processed;
    }
    
    /**
     * Render a page structure to HTML string
     */
    private function render_page_to_html($page_data, $business_data) {
        if (is_string($page_data)) {
            return $page_data;
        }
        
        // If it's structured data, convert to HTML
        $business_name = $business_data['business_name'] ?? 'My Business';
        $tagline = $business_data['tagline'] ?? '';
        
        $html = '<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">';
        $html .= '<title>' . esc_html($business_name) . '</title>';
        $html .= '<style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,-apple-system,sans-serif;line-height:1.6;color:#1f2937}</style>';
        $html .= '</head><body>';
        
        // Convert page data to HTML sections
        if (isset($page_data['sections']) && is_array($page_data['sections'])) {
            foreach ($page_data['sections'] as $section) {
                $html .= $this->render_section($section, $business_data);
            }
        } else {
            // Fallback - wrap content
            $html .= '<main style="max-width:1200px;margin:0 auto;padding:40px 20px;">';
            $html .= is_array($page_data) ? json_encode($page_data) : $page_data;
            $html .= '</main>';
        }
        
        $html .= '</body></html>';
        return $html;
    }
    
    /**
     * Render a single section to HTML
     */
    private function render_section($section, $business_data) {
        $type = $section['type'] ?? 'content';
        $content = $section['content'] ?? '';
        $style = $section['style'] ?? '';
        
        return '<section style="padding:60px 20px;' . esc_attr($style) . '">' . 
               '<div style="max-width:1200px;margin:0 auto;">' . 
               (is_string($content) ? $content : '') . 
               '</div></section>';
    }
    
    /**
     * Generate a fallback homepage if rendering fails
     */
    private function generate_fallback_homepage($business_data) {
        return $this->fallback_page_generation('home', array(
            'business_data' => is_array($business_data) ? $business_data : array(),
            'media_assets' => array(),
            'conversion_points' => array()
        ));
    }
    
    /**
     * Send completion notification to user
     */
    private function send_completion_notification($consultation_data, $website_url, $business_package) {
        $preview_ready = is_array($website_url) && !empty($website_url['preview_ready']);
        $media_complete = is_array($website_url) && !empty($website_url['media_complete']);
        if (!$preview_ready || !$media_complete) {
            return;
        }

        // Extract email from stored consultation_data if available
        $client_data = json_decode($consultation_data->consultation_data ?? '{}', true);
        $email = $client_data['email'] ?? ($client_data['contact_info']['email'] ?? '');
        
        if (!$email) return;
        
        $subject = "Your Website Preview Is Ready! - {$client_data['business_name']}";
        $message = $this->build_completion_email($client_data, $website_url, $business_package);
        
        wp_mail($email, $subject, $message, array('Content-Type: text/html; charset=UTF-8'));
    }
    
    /**
     * Build completion email content
     */
    private function build_completion_email($client_data, $website_url, $business_package) {
    $business_name = $client_data['business_name'] ?? 'Your Business';
    $preview_url = is_array($website_url) ? ($website_url['preview_url'] ?? ($website_url['url'] ?? '')) : (string) $website_url;
    $publish_ready = is_array($website_url) ? !empty($website_url['publish_ready']) : false;
        
        $email = "<html><body>";
        $email .= "<h2>🎉 Your Website Preview Is Ready For Review</h2>";
        $email .= "<p>Dear " . ($client_data['contact_name'] ?? 'Business Owner') . ",</p>";
        $email .= "<p>Your complete business website draft for <strong>{$business_name}</strong> has finished its required media generation and is ready for preview, refinement, and final approval.</p>";
        
        $email .= "<div style='background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;'>";
        $email .= "<h3>🌐 Your Website Draft</h3>";
        if ($preview_url) {
            $email .= "<p><strong>Preview URL:</strong> <a href='{$preview_url}' target='_blank'>{$preview_url}</a></p>";
        }
        $email .= "<p><strong>Publish readiness:</strong> " . ($publish_ready ? 'Passed QA checks and is ready for approval.' : 'Further review is required before publishing.') . "</p>";
        $email .= "<p>Your website includes:</p>";
        $email .= "<ul>";
        $email .= "<li>✅ Professional design optimized for your industry</li>";
        $email .= "<li>✅ All your uploaded media professionally integrated</li>";
        $email .= "<li>✅ AI-powered chat assistant for customer support</li>";
        $email .= "<li>✅ Mobile-responsive design</li>";
        $email .= "<li>✅ SEO optimization</li>";
        $email .= "<li>✅ Conversion-focused layout</li>";
        $email .= "</ul>";
        $email .= "</div>";
        
        $email .= "<div style='background: #e3f2fd; padding: 20px; border-radius: 8px; margin: 20px 0;'>";
        $email .= "<h3>📊 Your Complete Business Package</h3>";
        $email .= "<p>Along with your website, you've received a comprehensive business intelligence package including:</p>";
        $email .= "<ul>";
        $email .= "<li>📈 Detailed market analysis and competitive insights</li>";
        $email .= "<li>🎯 Strategic business plan and growth recommendations</li>";
        $email .= "<li>💡 Marketing strategy and implementation guide</li>";
        $email .= "<li>🔍 SEO optimization plan</li>";
        $email .= "<li>📝 Legal document templates</li>";
        $email .= "<li>💰 Financial projections and business model analysis</li>";
        $email .= "</ul>";
        $email .= "</div>";
        
        $email .= "<h3>🤖 AI Assistant Integration</h3>";
        $email .= "<p>Your website now includes an intelligent AI assistant that can:</p>";
        $email .= "<ul>";
        $email .= "<li>Answer customer questions 24/7</li>";
        $email .= "<li>Provide information about your services</li>";
        $email .= "<li>Help with bookings and inquiries</li>";
        $email .= "<li>Capture leads automatically</li>";
        $email .= "</ul>";
        
        $email .= "<div style='background: #fff3cd; padding: 15px; border-radius: 8px; margin: 20px 0;'>";
        $email .= "<h4>Next Steps:</h4>";
        $email .= "<ol>";
        $email .= "<li>Open your draft preview and review the website experience</li>";
        $email .= "<li>Review your business package documents</li>";
        $email .= "<li>Test the AI assistant functionality</li>";
        $email .= "<li>Publish the website once the readiness checks and approvals are satisfied</li>";
        $email .= "</ol>";
        $email .= "</div>";
        
        $email .= "<p>Thank you for choosing VMO AI Website Builder. We're excited to see your business thrive!</p>";
        $email .= "<p>Best regards,<br>The VMO AI Team</p>";
        $email .= "</body></html>";
        
        return $email;
    }
    
    /**
     * Send progress updates during website generation
     */
    private function send_progress_update($message, $progress) {
        // Store progress in transient for AJAX polling
        set_transient('vmo_website_generation_progress', array(
            'message' => $message,
            'progress' => $progress,
            'timestamp' => time()
        ), 300); // 5 minutes
        
        // You could also use websockets or server-sent events here for real-time updates
    }
    
    /**
     * AJAX handler to get website generation progress
     */
    public function ajax_get_consultation_progress() {
        $progress = get_transient('vmo_website_generation_progress');
        
        if ($progress) {
            wp_send_json_success($progress);
        } else {
            wp_send_json_error(array('message' => 'No progress data available'));
        }
    }
    
    /**
     * ✅ NEW: AJAX handler for CHUNKED business generation (prevents timeouts on long operations)
     * 
     * Like deep research, this splits the generation process into 12 chunks:
     * Chunk 1: Initialize & Process consultation data
     * Chunk 2: Process uploaded media assets
     * Chunk 3: Generate business package & analysis
     * Chunk 4: Setup payments & e-commerce (if applicable)
     * Chunk 5: Create website architecture
    * Chunk 6: Generate first page batch
    * Chunk 7: Generate second page batch
    * Chunk 8: Generate third page batch
    * Chunk 9: Generate fourth page batch
    * Chunk 10: Generate fifth page batch
    * Chunk 11: Generate legal documents + integrate AI assistant
     * Chunk 12: Publish website & send notifications
     */
    public function ajax_trigger_business_generation() {
        $this->prepare_long_running_runtime();

        // ✅ BULLETPROOF: Start output buffering to capture ANY stray output
        ob_start();
        
        // Debug log to confirm handler is reached
        error_log('VMO Generation Handler: ajax_trigger_business_generation called');
        
        try {
            // Verify nonce - check all possible nonce parameter names
            $nonce = '';
            if (!empty($_POST['nonce'])) {
                $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
            } elseif (!empty($_POST['security'])) {
                $nonce = sanitize_text_field(wp_unslash($_POST['security']));
            } elseif (!empty($_POST['_ajax_nonce'])) {
                $nonce = sanitize_text_field(wp_unslash($_POST['_ajax_nonce']));
            }
            
            $ok_ajax = $nonce && wp_verify_nonce($nonce, 'vmo_ajax_nonce');
            $ok_builder = $nonce && wp_verify_nonce($nonce, 'vmo_builder_nonce');
            $ok_admin = $nonce && wp_verify_nonce($nonce, 'vmo_admin_nonce');
            
            if (!($ok_ajax || $ok_builder || $ok_admin)) {
                error_log('VMO Generation Handler: Nonce verification failed. Nonce value: ' . substr($nonce, 0, 10) . '...');
                ob_end_clean();
                wp_send_json_error(array('message' => 'Security check failed'), 403);
                return;
            }
            
            error_log('VMO Generation Handler: Nonce verified successfully');
            
            $session_id = sanitize_text_field($_POST['session_id'] ?? '');
            $chunk = intval($_POST['chunk'] ?? 1);
            $user_id = get_current_user_id();
            
            if (!$session_id) {
                ob_end_clean();
                wp_send_json_error(array('message' => 'Session ID required'));
                return;
            }
        
            // Get or initialize generation state
            $gen_state_key = 'vmo_gen_state_' . $session_id;
            $gen_state = get_transient($gen_state_key) ?: [];
            
            // Total chunks
            $total_chunks = self::TOTAL_GENERATION_CHUNKS;
            
            error_log("VMO Generation: Executing chunk {$chunk}/{$total_chunks} for session {$session_id}");
            
            // ✅ RECOVERY: If essential state is missing for later chunks, try to recover
            if ($chunk > 1 && empty($gen_state['business_data'])) {
                error_log("VMO Generation: Recovering missing business_data for chunk {$chunk}");
                $consultation_row = $this->consultation_db->get_consultation_by_session($session_id);
                if ($consultation_row) {
                    $gen_state['business_data'] = $this->process_consultation_data($consultation_row);
                    $gen_state['consultation_id'] = $consultation_row->id;
                } else {
                    // Create minimal fallback data
                    $gen_state['business_data'] = [
                        'session_id' => $session_id,
                        'business_name' => 'Your Business',
                        'industry' => '',
                        'location' => '',
                        'goals' => []
                    ];
                }
            }
            
            if ($chunk >= 5 && empty($gen_state['architecture'])) {
                error_log("VMO Generation: Recovering missing architecture for chunk {$chunk}");
                $gen_state['media_assets'] = $gen_state['media_assets'] ?? [];
                $gen_state['business_data'] = $this->ensure_ecommerce_product_packets(
                    is_array($gen_state['business_data'] ?? null) ? $gen_state['business_data'] : array(),
                    $gen_state['media_assets']
                );
                $gen_state['business_package'] = $this->attach_ecommerce_product_packets_to_package(
                    is_array($gen_state['business_package'] ?? null) ? $gen_state['business_package'] : array(),
                    $gen_state['business_data']
                );
                $gen_state['architecture'] = $this->create_website_architecture(
                    $gen_state['business_data'], 
                    $gen_state['media_assets']
                );
            }

            if ($chunk >= 6 && (!isset($gen_state['page_queue']) || !is_array($gen_state['page_queue']))) {
                error_log("VMO Generation: Rebuilding missing page_queue for chunk {$chunk}");
                $gen_state['page_queue'] = $this->initialize_page_generation_queue($gen_state['architecture'] ?? array());
            }
            
            switch ($chunk) {
                case 1:
                    // ✅ TOKEN DEDUCTION: Charge for complete website generation (8,000 tokens)
                    // This is charged once at the start of generation for the entire process
                    if (function_exists('vmo_deduct_tokens_for_operation')) {
                        if (!vmo_deduct_tokens_for_operation($user_id, 'generate_website')) {
                            ob_end_clean();
                            wp_send_json_error(array(
                                'message' => 'Insufficient tokens for website generation. Please top up your balance.',
                                'status' => 'insufficient_tokens',
                                'tokens_required' => function_exists('vmo_get_token_cost') ? vmo_get_token_cost('generate_website') : 8000
                            ));
                            return;
                        }
                    }
                    
                    // Initialize & Process consultation data
                    $consultation_row = $this->consultation_db->get_consultation_by_session($session_id);
                    if (!$consultation_row) {
                        throw new Exception('Consultation not found');
                    }
                    $gen_state['business_data'] = $this->process_consultation_data($consultation_row);
                    $gen_state['consultation_id'] = $consultation_row->id;
                    $gen_state['start_time'] = time();
                    $this->send_progress_update('Processing consultation data...', 8);
                    break;
                    
                case 2:
                    // Process uploaded media assets
                    $gen_state['media_assets'] = $this->process_uploaded_media($session_id);
                    $this->send_progress_update('Processing media assets...', 16);
                    break;
                    
                case 3:
                    // Generate business package & analysis
                    $gen_state['business_package'] = $this->generate_business_package($gen_state['business_data']);
                    $gen_state['business_data'] = $this->ensure_business_manifest_for_generation($gen_state['business_data'], $gen_state['business_package']);
                    $gen_state['business_data'] = $this->ensure_ecommerce_product_packets(
                        $gen_state['business_data'],
                        is_array($gen_state['media_assets'] ?? null) ? $gen_state['media_assets'] : array()
                    );
                    $gen_state['business_package'] = $this->attach_ecommerce_product_packets_to_package($gen_state['business_package'], $gen_state['business_data']);
                    $this->send_progress_update('Generating business strategy...', 25);
                    break;
                    
                case 4:
                    // Setup payments & e-commerce (if applicable)
                    $gen_state['business_data'] = $this->ensure_ecommerce_product_packets(
                        is_array($gen_state['business_data'] ?? null) ? $gen_state['business_data'] : array(),
                        is_array($gen_state['media_assets'] ?? null) ? $gen_state['media_assets'] : array()
                    );
                    $gen_state['business_package'] = $this->attach_ecommerce_product_packets_to_package(
                        is_array($gen_state['business_package'] ?? null) ? $gen_state['business_package'] : array(),
                        $gen_state['business_data']
                    );
                    if (defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED) {
                        $mode = (string) ($gen_state['business_data']['ecommerce']['collection_mode'] ?? 'products');
                        if ($mode === 'donations') {
                            $this->auto_setup_payments_and_store($gen_state['business_data'], true);
                        } else {
                            $products = $this->auto_setup_payments_and_store($gen_state['business_data']);
                            if (is_array($products) && isset($gen_state['business_data']['ecommerce'])) {
                                $gen_state['business_data']['ecommerce']['products'] = $products;
                            }
                        }
                    }
                    $this->send_progress_update('Setting up e-commerce...', 33);
                    break;
                    
                case 5:
                    // Create website architecture
                    $gen_state['architecture'] = $this->create_website_architecture(
                        $gen_state['business_data'], 
                        $gen_state['media_assets']
                    );
                    $gen_state['page_queue'] = $this->initialize_page_generation_queue($gen_state['architecture']);
                    $gen_state['pages_generated'] = is_array($gen_state['pages_generated'] ?? null)
                        ? $gen_state['pages_generated']
                        : array();
                    $this->send_progress_update('Designing website architecture...', 42);
                    break;
                    
                case 6:
                    // Generate first batch of pages
                    $this->generate_pages_from_queue_chunk($gen_state, self::MAX_PAGES_PER_GENERATION_CHUNK);
                    $this->send_progress_update('Creating core website pages...', 50);
                    break;
                    
                case 7:
                    // Generate second batch of pages
                    $this->generate_pages_from_queue_chunk($gen_state, self::MAX_PAGES_PER_GENERATION_CHUNK);
                    $this->send_progress_update('Creating additional pages...', 58);
                    break;
                    
                case 8:
                    // Generate third batch of pages
                    $this->generate_pages_from_queue_chunk($gen_state, self::MAX_PAGES_PER_GENERATION_CHUNK);
                    $this->send_progress_update('Creating services and sales pages...', 66);
                    break;
                    
                case 9:
                    // Generate fourth batch of pages
                    $this->generate_pages_from_queue_chunk($gen_state, self::MAX_PAGES_PER_GENERATION_CHUNK);
                    $this->send_progress_update('Creating contact and conversion pages...', 74);
                    break;
                    
                case 10:
                    // Generate fifth batch of pages
                    $batch_result = $this->generate_pages_from_queue_chunk($gen_state, self::MAX_PAGES_PER_GENERATION_CHUNK);
                    if (($batch_result['remaining'] ?? 0) > 0) {
                        error_log('VMO Generation: Page queue still has ' . intval($batch_result['remaining']) . ' pages after chunk 10.');
                    }
                    $this->send_progress_update('Completing remaining pages...', 82);
                    break;
                    
                case 11:
                    // Final page sweep (if needed), then legal docs + AI integration
                    if (!empty($gen_state['page_queue']) && is_array($gen_state['page_queue'])) {
                        $this->generate_pages_from_queue_chunk($gen_state, self::MAX_PAGES_PER_GENERATION_CHUNK);
                    }
                    $gen_state['legal_docs'] = $this->generate_legal_documents($gen_state['business_data']);
                    $this->integrate_ai_assistant($session_id, $gen_state['business_data']);
                    $this->send_progress_update('Generating legal docs and integrating AI...', 91);
                    break;
                    
                case 12:
                    // Publish website & send notifications
                    if (!empty($gen_state['page_queue']) && is_array($gen_state['page_queue'])) {
                        error_log('VMO Generation: Draining final page queue in publish chunk. Remaining=' . count($gen_state['page_queue']));
                        $remaining_pages = array_values($gen_state['page_queue']);
                        $existing_pages = is_array($gen_state['pages_generated'] ?? null) ? $gen_state['pages_generated'] : array();
                        $remaining_generated = $this->generate_website_pages_chunk(
                            $gen_state['architecture'] ?? array(),
                            $gen_state['business_data'] ?? array(),
                            $gen_state['media_assets'] ?? array(),
                            $remaining_pages
                        );
                        $gen_state['pages_generated'] = array_merge($existing_pages, is_array($remaining_generated) ? $remaining_generated : array());
                        $gen_state['page_queue'] = array();
                    }

                    // Page count validation: Warn if generated pages don't match architecture plan
                    $pages_generated_arr = $gen_state['pages_generated'] ?? [];
                    $architecture_pages = is_array($gen_state['architecture']['pages'] ?? null) ? $gen_state['architecture']['pages'] : [];
                    $generated_count = is_array($pages_generated_arr) ? count($pages_generated_arr) : 0;
                    $planned_count = count($architecture_pages);
                    if ($planned_count > 0 && $generated_count < $planned_count) {
                        error_log("VMO Generation WARNING: Only {$generated_count} of {$planned_count} planned pages were generated for session {$session_id}.");
                        $gen_state['page_count_warning'] = "Generated {$generated_count} of {$planned_count} planned pages.";
                    }

                    $gen_state['website_url'] = $this->save_and_publish_website(
                        $pages_generated_arr,
                        $gen_state['business_data'],
                        $session_id,
                        $gen_state['business_package'] ?? array(),
                        $gen_state['media_assets'] ?? array()
                    );

                    if (method_exists($this->business_wizard, 'persist_generation_results_from_business_data')) {
                        $this->business_wizard->persist_generation_results_from_business_data(
                            $gen_state['business_data'] ?? array(),
                            $gen_state['business_package'] ?? array(),
                            is_array($gen_state['website_url'] ?? null) ? $gen_state['website_url'] : array(),
                            $session_id
                        );
                    }

                    $preview_ready = is_array($gen_state['website_url'] ?? null) ? !empty($gen_state['website_url']['preview_ready']) : false;
                    $media_complete = is_array($gen_state['website_url'] ?? null) ? !empty($gen_state['website_url']['media_complete']) : false;
                    $gen_state['preview_ready'] = $preview_ready;
                    $gen_state['media_complete'] = $media_complete;
                    $gen_state['draft_assembled'] = !empty($gen_state['website_url']);
                    
                    // Keep final consultation completion reserved for the full setup readiness contract.
                    $this->update_status_by_session($session_id, $preview_ready ? 'preview_ready' : 'generating_media');
                    
                    // Get consultation row for notification
                    $consultation_row = $this->consultation_db->get_consultation_by_session($session_id);
                    if ($consultation_row) {
                        $this->send_completion_notification(
                            $consultation_row, 
                            $gen_state['website_url'], 
                            $gen_state['business_package'] ?? []
                        );
                    }
                    
                    // Calculate total time
                    $total_time = time() - ($gen_state['start_time'] ?? time());
                    $gen_state['total_time'] = $total_time;
                    $gen_state['completed'] = true;
                    
                    $this->send_progress_update(
                        $preview_ready
                            ? 'Website preview is ready for review and approval.'
                            : 'Website draft assembled. Required media is still generating before preview release.',
                        100
                    );
                    break;
            }
            
            // Save generation state
            set_transient($gen_state_key, $gen_state, 7200); // ✅ BOTTLENECK FIX: 2-hour TTL (was 1hr) — generation with images can exceed 1 hour
            
            // ✅ PERSISTENCE FIX: When generation completes, persist final state to consultation DB permanently
            $is_complete = ($chunk >= $total_chunks);
            if ($is_complete && !empty($gen_state['completed'])) {
                $user_id = get_current_user_id();
                if ($user_id) {
                    $existing_row = $this->consultation_db->get_consultation_by_session($session_id);
                    if ($existing_row) {
                        $website_data_arr = json_decode($existing_row->website_data ?: '{}', true);
                        if (!is_array($website_data_arr)) $website_data_arr = [];
                        $preview_ready = !empty($gen_state['preview_ready']);
                        $website_data_arr['draft_assembled'] = !empty($gen_state['draft_assembled']);
                        $website_data_arr['media_complete'] = !empty($gen_state['media_complete']);
                        $website_data_arr['preview_ready'] = $preview_ready;
                        $website_data_arr['draft_ready'] = $preview_ready;
                        $website_data_arr['generation_completed'] = $preview_ready;
                        $website_data_arr['generation_completed_at'] = $preview_ready ? current_time('mysql') : null;
                        $website_data_arr['total_time'] = $gen_state['total_time'] ?? null;
                        $website_data_arr['pages_generated'] = array_keys($gen_state['pages_generated'] ?? []);
                        $website_data_arr['architecture'] = $gen_state['architecture'] ?? null;
                        $website_data_arr['business_package'] = $gen_state['business_package'] ?? null;
                        $website_data_arr['legal_docs'] = is_array($gen_state['legal_docs'] ?? null) ? $gen_state['legal_docs'] : array();
                        $website_data_arr['legal_docs_ready'] = !empty($website_data_arr['legal_docs']);
                        if (!empty($gen_state['website_url'])) {
                            $website_data_arr['website_url'] = $gen_state['website_url'];
                        }
                        $this->consultation_db->save_consultation($user_id, $session_id, [
                            'website_data' => json_encode($website_data_arr),
                            'status' => $preview_ready ? 'preview_ready' : 'generating_media',
                        ]);
                        error_log('✅ VMO: Generation state permanently saved to consultation DB for session ' . $session_id);
                    }
                }
            }
            
            $progress_percent = round(($chunk / $total_chunks) * 100);
            
            // ✅ Clear any stray output before sending JSON
            ob_end_clean();
            
            // ✅ For final chunk, include homepage HTML for immediate preview
            $homepage_html = null;
            $post_id = null;
            $preview_url = null;
            $pages_map = array();
            $builder_state = array();
            $project_title = null;
            
            // Extract homepage HTML for live preview
            if ($is_complete && !empty($gen_state['pages_generated']['home'])) {
                $home_content = $gen_state['pages_generated']['home'];
                if (is_string($home_content)) {
                    $homepage_html = $home_content;
                } elseif (is_array($home_content) && isset($home_content['html'])) {
                    $homepage_html = $home_content['html'];
                }
            }
            
            // Extract draft website info (website_url is now an array from save_and_publish_website)
            $chunk_preview_ready = !empty($gen_state['preview_ready']);
            if ($is_complete && !empty($gen_state['website_url'])) {
                $website_data = $gen_state['website_url'];
                if (is_array($website_data)) {
                    $post_id = $website_data['post_id'] ?? null;
                    $preview_url = $chunk_preview_ready ? ($website_data['preview_url'] ?? null) : null;
                    $pages_map = is_array($website_data['pages_map'] ?? null) ? $website_data['pages_map'] : array();
                    $builder_state = is_array($website_data['builder_state'] ?? null) ? $website_data['builder_state'] : array();
                    $project_title = $website_data['project_title'] ?? null;
                    // Use the rendered HTML from save if we don't have it yet
                    if (empty($homepage_html) && !empty($website_data['homepage_html'])) {
                        $homepage_html = $website_data['homepage_html'];
                    }
                }
            }
            
            wp_send_json_success([
                'chunk_completed' => $chunk,
                'total_chunks' => $total_chunks,
                'progress_percent' => $progress_percent,
                'is_complete' => $is_complete,
                'post_id' => $post_id,
                'preview_url' => $preview_url,
                'status' => $is_complete
                    ? (!empty($gen_state['preview_ready']) ? 'draft' : 'generating_media')
                    : 'generating',
                'homepage_html' => $homepage_html,
                'pages_map' => $pages_map,
                'builder_state' => $builder_state,
                'project_title' => $project_title,
                'qa_report' => ($is_complete && is_array($gen_state['website_url'] ?? null)) ? ($gen_state['website_url']['qa_report'] ?? array()) : array(),
                'media_complete' => ($is_complete && is_array($gen_state['website_url'] ?? null)) ? !empty($gen_state['website_url']['media_complete']) : !empty($gen_state['media_complete']),
                'preview_ready' => ($is_complete && is_array($gen_state['website_url'] ?? null)) ? !empty($gen_state['website_url']['preview_ready']) : $chunk_preview_ready,
                'publish_ready' => ($is_complete && is_array($gen_state['website_url'] ?? null)) ? !empty($gen_state['website_url']['publish_ready']) : false,
                'total_time' => $gen_state['total_time'] ?? null,
                'business_name' => $gen_state['business_data']['business_name'] ?? null,
                'message' => $is_complete
                    ? ($chunk_preview_ready
                        ? 'Website preview is ready for review.'
                        : 'Website draft assembled. The preview will finish after required media completes.')
                    : "Chunk {$chunk}/{$total_chunks} completed"
            ]);
            
        } catch (\Throwable $e) {
            $stray_output = ob_get_clean();
            if (!empty($stray_output)) {
                error_log('VMO Generation: Stray output captured: ' . substr($stray_output, 0, 500));
            }
            error_log('VMO Generation Error (Chunk ' . $chunk . '): ' . $e->getMessage());
            wp_send_json_error([
                'message' => 'Generation failed: ' . $e->getMessage(),
                'chunk' => $chunk,
                'can_retry' => true
            ]);
        }
    }
    
    /**
     * Generate a subset of website pages (for chunked generation)
     * ✅ BULLETPROOF: Each page generation wrapped in try-catch
     */
    private function generate_website_pages_chunk($architecture, $business_data, $media_assets, $page_types) {
        $generated = [];
        
        // ✅ Defensive: Ensure inputs are valid
        if (!is_array($architecture)) {
            $architecture = array(
                'pages' => $this->looks_like_vmo_plugin_blueprint($business_data) ? $this->get_vmo_plugin_default_pages() : array('home', 'about', 'services', 'contact'),
                'conversion_points' => array(),
            );
        }
        if (!is_array($business_data)) {
            $business_data = array('business_name' => 'Your Business', 'industry' => '', 'location' => '');
        }
        if (!is_array($media_assets)) {
            $media_assets = array();
        }

        $all_pages = is_array($architecture['pages'] ?? null) ? $architecture['pages'] : [];
        
        foreach ($page_types as $page_type) {
            try {
                if (!in_array($page_type, $all_pages, true)) {
                    continue;
                }

                if ($page_type === 'shop') {
                    $page_content = $this->generate_shop_page($business_data, $media_assets);
                } elseif ($page_type === 'donate') {
                    $page_content = $this->generate_donate_page($business_data, $media_assets);
                } elseif (strpos($page_type, 'product-') === 0) {
                    $product = $this->find_product_by_slug($business_data, $page_type);
                    $page_content = $this->generate_product_page($product, $business_data, $media_assets);
                } else {
                    $page_content = $this->generate_single_page($page_type, $business_data, $media_assets, $architecture);
                }

                if (!is_string($page_content) || trim($page_content) === '') {
                    $page_content = $this->fallback_page_generation($page_type, array(
                        'business_data' => $business_data,
                        'media_assets' => $media_assets,
                        'conversion_points' => $architecture['conversion_points'] ?? array()
                    ));
                }

                $generated[$page_type] = $page_content;
            } catch (\Throwable $e) {
                error_log("VMO Page Generation Error ({$page_type}): " . $e->getMessage());
                // ✅ Generate fallback page so we don't break the flow
                $generated[$page_type] = $this->fallback_page_generation($page_type, array(
                    'business_data' => $business_data,
                    'media_assets' => $media_assets
                ));
            }
        }
        
        return $generated;
    }

    private function initialize_page_generation_queue($architecture) {
        $all_pages = is_array($architecture['pages'] ?? null) ? array_values($architecture['pages']) : array();
        if (empty($all_pages)) {
            return array('home', 'about', 'services', 'contact');
        }

        $priority = array('home', 'about', 'services', 'shop', 'donate', 'portfolio', 'contact', 'booking', 'blog');
        $queue = array();

        foreach ($priority as $page_type) {
            if (in_array($page_type, $all_pages, true) && !in_array($page_type, $queue, true)) {
                $queue[] = $page_type;
            }
        }

        foreach ($all_pages as $page_type) {
            if (!is_string($page_type) || $page_type === '') {
                continue;
            }
            if (!in_array($page_type, $queue, true)) {
                $queue[] = $page_type;
            }
        }

        return $queue;
    }

    private function generate_pages_from_queue_chunk(&$gen_state, $max_pages = self::MAX_PAGES_PER_GENERATION_CHUNK) {
        $queue = is_array($gen_state['page_queue'] ?? null) ? array_values($gen_state['page_queue']) : array();
        if (empty($queue)) {
            return array('generated' => 0, 'remaining' => 0, 'pages' => array());
        }

        $batch_size = max(1, intval($max_pages));
        $pages_for_chunk = array_slice($queue, 0, $batch_size);

        $generated_batch = $this->generate_website_pages_chunk(
            $gen_state['architecture'] ?? array(),
            $gen_state['business_data'] ?? array(),
            $gen_state['media_assets'] ?? array(),
            $pages_for_chunk
        );

        $existing_pages = is_array($gen_state['pages_generated'] ?? null) ? $gen_state['pages_generated'] : array();
        $gen_state['pages_generated'] = array_merge($existing_pages, is_array($generated_batch) ? $generated_batch : array());
        $gen_state['page_queue'] = array_values(array_slice($queue, count($pages_for_chunk)));

        return array(
            'generated' => is_array($generated_batch) ? count($generated_batch) : 0,
            'remaining' => count($gen_state['page_queue']),
            'pages' => $pages_for_chunk,
        );
    }
    
    /**
     * Generate legal documents for the business using AI
     * ✅ BULLETPROOF: Wrapped in try-catch with fallback templates
     */
    private function generate_legal_documents($business_data) {
        $legal_docs = [];
        $business_name = $business_data['business_name'] ?? 'Your Business';
        $business_type = $business_data['business_type'] ?? '';
        $location = $business_data['location'] ?? '';
        
        try {
            // Use the website_assistant or generate via AI call
            if (method_exists($this->business_wizard, 'generate_legal_document')) {
                // Use existing method if available
                $legal_docs['privacy_policy'] = $this->business_wizard->generate_legal_document(
                    'privacy_policy', 
                    $business_name, 
                    $business_type,
                    $location
                );
                
                $legal_docs['terms_of_service'] = $this->business_wizard->generate_legal_document(
                    'terms_of_service', 
                    $business_name, 
                    $business_type,
                    $location
                );
                
                if (in_array('e-commerce', $business_data['goals'] ?? [])) {
                    $legal_docs['refund_policy'] = $this->business_wizard->generate_legal_document(
                        'refund_policy', 
                        $business_name, 
                        $business_type,
                        $location
                    );
                }
            } else {
                // Generate using AI API
                $legal_docs['privacy_policy'] = $this->generate_legal_document_via_ai(
                    'privacy_policy', 
                    $business_name, 
                    $business_type,
                    $location
                );
                
                $legal_docs['terms_of_service'] = $this->generate_legal_document_via_ai(
                    'terms_of_service', 
                    $business_name, 
                    $business_type,
                    $location
                );
                
                if (in_array('e-commerce', $business_data['goals'] ?? [])) {
                    $legal_docs['refund_policy'] = $this->generate_legal_document_via_ai(
                        'refund_policy', 
                        $business_name, 
                        $business_type,
                        $location
                    );
                }
            }
        } catch (\Throwable $e) {
            error_log('VMO Legal Documents Generation Error: ' . $e->getMessage());
            // ✅ Fallback: Return placeholder legal docs
            $legal_docs = $this->generate_fallback_legal_docs($business_name);
        }
        
        return $legal_docs;
    }
    
    /**
     * ✅ NEW: Fallback legal documents when AI generation fails
     */
    private function generate_fallback_legal_docs($business_name) {
        return array(
            'privacy_policy' => "<h1>Privacy Policy</h1><p>This privacy policy explains how {$business_name} collects, uses, and protects your personal information. Please contact us for detailed privacy information.</p>",
            'terms_of_service' => "<h1>Terms of Service</h1><p>By using the services of {$business_name}, you agree to these terms. Please contact us for detailed terms and conditions.</p>",
            'is_fallback' => true
        );
    }
    
    /**
     * Generate a single legal document using AI
     */
    private function generate_legal_document_via_ai($doc_type, $business_name, $business_type, $location) {
        $doc_titles = [
            'privacy_policy' => 'Privacy Policy',
            'terms_of_service' => 'Terms of Service',
            'refund_policy' => 'Refund & Cancellation Policy'
        ];
        
        $title = $doc_titles[$doc_type] ?? 'Legal Document';
        
        // Use the website assistant to generate via AI if available
        if (method_exists($this->website_assistant, 'generate_content')) {
            $prompt = "Generate a comprehensive {$title} for {$business_name}, a {$business_type} business located in {$location}. 
            
            Requirements:
            - Professional legal language
            - Compliant with GDPR, CCPA, and general data protection regulations
            - Cover all essential sections for a {$business_type} business
            - Include contact information placeholders
            - Format in clean HTML with proper headings
            
            Return ONLY the HTML content, no explanations.";
            
            return $this->website_assistant->generate_content($prompt);
        }
        
        // Fallback: return a template
        return $this->get_legal_document_template($doc_type, $business_name, $business_type, $location);
    }
    
    /**
     * Get a basic legal document template as fallback
     */
    private function get_legal_document_template($doc_type, $business_name, $business_type, $location) {
        $date = date('F j, Y');
        
        $templates = [
            'privacy_policy' => "
                <h1>Privacy Policy</h1>
                <p><strong>Last updated:</strong> {$date}</p>
                <p>{$business_name} ('we', 'our', or 'us') is committed to protecting your privacy. This Privacy Policy explains how we collect, use, and safeguard your information.</p>
                <h2>Information We Collect</h2>
                <p>We may collect personal information you provide directly to us, including name, email address, phone number, and any other information you choose to provide.</p>
                <h2>How We Use Your Information</h2>
                <p>We use the information we collect to provide, maintain, and improve our services, communicate with you, and comply with legal obligations.</p>
                <h2>SMS Messaging Privacy</h2>
                <p>If you opt in to receive text messages from {$business_name}, we may collect your mobile phone number, consent records, messaging preferences, and related interaction data in order to send account updates, customer care messages, appointment reminders, service notifications, and occasional promotional messages where permitted.</p>
                <p>Message frequency varies. Message and data rates may apply.</p>
                <p>Mobile opt-in data, text messaging originator consent, and related personal information will not be sold, rented, or shared with third parties or affiliates for their own marketing or promotional purposes. We may share this information only with service providers that support message delivery, compliance, suppression management, or related operational functions necessary to provide the messaging service.</p>
                <h2>Contact Us</h2>
                <p>If you have questions about this Privacy Policy, please contact us at our business location in {$location}.</p>
            ",
            'terms_of_service' => "
                <h1>Terms of Service</h1>
                <p><strong>Last updated:</strong> {$date}</p>
                <p>Welcome to {$business_name}. By accessing or using our services, you agree to be bound by these Terms of Service.</p>
                <h2>Use of Services</h2>
                <p>You may use our services only in compliance with these Terms and all applicable laws and regulations.</p>
                <h2>SMS Terms</h2>
                <p>By providing your mobile number and affirmatively opting in, you agree to receive recurring SMS/MMS messages from {$business_name} related to account updates, customer care, appointment reminders, service notifications, and occasional promotional content where permitted.</p>
                <p>Message frequency varies. Message and data rates may apply. Consent is not a condition of purchase. Reply STOP to opt out at any time. Reply HELP for help.</p>
                <h2>Limitation of Liability</h2>
                <p>{$business_name} shall not be liable for any indirect, incidental, special, consequential, or punitive damages.</p>
                <h2>Governing Law</h2>
                <p>These Terms shall be governed by the laws of the jurisdiction in which we operate ({$location}).</p>
            ",
            'refund_policy' => "
                <h1>Refund & Cancellation Policy</h1>
                <p><strong>Last updated:</strong> {$date}</p>
                <p>At {$business_name}, we want you to be completely satisfied with your purchase.</p>
                <h2>Refund Eligibility</h2>
                <p>Products may be returned within 30 days of purchase in their original condition for a full refund.</p>
                <h2>How to Request a Refund</h2>
                <p>To request a refund, please contact our customer service team with your order details.</p>
                <h2>Processing Time</h2>
                <p>Refunds will be processed within 5-10 business days after we receive your return.</p>
            "
        ];
        
        return $templates[$doc_type] ?? '<p>Document not available.</p>';
    }
    
    /**
     * AJAX handler to get generation status
     */
    public function ajax_get_generation_status() {
        $nonce = sanitize_text_field($_REQUEST['security'] ?? $_REQUEST['nonce'] ?? $_REQUEST['_ajax_nonce'] ?? '');
        $ok = wp_verify_nonce($nonce, 'vmo_ajax_nonce') ||
              wp_verify_nonce($nonce, 'vmo_builder_nonce') ||
              wp_verify_nonce($nonce, 'vmo_admin_nonce');

        if (!$ok) {
            wp_send_json_error(['message' => 'Security check failed'], 403);
            return;
        }

        $session_id = sanitize_text_field($_POST['session_id'] ?? $_GET['session_id'] ?? '');
        
        if (!$session_id) {
            wp_send_json_error(['message' => 'Session ID required']);
            return;
        }

        $row = $this->get_authorized_consultation_row($session_id);
        if (is_wp_error($row)) {
            wp_send_json_error(['message' => $row->get_error_message()], (int) ($row->get_error_data('status') ?: 403));
            return;
        }
        
        $gen_state_key = 'vmo_gen_state_' . $session_id;
        $gen_state = get_transient($gen_state_key);
        $progress = get_transient('vmo_website_generation_progress');
        
        // ✅ PERSISTENCE FIX: If transient expired, check permanent DB for completed state
        if (empty($gen_state)) {
            if ($row && !empty($row->website_data)) {
                $website_data = json_decode($row->website_data, true);
                if (is_array($website_data) && (!empty($website_data['generation_completed']) || ($row->status ?? '') === 'generating_media')) {
                    $gen_state = [
                        'completed' => !empty($website_data['generation_completed']),
                        'draft_assembled' => !empty($website_data['draft_assembled']),
                        'media_complete' => !empty($website_data['media_complete']),
                        'preview_ready' => !empty($website_data['preview_ready']),
                        'status' => !empty($website_data['generation_completed']) ? 'completed' : 'generating_media',
                        'pages_generated' => $website_data['pages_generated'] ?? [],
                        'total_time' => $website_data['total_time'] ?? null,
                        'source' => 'permanent_db',
                    ];
                }
            }
        }
        
        wp_send_json_success([
            'generation_state' => $gen_state ?: null,
            'progress' => $progress ?: null,
            'is_complete' => !empty($gen_state['completed'])
        ]);
    }
    
    /**
     * Helper methods for architecture planning
     */
    private function create_navigation_structure($pages, $page_labels = array()) {
        $nav_items = array();
        foreach ($pages as $page) {
            $nav_items[] = array(
                'slug' => $page,
                'title' => $this->humanize_page_title($page, $page_labels),
                'order' => array_search($page, $pages)
            );
        }
        return $nav_items;
    }
    
    private function plan_media_placement($pages, $media_assets) {
        $placement_plan = array();
        
        foreach ($pages as $page) {
            $placement_plan[$page] = array();
            
            foreach ($media_assets as $media) {
                if ($this->should_place_media_on_page($media, $page)) {
                    $placement_plan[$page][] = array(
                        'media_id' => $media['id'],
                        'placement' => $this->determine_media_placement($media, $page)
                    );
                }
            }
        }
        
        return $placement_plan;
    }
    
    private function identify_conversion_points($business_data) {
        if ($this->looks_like_vmo_plugin_blueprint($business_data)) {
            return array('download_plugin', 'select_plan', 'quicklaunch_onboarding', 'affiliate_signup');
        }

        $conversion_points = array('contact_form', 'phone_call');
        $goals = is_array($business_data['goals'] ?? null) ? $business_data['goals'] : array();
        $business_type = strtolower((string) ($business_data['business_type'] ?? ''));
        
        if (in_array('e-commerce', $goals, true)) {
            $conversion_points[] = 'online_ordering';
        }
        
        if (strpos($business_type, 'service') !== false) {
            $conversion_points[] = 'appointment_booking';
        }
        
        return $conversion_points;
    }
    
    private function should_place_media_on_page($media, $page) {
        // Logic to determine if media should be placed on specific page
        if ($media['usage_context'] === 'logo') return true; // Logo goes on all pages
        if ($media['usage_context'] === 'hero_image' && $page === 'home') return true;
        if ($media['usage_context'] === 'team_photo' && $page === 'about') return true;
        if ($media['usage_context'] === 'product_image' && in_array($page, array('home', 'services', 'portfolio'))) return true;
        
        return false;
    }
    
    private function determine_media_placement($media, $page) {
        if ($media['usage_context'] === 'logo') return 'header';
        if ($media['usage_context'] === 'hero_image') return 'hero_section';
        if ($media['usage_context'] === 'team_photo') return 'team_section';
        
        return 'content_area';
    }
    
    private function fallback_page_generation($page_type, $context) {
        $business_data = is_array($context['business_data'] ?? null) ? $context['business_data'] : array();
        $page_type = is_string($page_type) ? $page_type : 'page';

        if ($this->looks_like_vmo_plugin_blueprint($business_data)) {
            return $this->fallback_vmo_product_page_generation($page_type, $context);
        }

        $business_name_plain = sanitize_text_field((string) ($business_data['business_name'] ?? 'Your Business'));
        if ($business_name_plain === '') {
            $business_name_plain = 'Your Business';
        }
        $industry_plain = sanitize_text_field((string) ($business_data['industry'] ?? 'professional services'));
        if ($industry_plain === '') {
            $industry_plain = 'professional services';
        }
        $location_plain = sanitize_text_field((string) ($business_data['location'] ?? 'your area'));
        if ($location_plain === '') {
            $location_plain = 'your area';
        }
        $target_plain = sanitize_text_field((string) ($business_data['target_audience'] ?? 'modern customers'));
        if ($target_plain === '') {
            $target_plain = 'modern customers';
        }

        $tagline_plain = sanitize_text_field((string) ($business_data['tagline'] ?? 'Premium digital experiences engineered for measurable growth.'));
        if ($tagline_plain === '') {
            $tagline_plain = 'Premium digital experiences engineered for measurable growth.';
        }

        $contact_info = is_array($business_data['contact_info'] ?? null) ? $business_data['contact_info'] : array();
        $email_plain = sanitize_email((string) ($contact_info['email'] ?? ''));
        $phone_plain = sanitize_text_field((string) ($contact_info['phone'] ?? ''));
        $phone_href = preg_replace('/[^0-9+]/', '', $phone_plain);

        $services = is_array($business_data['services'] ?? null) ? array_values(array_filter($business_data['services'])) : array();
        if (!$services) {
            $services = array('Strategy Consulting', 'Implementation Services', 'Ongoing Support');
        }

        $prompt_business = preg_replace('/[^a-z0-9\s\-]/i', '', $business_name_plain);
        $prompt_industry = preg_replace('/[^a-z0-9\s\-]/i', '', $industry_plain);

        $service_cards = '';
        foreach (array_slice($services, 0, 6) as $service) {
            $service_label = sanitize_text_field((string) $service);
            if ($service_label === '') {
                continue;
            }

            $service_cards .= '<article class="vmo-service-card" data-vmo-reveal>'
                . '<div class="vmo-service-media">[GENERATED_IMAGE: premium website section image for ' . esc_html($service_label) . ' in ' . esc_html($prompt_industry) . ' style, modern and clean]</div>'
                . '<h3>' . esc_html($service_label) . '</h3>'
                . '<p>Designed to help ' . esc_html($target_plain) . ' achieve faster, clearer outcomes with confidence.</p>'
                . '</article>';
        }

        if ($service_cards === '') {
            $service_cards = '<article class="vmo-service-card" data-vmo-reveal>'
                . '<div class="vmo-service-media">[GENERATED_IMAGE: premium business service visual for modern website]</div>'
                . '<h3>Core Service</h3>'
                . '<p>Delivered with clarity, speed, and measurable value.</p>'
                . '</article>';
        }

        $contact_items = '<li><strong>Location:</strong> ' . esc_html($location_plain) . '</li>';
        if ($phone_plain !== '') {
            $contact_items .= '<li><strong>Phone:</strong> <a href="tel:' . esc_attr($phone_href ?: $phone_plain) . '">' . esc_html($phone_plain) . '</a></li>';
        }
        if ($email_plain !== '') {
            $contact_items .= '<li><strong>Email:</strong> <a href="mailto:' . esc_attr($email_plain) . '">' . esc_html($email_plain) . '</a></li>';
        }

        $page_headings = array(
            'home' => 'Build momentum with ' . $business_name_plain,
            'about' => 'About ' . $business_name_plain,
            'services' => 'Services designed for growth',
            'faq' => 'Answers to common questions',
            'contact' => 'Let\'s build your next milestone',
            'booking' => 'Book your strategy session',
            'blog' => 'Insights from ' . $business_name_plain,
            'portfolio' => 'Recent work and proven outcomes',
        );

        $hero_heading = $page_headings[$page_type] ?? ('Welcome to ' . $business_name_plain);
        $hero_subtitle = 'A premium ' . $industry_plain . ' experience for ' . $target_plain . '.';

        $service_summary = implode(', ', array_slice(array_map('sanitize_text_field', $services), 0, 4));
        if ($service_summary === '') {
            $service_summary = 'strategy, execution, and ongoing support';
        }
        $faq_contact_channel = 'our contact page';
        $faq_get_started_channel = 'through our contact page';
        if ($phone_plain !== '') {
            $faq_contact_channel = 'calling us at ' . $phone_plain;
            $faq_get_started_channel = 'by calling us at ' . $phone_plain;
        } elseif ($email_plain !== '') {
            $faq_contact_channel = 'emailing us at ' . $email_plain;
            $faq_get_started_channel = 'by emailing us at ' . $email_plain;
        }
        $faq_items = array(
            array(
                'question' => 'What does ' . $business_name_plain . ' help with?',
                'answer' => $business_name_plain . ' helps with ' . $service_summary . ', tailored for ' . $target_plain . '.'
            ),
            array(
                'question' => 'Who is this designed for?',
                'answer' => 'Our work is built for ' . $target_plain . ' who want clear outcomes, premium execution, and a smoother path to growth.'
            ),
            array(
                'question' => 'How do I get started?',
                'answer' => 'The fastest way to get started is ' . $faq_get_started_channel . ' so we can understand your goals and recommend the right next step.'
            ),
            array(
                'question' => 'Do you work with clients in ' . $location_plain . '?',
                'answer' => 'Yes. We support clients in ' . $location_plain . ' and can discuss the best delivery format for your project during the first conversation.'
            ),
            array(
                'question' => 'How quickly do you respond?',
                'answer' => 'We typically respond within one business day and keep the process clear from the first inquiry onward.'
            ),
            array(
                'question' => 'How is pricing handled?',
                'answer' => 'Pricing depends on the scope and priorities of your project, so we walk you through the best-fit option once we understand what you need.'
            ),
        );
        $faq_cards = '';
        foreach ($faq_items as $faq_item) {
            $faq_cards .= '<article class="vmo-card"><h3>' . esc_html($faq_item['question']) . '</h3><p>' . esc_html($faq_item['answer']) . '</p></article>';
        }

        switch ($page_type) {
            case 'about':
                $main_content = '<section class="vmo-section vmo-card" data-vmo-reveal>'
                    . '<h2>Our story</h2>'
                    . '<p>' . esc_html($business_name_plain) . ' exists to deliver exceptional ' . esc_html($industry_plain) . ' outcomes for ' . esc_html($target_plain) . '. We combine strategy, execution, and measurable performance in one integrated process.</p>'
                    . '<div class="vmo-stats">'
                    . '<div><strong>10+</strong><span>Years Combined Experience</span></div>'
                    . '<div><strong>500+</strong><span>Projects Delivered</span></div>'
                    . '<div><strong>98%</strong><span>Client Satisfaction</span></div>'
                    . '</div>'
                    . '</section>'
                    . '<section class="vmo-section" data-vmo-reveal>'
                    . '<h2>What we value</h2>'
                    . '<div class="vmo-grid vmo-three">'
                    . '<article class="vmo-card"><h3>Clarity</h3><p>Every deliverable is structured for fast decisions and confident action.</p></article>'
                    . '<article class="vmo-card"><h3>Craft</h3><p>We combine strong strategy with polished execution and premium presentation.</p></article>'
                    . '<article class="vmo-card"><h3>Results</h3><p>Our work is measured by business impact, not just visual output.</p></article>'
                    . '</div>'
                    . '</section>';
                break;

            case 'services':
                $main_content = '<section class="vmo-section" data-vmo-reveal>'
                    . '<h2>Core services</h2>'
                    . '<div class="vmo-grid vmo-three">' . $service_cards . '</div>'
                    . '</section>'
                    . '<section class="vmo-section vmo-card" data-vmo-reveal>'
                    . '<h2>How we deliver</h2>'
                    . '<ol class="vmo-process">'
                    . '<li><strong>Discover:</strong> Align your goals, audience, and positioning.</li>'
                    . '<li><strong>Build:</strong> Create assets and systems tailored to your growth plan.</li>'
                    . '<li><strong>Launch:</strong> Deploy, optimize, and improve performance over time.</li>'
                    . '</ol>'
                    . '<a class="vmo-btn" href="#contact">Request a custom proposal</a>'
                    . '</section>';
                break;

            case 'contact':
                $main_content = '<section class="vmo-section" data-vmo-reveal>'
                    . '<div class="vmo-grid vmo-two">'
                    . '<div class="vmo-card">'
                    . '<h2>Contact details</h2>'
                    . '<ul class="vmo-contact-list">' . $contact_items . '</ul>'
                    . '<p>We typically respond within one business day.</p>'
                    . '</div>'
                    . '<form class="vmo-card vmo-form" aria-label="Contact form">'
                    . '<h2>Send a message</h2>'
                    . '<label>Name<input type="text" placeholder="Your name" /></label>'
                    . '<label>Email<input type="email" placeholder="you@example.com" /></label>'
                    . '<label>Message<textarea rows="4" placeholder="Tell us about your goals"></textarea></label>'
                    . '<button type="button" class="vmo-btn">Send message</button>'
                    . '</form>'
                    . '</div>'
                    . '</section>'
                    . '<section class="vmo-section vmo-card" data-vmo-reveal>'
                    . '<h2>Find us</h2>'
                    . '<p>Visit us in ' . esc_html($location_plain) . ' or connect online.</p>'
                    . '<div class="vmo-map-shell">[VMO_MAP_SECTION]</div>'
                    . '</section>';
                break;

            case 'booking':
                $main_content = '<section class="vmo-section" data-vmo-reveal>'
                    . '<div class="vmo-grid vmo-three">'
                    . '<article class="vmo-card"><h3>1. Pick a service</h3><p>Select the outcome you need most right now.</p></article>'
                    . '<article class="vmo-card"><h3>2. Choose a time</h3><p>Book a convenient slot and receive confirmation instantly.</p></article>'
                    . '<article class="vmo-card"><h3>3. Get results</h3><p>Meet with our team and receive a clear execution roadmap.</p></article>'
                    . '</div>'
                    . '</section>'
                    . '<section class="vmo-section vmo-card" data-vmo-reveal>'
                    . '<h2>Request availability</h2>'
                    . '<p>Tell us your preferred dates and we\'ll confirm shortly.</p>'
                    . '<a class="vmo-btn" href="#contact">Book now</a>'
                    . '</section>';
                break;

            case 'blog':
                $main_content = '<section class="vmo-section" data-vmo-reveal>'
                    . '<h2>Featured insights</h2>'
                    . '<div class="vmo-grid vmo-three">'
                    . '<article class="vmo-card"><h3>How to grow in ' . esc_html($industry_plain) . '</h3><p>Practical steps to build momentum and improve conversion.</p></article>'
                    . '<article class="vmo-card"><h3>Brand clarity checklist</h3><p>A simple framework for stronger messaging and positioning.</p></article>'
                    . '<article class="vmo-card"><h3>Operational efficiency playbook</h3><p>Improve handoffs, reduce friction, and scale service quality.</p></article>'
                    . '</div>'
                    . '</section>';
                break;

            case 'portfolio':
                $main_content = '<section class="vmo-section" data-vmo-reveal>'
                    . '<h2>Selected work</h2>'
                    . '<div class="vmo-grid vmo-three">'
                    . '<article class="vmo-card"><div class="vmo-service-media">[GENERATED_IMAGE: premium portfolio project showcase for ' . esc_html($prompt_business) . ' case study one]</div><h3>Project One</h3><p>Positioning and redesign focused on conversion growth.</p></article>'
                    . '<article class="vmo-card"><div class="vmo-service-media">[GENERATED_IMAGE: premium portfolio project showcase for ' . esc_html($prompt_business) . ' case study two]</div><h3>Project Two</h3><p>Service packaging and messaging transformation.</p></article>'
                    . '<article class="vmo-card"><div class="vmo-service-media">[GENERATED_IMAGE: premium portfolio project showcase for ' . esc_html($prompt_business) . ' case study three]</div><h3>Project Three</h3><p>Launch strategy with polished digital execution.</p></article>'
                    . '</div>'
                    . '</section>';
                break;

            case 'faq':
                $main_content = '<section class="vmo-section" data-vmo-reveal>'
                    . '<h2>Frequently asked questions</h2>'
                    . '<div class="vmo-grid vmo-two">' . $faq_cards . '</div>'
                    . '</section>'
                    . '<section class="vmo-section vmo-card" data-vmo-reveal>'
                    . '<h2>Still have a question?</h2>'
                    . '<p>Reach out via ' . esc_html($faq_contact_channel) . ' and we\'ll help you find the best next step.</p>'
                    . '<a class="vmo-btn" href="#contact">Contact us</a>'
                    . '</section>';
                break;

            case 'home':
            default:
                $main_content = '<section class="vmo-section" data-vmo-reveal>'
                    . '<h2>What we do best</h2>'
                    . '<div class="vmo-grid vmo-three">' . $service_cards . '</div>'
                    . '</section>'
                    . '<section class="vmo-section vmo-card" data-vmo-reveal>'
                    . '<h2>Why clients choose ' . esc_html($business_name_plain) . '</h2>'
                    . '<div class="vmo-grid vmo-three">'
                    . '<div><strong>Strategic focus</strong><p>Every decision aligns to measurable growth objectives.</p></div>'
                    . '<div><strong>Premium execution</strong><p>Refined visuals, clear messaging, and conversion-oriented structure.</p></div>'
                    . '<div><strong>Reliable delivery</strong><p>Structured process, transparent milestones, and consistent quality.</p></div>'
                    . '</div>'
                    . '</section>'
                    . '<section class="vmo-section vmo-cta" data-vmo-reveal>'
                    . '<h2>Ready to scale in ' . esc_html($location_plain) . '?</h2>'
                    . '<p>Let\'s turn your goals into a high-performing digital presence.</p>'
                    . '<a class="vmo-btn" href="#contact">Start your project</a>'
                    . '</section>';
                break;
        }

        $scope_id = 'vmo-fallback-' . sanitize_html_class($page_type);

        $html = '<div class="vmo-fallback-page vmo-internal-fallback" id="' . esc_attr($scope_id) . '" data-vmo-internal-fallback="blocked" data-vmo-preview-blocked="true" data-vmo-fallback-reason="premium_generation_required">'
            . '<header class="vmo-hero">'
            . '<div class="vmo-hero-bg vmo-media-placeholder vmo-media-placeholder-image" data-type="image" data-media-id="media_' . wp_generate_password(8, false) . '" data-vmo-generate="true" data-vmo-prompt="premium hero background for ' . esc_attr($prompt_business) . ' in ' . esc_attr($prompt_industry) . ' style, cinematic depth, modern lighting" aria-hidden="true"></div>'
            . '<div class="vmo-hero-overlay"></div>'
            . '<div class="vmo-hero-content" data-vmo-reveal>'
            . '<p class="vmo-kicker">' . esc_html(ucfirst($page_type)) . ' Page</p>'
            . '<h1>' . esc_html($hero_heading) . '</h1>'
            . '<p>' . esc_html($hero_subtitle) . ' ' . esc_html($tagline_plain) . '</p>'
            . '<a class="vmo-btn" href="#contact">Get started</a>'
            . '</div>'
            . '</header>'
            . $main_content
            . '<footer class="vmo-footer" id="contact" data-vmo-reveal>'
            . '<p>© ' . esc_html(date('Y')) . ' ' . esc_html($business_name_plain) . '. Built for growth, clarity, and conversion.</p>'
            . '</footer>'
            . '</div>';

        $html .= '<style>
        .vmo-fallback-page{max-width:1280px;margin:0 auto;padding:0 16px 24px;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#0f172a}
        .vmo-hero{position:relative;overflow:hidden;border-radius:20px;min-height:62vh;margin:16px 0 24px;border:1px solid rgba(148,163,184,.35);box-shadow:0 18px 45px -22px rgba(15,23,42,.35)}
        .vmo-hero-bg{position:absolute;inset:0;background-size:cover;background-position:center;transform:scale(1.04)}
        .vmo-hero-overlay{position:absolute;inset:0;background:linear-gradient(130deg,rgba(2,6,23,.76),rgba(15,23,42,.32))}
        .vmo-hero-content{position:relative;z-index:2;padding:56px 28px;color:#f8fafc;max-width:760px}
        .vmo-kicker{letter-spacing:.08em;text-transform:uppercase;font-size:.78rem;opacity:.86;margin:0 0 10px}
        .vmo-hero h1{margin:0 0 12px;font-size:clamp(2rem,4vw,3.4rem);line-height:1.06}
        .vmo-hero p{margin:0 0 20px;font-size:clamp(1rem,1.8vw,1.2rem);line-height:1.65;opacity:.94}
        .vmo-btn{display:inline-flex;align-items:center;justify-content:center;padding:12px 22px;border-radius:999px;background:linear-gradient(135deg,#22d3ee,#7c3aed);color:#fff;text-decoration:none;font-weight:700;box-shadow:0 14px 30px -18px rgba(124,58,237,.7);border:0;cursor:pointer}
        .vmo-section{margin:0 0 18px}
        .vmo-section h2{margin:0 0 12px;font-size:clamp(1.35rem,2.3vw,1.95rem);line-height:1.2}
        .vmo-section p{margin:0 0 10px;line-height:1.7;color:#334155}
        .vmo-grid{display:grid;gap:14px}
        .vmo-grid.vmo-three{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
        .vmo-grid.vmo-two{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
        .vmo-card{background:linear-gradient(180deg,#ffffff,#f8fafc);border:1px solid rgba(148,163,184,.3);border-radius:16px;padding:16px;box-shadow:0 10px 30px -22px rgba(15,23,42,.35)}
        .vmo-card h3{margin:0 0 8px;font-size:1.1rem}
        .vmo-service-card{background:#fff;border:1px solid rgba(148,163,184,.3);border-radius:16px;overflow:hidden;box-shadow:0 10px 28px -22px rgba(15,23,42,.35)}
        .vmo-service-media{aspect-ratio:16/10;background:#e2e8f0;display:flex;align-items:center;justify-content:center;overflow:hidden}
        .vmo-service-card h3{margin:14px 14px 8px}
        .vmo-service-card p{margin:0 14px 14px}
        .vmo-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-top:12px}
        .vmo-stats strong{display:block;font-size:1.45rem;line-height:1.1}
        .vmo-stats span{display:block;font-size:.88rem;color:#475569}
        .vmo-process{margin:0 0 14px;padding-left:18px;color:#334155}
        .vmo-process li{margin:0 0 8px;line-height:1.6}
        .vmo-contact-list{margin:0;padding-left:18px;color:#334155}
        .vmo-contact-list li{margin:0 0 8px}
        .vmo-contact-list a{color:#0f172a}
        .vmo-form label{display:flex;flex-direction:column;gap:6px;font-weight:600;font-size:.92rem;margin:0 0 10px}
        .vmo-form input,.vmo-form textarea{border:1px solid rgba(148,163,184,.45);border-radius:12px;padding:10px 12px;font:inherit}
        .vmo-map-shell{border-radius:12px;overflow:hidden}
        .vmo-cta{text-align:center;padding:22px;border-radius:18px;background:linear-gradient(135deg,#0f172a,#1e293b);color:#f8fafc}
        .vmo-cta p{color:#cbd5e1}
        .vmo-footer{margin:20px 0 0;padding:14px;border-top:1px solid rgba(148,163,184,.35);color:#475569;text-align:center;font-size:.9rem}
        [data-vmo-reveal]{opacity:0;transform:translateY(14px)}
        @media(max-width:768px){.vmo-hero-content{padding:34px 20px}.vmo-btn{width:100%}}
        </style>';

        $html .= '<script>(function(){
            var root = document.getElementById(' . wp_json_encode($scope_id) . ');
            if(!root) return;
            var els = root.querySelectorAll("[data-vmo-reveal]");
            if(!("IntersectionObserver" in window)){
                els.forEach(function(el){el.style.opacity="1";el.style.transform="translateY(0)";});
                return;
            }
            var io = new IntersectionObserver(function(entries){
                entries.forEach(function(entry){
                    if(!entry.isIntersecting) return;
                    entry.target.style.transition="opacity .6s ease, transform .6s ease";
                    entry.target.style.opacity="1";
                    entry.target.style.transform="translateY(0)";
                    io.unobserve(entry.target);
                });
            }, { threshold: 0.14 });
            els.forEach(function(el){ io.observe(el); });
        })();</script>';

        return $html;
    }

    private function update_status_by_session($session_id, $status) {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_consultations';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update($table, array('status' => $status, 'updated_at' => current_time('mysql')), array('session_id' => $session_id), array('%s','%s'), array('%s'));
    }

    private function get_authorized_consultation_row($session_id) {
        $row = $this->consultation_db->get_consultation_by_session($session_id);
        if (!$row) {
            return new WP_Error('vmo_consultation_not_found', 'Consultation not found.', array('status' => 404));
        }

        $current_user_id = get_current_user_id();
        if ($current_user_id <= 0) {
            return new WP_Error('vmo_login_required', 'You must be logged in to access this consultation.', array('status' => 403));
        }

        if (current_user_can('manage_options') || (int) ($row->user_id ?? 0) === $current_user_id) {
            return $row;
        }

        return new WP_Error('vmo_forbidden_consultation', 'You do not have permission to access this consultation.', array('status' => 403));
    }
    
    /**
     * AJAX handler to publish a draft website
     * Called when user clicks "Publish Now" after refinement
     */
    public function ajax_publish_draft_website() {
        $nonce = sanitize_text_field($_POST['security'] ?? $_POST['nonce'] ?? '');
        
        $ok = wp_verify_nonce($nonce, 'vmo_ajax_nonce') ||
              wp_verify_nonce($nonce, 'vmo_builder_nonce') ||
              wp_verify_nonce($nonce, 'vmo_admin_nonce');
              
        if (!$ok) {
            wp_send_json_error(['message' => 'Security check failed'], 403);
            return;
        }
        
        $post_id = intval($_POST['post_id'] ?? 0);
        
        if (!$post_id) {
            wp_send_json_error(['message' => 'No post ID provided']);
            return;
        }
        
        // Verify the post exists and is a draft
        $post = get_post($post_id);
        
        if (!$post) {
            wp_send_json_error(['message' => 'Website not found']);
            return;
        }
        
        if ($post->post_type !== 'vmo_project') {
            wp_send_json_error(['message' => 'Invalid post type']);
            return;
        }

        $current_user_id = get_current_user_id();
        $can_edit_post = current_user_can('edit_post', $post_id) || ((int) $post->post_author === $current_user_id && $current_user_id > 0);
        if (!$can_edit_post && !current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'You do not have permission to publish this website.'], 403);
            return;
        }

        $session_id = (string) get_post_meta($post_id, 'vmo_session_id', true);
        if ($session_id) {
            $row = $this->get_authorized_consultation_row($session_id);
            if (is_wp_error($row)) {
                wp_send_json_error(['message' => $row->get_error_message()], (int) ($row->get_error_data('status') ?: 403));
                return;
            }
        }

        $pages_map = json_decode((string) get_post_meta($post_id, 'vmo_pages_map', true), true);
        $business_package = json_decode((string) get_post_meta($post_id, 'vmo_business_package', true), true);
        if (!is_array($pages_map)) {
            $pages_map = array();
        }
        if (!is_array($business_package)) {
            $business_package = array();
        }

        // Server-side media completeness check: block publish if media is still generating
        if ($session_id) {
            $gen_state_key = 'vmo_gen_state_' . $session_id;
            $gen_state = get_transient($gen_state_key);
            if (is_array($gen_state)) {
                $media_complete = !empty($gen_state['media_complete']);
                $media_generating = !empty($gen_state['media_generating']) || (!empty($gen_state['completed']) && !$media_complete);
                if ($media_generating && !$media_complete) {
                    wp_send_json_error(array(
                        'message' => 'Media assets are still being generated. Please wait for all images and media to complete before publishing.',
                        'media_incomplete' => true,
                    ));
                    return;
                }
            }
        }

        if (function_exists('vmo_get_approval_guard')) {
            $publish_gate = vmo_get_approval_guard()->can_execute('publish_website', get_current_user_id(), $session_id, array(
                'content' => (string) $post->post_content,
                'pages_map' => $pages_map,
            ), $business_package, array(
                'related_post_id' => $post_id,
                'related_object_id' => (string) $post_id,
                'draft_key' => (string) $post_id,
                'title' => 'Publish website draft: ' . $post->post_title,
                'user_requested_publish' => true,
            ));

            if (empty($publish_gate['allowed'])) {
                wp_send_json_error(array(
                    'message' => $publish_gate['message'] ?? 'Publish blocked by readiness checks.',
                    'approval_required' => !empty($publish_gate['approval_required']),
                    'approval_granted' => !empty($publish_gate['approval_granted']),
                    'queue_id' => $publish_gate['queue_id'] ?? 0,
                    'readiness' => $publish_gate['readiness'] ?? array(),
                ));
                return;
            }
        } else {
            // Fallback: Check QA publish_ready flag directly when approval guard is not loaded
            $qa_ready = get_post_meta($post_id, 'vmo_publish_ready', true);
            if ($qa_ready !== 'yes') {
                wp_send_json_error(array(
                    'message' => 'Website has not passed QA checks. Please run QA evaluation before publishing.',
                    'approval_required' => true,
                    'approval_granted' => false,
                ));
                return;
            }
        }
        
        // Update post status to publish
        $result = wp_update_post([
            'ID' => $post_id,
            'post_status' => 'publish'
        ]);
        
        if (is_wp_error($result)) {
            wp_send_json_error(['message' => 'Failed to publish: ' . $result->get_error_message()]);
            return;
        }
        
        // Update meta to indicate it's published
        update_post_meta($post_id, 'vmo_ready_to_publish', 'yes');
        update_post_meta($post_id, 'vmo_publish_ready', 'yes');
        update_post_meta($post_id, 'vmo_published_date', current_time('mysql'));
        
        // Get the live URL
        $url = get_permalink($post_id);
        
        // Send completion notification email
        if ($session_id) {
            $this->update_status_by_session($session_id, 'published');
        }
        
        wp_send_json_success([
            'message' => 'Website published successfully!',
            'url' => $url,
            'title' => $post->post_title,
            'post_id' => $post_id
        ]);
    }
}

// Initialize the integration system - Priority 20 to ensure all dependencies are loaded first
add_action('plugins_loaded', function() {
    if (class_exists('VMO_Consultation_Database') && 
        class_exists('VMO_Enhanced_Business_Wizard')) {
        // VMO_Website_AI_Assistant is optional - integration can work without it
        $GLOBALS['vmo_consultation_website_integration'] = VMO_Consultation_Website_Integration::get_instance();
    }
}, 20); // Priority 20 - runs AFTER default priority (10) hooks that load dependencies

// Add async website generation hook
add_action('vmo_generate_website_async', function($consultation_data) {
    if (class_exists('VMO_Canonical_Website_Pipeline')) {
        $session_id = is_array($consultation_data) && isset($consultation_data['session_id']) ? $consultation_data['session_id'] : (string) $consultation_data;
        $result = VMO_Canonical_Website_Pipeline::get_instance()->run_from_consultation($session_id);
        if (!is_wp_error($result)) {
            return;
        }
        error_log('VMO canonical async website generation failed: ' . $result->get_error_message());
        return;
    }

    error_log('VMO canonical async website generation skipped: canonical pipeline is not available.');
});
