<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\AddonPack;

use Help4BuilderSuite\Contracts\ModuleContract;

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

final class Module implements ModuleContract
{
    /**
     * @var array<string, string>
     */
    private array $shortcodes = [
        'h4_ea_info_box' => 'renderInfoBox',
        'h4_ea_flip_box' => 'renderFlipBox',
        'h4_ea_advanced_tabs' => 'renderAdvancedTabs',
        'h4_ea_advanced_accordion' => 'renderAdvancedAccordion',
        'h4_ea_testimonial_slider' => 'renderTestimonialSlider',
        'h4_ea_logo_carousel' => 'renderLogoCarousel',
        'h4_ea_data_table' => 'renderDataTable',
        'h4_ea_post_grid' => 'renderPostGrid',
        'h4_ea_countdown' => 'renderCountdown',
        'h4_ea_filterable_gallery' => 'renderFilterableGallery',
        'h4_ea_image_accordion' => 'renderImageAccordion',
        'h4_ea_hotspot' => 'renderHotspot',
        'h4_ea_content_ticker' => 'renderContentTicker',
        'h4_ea_step_flow' => 'renderStepFlow',
        'h4_ea_stats_strip' => 'renderStatsStrip',
        'h4_ea_comparison_cards' => 'renderComparisonCards',
        'h4_ea_template_part_nav' => 'renderTemplatePartNav',
        'h4_ea_screenshot_cards' => 'renderScreenshotCards',
        'h4_pro_dual_heading' => 'renderProfessionalWidget',
        'h4_pro_creative_button' => 'renderProfessionalWidget',
        'h4_pro_feature_list' => 'renderProfessionalWidget',
        'h4_pro_icon_list' => 'renderProfessionalWidget',
        'h4_pro_image_box' => 'renderProfessionalWidget',
        'h4_pro_team_grid' => 'renderProfessionalWidget',
        'h4_pro_testimonial_grid' => 'renderProfessionalWidget',
        'h4_pro_business_hours' => 'renderProfessionalWidget',
        'h4_pro_price_menu' => 'renderProfessionalWidget',
        'h4_pro_timeline' => 'renderProfessionalWidget',
        'h4_pro_modal' => 'renderProfessionalWidget',
        'h4_pro_offcanvas' => 'renderProfessionalWidget',
        'h4_pro_content_toggle' => 'renderProfessionalWidget',
        'h4_pro_tooltip' => 'renderProfessionalWidget',
        'h4_pro_protected_content' => 'renderProfessionalWidget',
        'h4_pro_image_compare' => 'renderProfessionalWidget',
        'h4_pro_lightbox_gallery' => 'renderProfessionalWidget',
        'h4_pro_progress' => 'renderProfessionalWidget',
        'h4_pro_counter' => 'renderProfessionalWidget',
        'h4_pro_circle_progress' => 'renderProfessionalWidget',
        'h4_pro_animated_heading' => 'renderProfessionalWidget',
        'h4_pro_table_of_contents' => 'renderProfessionalWidget',
        'h4_pro_reading_progress' => 'renderProfessionalWidget',
        'h4_pro_scroll_top' => 'renderProfessionalWidget',
        'h4_pro_alert' => 'renderProfessionalWidget',
        'h4_pro_social_icons' => 'renderProfessionalWidget',
        'h4_pro_share_buttons' => 'renderProfessionalWidget',
        'h4_pro_sticky_video' => 'renderProfessionalWidget',
        // Compatibility alias shortcodes.
        'h4_bb_info_box' => 'renderInfoBox',
        'h4_bb_accordion' => 'renderAdvancedAccordion',
        'h4_bb_tabs' => 'renderAdvancedTabs',
        'h4_bb_post_grid' => 'renderPostGrid',
    ];

    public function key(): string
    {
        return 'addon_pack';
    }

    public function boot(): void
    {
        add_action('init', [$this, 'registerShortcodes']);
        add_filter('h4bs_builder_widget_catalog', [$this, 'extendBuilderWidgetCatalog']);
        add_filter('h4bs_builder_widget_control_schemas', [$this, 'extendBuilderWidgetControlSchemas']);
    }

    public function registerShortcodes(): void
    {
        foreach ($this->shortcodes as $tag => $method) {
            if (method_exists($this, $method)) {
                add_shortcode(
                    $tag,
                    function ($atts = [], $content = '', $shortcodeTag = '') use ($method, $tag): string {
                        $this->enqueueAssets();
                        $safeAtts = is_array($atts) ? $atts : [];
                        $safeContent = is_string($content) ? $content : '';
                        $resolvedTag = is_string($shortcodeTag) && $shortcodeTag !== '' ? $shortcodeTag : $tag;
                        return (string) $this->{$method}($safeAtts, $safeContent, $resolvedTag);
                    }
                );
            }
        }
    }

    private function enqueueAssets(): void
    {
        wp_enqueue_style(
            'h4bs-addon-pack',
            H4BS_URL . 'assets/css/addon-pack.css',
            [],
            H4BS_VERSION
        );
        wp_enqueue_script(
            'h4bs-addon-pack',
            H4BS_URL . 'assets/js/addon-pack.js',
            [],
            H4BS_VERSION,
            true
        );
    }

    /**
     * @param array<int, array<string, mixed>> $catalog
     * @return array<int, array<string, mixed>>
     */
    public function extendBuilderWidgetCatalog(array $catalog): array
    {
        $addonWidgets = [
            ['slug' => 'ea-info-box', 'label' => 'Info Box', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_info_box title="Info Box" text="Addon-style content card with icon and CTA." icon="dashicons-info"]', 'status' => 'ready'],
            ['slug' => 'ea-flip-box', 'label' => 'Flip Box', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_flip_box front_title="Front" front_text="Hover to reveal" back_title="Back" back_text="Extra details and CTA"]', 'status' => 'ready'],
            ['slug' => 'ea-advanced-tabs', 'label' => 'Advanced Tabs', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_advanced_tabs items="Overview::Tab one body|Features::Tab two body|Pricing::Tab three body"]', 'status' => 'ready'],
            ['slug' => 'ea-advanced-accordion', 'label' => 'Advanced Accordion', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_advanced_accordion items="Question 1::Answer 1|Question 2::Answer 2"]', 'status' => 'ready'],
            ['slug' => 'ea-testimonial-slider', 'label' => 'Testimonial Slider', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_testimonial_slider items="Jane Doe::Amazing support and speed.|John Roe::Our migration finished in one day."]', 'status' => 'ready'],
            ['slug' => 'ea-logo-carousel', 'label' => 'Logo Carousel', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_logo_carousel logos="https://picsum.photos/seed/logo1/220/80,https://picsum.photos/seed/logo2/220/80,https://picsum.photos/seed/logo3/220/80"]', 'status' => 'ready'],
            ['slug' => 'ea-data-table', 'label' => 'Data Table', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_data_table headers="Feature|Starter|Pro" rows="Storage|25GB|250GB;Support|Email|Priority"]', 'status' => 'ready'],
            ['slug' => 'ea-post-grid', 'label' => 'Post Grid', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_post_grid limit="6" post_type="post"]', 'status' => 'ready'],
            ['slug' => 'ea-countdown', 'label' => 'Countdown', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_countdown end="2030-01-01 00:00:00" label="Launch Window"]', 'status' => 'ready'],
            ['slug' => 'ea-filterable-gallery', 'label' => 'Filterable Gallery', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_filterable_gallery items="Design::https://picsum.photos/seed/ea1/480/320|Marketing::https://picsum.photos/seed/ea2/480/320|Design::https://picsum.photos/seed/ea3/480/320"]', 'status' => 'ready'],
            ['slug' => 'ea-image-accordion', 'label' => 'Image Accordion', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_image_accordion items="Hosting::https://picsum.photos/seed/ea4/600/360|Security::https://picsum.photos/seed/ea5/600/360|Speed::https://picsum.photos/seed/ea6/600/360"]', 'status' => 'ready'],
            ['slug' => 'ea-hotspot', 'label' => 'Hotspot', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_hotspot image="https://picsum.photos/seed/ea7/1000/500" points="24,35,Fast SSD stack|61,48,Edge cache layer|72,26,Security headers"]', 'status' => 'ready'],
            ['slug' => 'ea-content-ticker', 'label' => 'Content Ticker', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_content_ticker items="Managed migrations included|Security hardening by default|Checkout routed through your configured endpoint"]', 'status' => 'ready'],
            ['slug' => 'ea-step-flow', 'label' => 'Step Flow', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_step_flow items="Pick a template::Choose the closest starter|Swap content::Replace words, images, and links|Publish safely::Check mobile, SEO, and forms"]', 'status' => 'ready'],
            ['slug' => 'ea-stats-strip', 'label' => 'Stats Strip', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_stats_strip items="135::Starter specs|230::Default icons|5 min::First page path"]', 'status' => 'ready'],
            ['slug' => 'ea-comparison-cards', 'label' => 'Comparison Cards', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_comparison_cards items="Blank theme::Clean base, builder controls, global parts|Traditional theme::Fixed layout, harder handoff, more overrides"]', 'status' => 'ready'],
            ['slug' => 'ea-template-part-nav', 'label' => 'Template Part Nav', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_template_part_nav items="Headers::Global navigation starters|Footers::Trust, support, and legal blocks|404s::Rescue pages|Canvas::No-header/no-footer pages"]', 'status' => 'ready'],
            ['slug' => 'ea-screenshot-cards', 'label' => 'Screenshot Cards', 'category' => 'Advanced Widgets', 'shortcode' => '[h4_ea_screenshot_cards items="Homepage::https://picsum.photos/seed/shot1/640/420|Mobile view::https://picsum.photos/seed/shot2/640/420|Checkout::https://picsum.photos/seed/shot3/640/420"]', 'status' => 'ready'],
            ['slug' => 'pro-dual-heading', 'label' => 'Dual Color Heading', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_dual_heading first="Build faster." second="Own the result." tag="h2"]', 'status' => 'ready'],
            ['slug' => 'pro-creative-button', 'label' => 'Creative Button', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_creative_button label="Get Started" url="#" style="fill" icon="arrow"]', 'status' => 'ready'],
            ['slug' => 'pro-feature-list', 'label' => 'Feature List', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_feature_list items="Fast launch::Start from a proven layout|Live editing::See changes before saving|Responsive::Tune every breakpoint"]', 'status' => 'ready'],
            ['slug' => 'pro-icon-list', 'label' => 'Linked Icon List', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_icon_list items="Documentation::#docs|Support::#support"]', 'status' => 'ready'],
            ['slug' => 'pro-image-box', 'label' => 'Image Box', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_image_box title="Image Box" text="Combine media, copy, and a link." image="" url="#"]', 'status' => 'ready'],
            ['slug' => 'pro-team-grid', 'label' => 'Team Grid', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_team_grid items="Alex Morgan::Founder::|Jordan Lee::Support Lead::"]', 'status' => 'ready'],
            ['slug' => 'pro-testimonial-grid', 'label' => 'Testimonial Grid', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_testimonial_grid items="Jane Doe::The project launched cleanly.::Owner|John Roe::Support was direct and fast.::Director"]', 'status' => 'ready'],
            ['slug' => 'pro-business-hours', 'label' => 'Business Hours', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_business_hours items="Monday-Friday::9:00 AM-5:00 PM|Saturday::By appointment|Sunday::Closed"]', 'status' => 'ready'],
            ['slug' => 'pro-price-menu', 'label' => 'Price Menu', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_price_menu items="Starter::A focused starting point::$49|Growth::More capacity and support::$99"]', 'status' => 'ready'],
            ['slug' => 'pro-timeline', 'label' => 'Content Timeline', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_timeline items="Plan::Confirm goals and inputs::Day 1|Build::Create and review::Days 2-5|Launch::Test and publish::Day 6"]', 'status' => 'ready'],
            ['slug' => 'pro-modal', 'label' => 'Modal Window', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_modal button="Open Details" title="More information" content="Add focused modal content here."]', 'status' => 'ready'],
            ['slug' => 'pro-offcanvas', 'label' => 'Offcanvas Panel', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_offcanvas button="Open Panel" title="Quick Panel" content="Add navigation, contact, or product details."]', 'status' => 'ready'],
            ['slug' => 'pro-content-toggle', 'label' => 'Content Switcher', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_content_toggle first_label="Monthly" first="Monthly offer details" second_label="Annual" second="Annual offer details"]', 'status' => 'ready'],
            ['slug' => 'pro-tooltip', 'label' => 'Tooltip', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_tooltip label="Hover or focus" tip="Helpful context without clutter."]', 'status' => 'ready'],
            ['slug' => 'pro-protected-content', 'label' => 'Protected Content', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_protected_content access="logged_in" content="Member-only content" fallback="Sign in to continue."]', 'status' => 'ready'],
            ['slug' => 'pro-image-compare', 'label' => 'Image Comparison', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_image_compare before="" after="" before_label="Before" after_label="After" position="50"]', 'status' => 'ready'],
            ['slug' => 'pro-lightbox-gallery', 'label' => 'Lightbox Gallery', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_lightbox_gallery items="Project One::https://picsum.photos/seed/h4lg1/800/600|Project Two::https://picsum.photos/seed/h4lg2/800/600"]', 'status' => 'ready'],
            ['slug' => 'pro-progress', 'label' => 'Progress Bar', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_progress label="Project complete" value="72" color="#1565c0"]', 'status' => 'ready'],
            ['slug' => 'pro-counter', 'label' => 'Animated Counter', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_counter value="250" prefix="" suffix="+" label="Projects completed"]', 'status' => 'ready'],
            ['slug' => 'pro-circle-progress', 'label' => 'Circle Progress', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_circle_progress value="86" label="Readiness"]', 'status' => 'ready'],
            ['slug' => 'pro-animated-heading', 'label' => 'Animated Heading', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_animated_heading prefix="Build" words="faster|smarter|without lock-in" suffix="with Help4"]', 'status' => 'ready'],
            ['slug' => 'pro-table-of-contents', 'label' => 'Table of Contents', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_table_of_contents title="On this page" selector="h2,h3"]', 'status' => 'ready'],
            ['slug' => 'pro-reading-progress', 'label' => 'Reading Progress', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_reading_progress position="top" color="#1565c0"]', 'status' => 'ready'],
            ['slug' => 'pro-scroll-top', 'label' => 'Scroll to Top', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_scroll_top label="Scroll to top" position="right" show_after="480"]', 'status' => 'ready'],
            ['slug' => 'pro-alert', 'label' => 'Alert Box', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_alert type="info" title="Good to know" text="Use this space for a concise notice." dismissible="1"]', 'status' => 'ready'],
            ['slug' => 'pro-social-icons', 'label' => 'Social Links', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_social_icons items="Facebook::https://facebook.com|LinkedIn::https://linkedin.com|X::https://x.com"]', 'status' => 'ready'],
            ['slug' => 'pro-share-buttons', 'label' => 'Share Buttons', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_share_buttons networks="facebook,linkedin,x,email"]', 'status' => 'ready'],
            ['slug' => 'pro-sticky-video', 'label' => 'Sticky Video', 'category' => 'Help4 Pro Widgets', 'shortcode' => '[h4_pro_sticky_video url="" title="Featured video" sticky="1"]', 'status' => 'ready'],
        ];

        return array_merge($catalog, $addonWidgets);
    }

    /**
     * @param array<string, array<int, array<string, mixed>>> $schemas
     * @return array<string, array<int, array<string, mixed>>>
     */
    public function extendBuilderWidgetControlSchemas(array $schemas): array
    {
        $yesNo = [
            ['value' => '1', 'label' => __('Yes', 'help4-builder-suite')],
            ['value' => '0', 'label' => __('No', 'help4-builder-suite')],
        ];
        $items = static fn (string $label, string $default): array => [
            ['key' => 'items', 'label' => $label, 'type' => 'textarea', 'default' => $default],
        ];

        $schemas['h4_pro_dual_heading'] = [
            ['key' => 'first', 'label' => __('First phrase', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Build faster.', 'help4-builder-suite')],
            ['key' => 'second', 'label' => __('Second phrase', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Own the result.', 'help4-builder-suite')],
            ['key' => 'tag', 'label' => __('Heading level', 'help4-builder-suite'), 'type' => 'select', 'options' => ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], 'default' => 'h2'],
            ['key' => 'first_color', 'label' => __('First color', 'help4-builder-suite'), 'type' => 'color', 'default' => '#111827'],
            ['key' => 'second_color', 'label' => __('Second color', 'help4-builder-suite'), 'type' => 'color', 'default' => '#1565c0'],
        ];
        $schemas['h4_pro_creative_button'] = [
            ['key' => 'label', 'label' => __('Button label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Get Started', 'help4-builder-suite')],
            ['key' => 'url', 'label' => __('Destination URL', 'help4-builder-suite'), 'type' => 'text', 'default' => '#'],
            ['key' => 'style', 'label' => __('Style', 'help4-builder-suite'), 'type' => 'select', 'options' => ['fill', 'outline', 'ghost', 'underline'], 'default' => 'fill'],
            ['key' => 'icon', 'label' => __('Icon', 'help4-builder-suite'), 'type' => 'select', 'options' => ['none', 'arrow', 'download', 'external', 'play'], 'default' => 'arrow'],
            ['key' => 'new_tab', 'label' => __('Open in new tab', 'help4-builder-suite'), 'type' => 'select', 'options' => $yesNo, 'default' => '0'],
        ];
        $schemas['h4_pro_feature_list'] = $items(__('Features (title::description | ...)', 'help4-builder-suite'), 'Fast launch::Start from a proven layout|Live editing::See changes before saving');
        $schemas['h4_pro_icon_list'] = $items(__('Items (label::URL | ...)', 'help4-builder-suite'), 'Documentation::#docs|Support::#support');
        $schemas['h4_pro_image_box'] = [
            ['key' => 'title', 'label' => __('Title', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Image Box', 'help4-builder-suite')],
            ['key' => 'text', 'label' => __('Description', 'help4-builder-suite'), 'type' => 'textarea', 'default' => ''],
            ['key' => 'image', 'label' => __('Image URL', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
            ['key' => 'alt', 'label' => __('Image alternative text', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
            ['key' => 'url', 'label' => __('Destination URL', 'help4-builder-suite'), 'type' => 'text', 'default' => '#'],
        ];
        $schemas['h4_pro_team_grid'] = $items(__('People (name::role::image URL | ...)', 'help4-builder-suite'), 'Alex Morgan::Founder::|Jordan Lee::Support Lead::');
        $schemas['h4_pro_testimonial_grid'] = $items(__('Testimonials (name::quote::role | ...)', 'help4-builder-suite'), 'Jane Doe::The project launched cleanly.::Owner');
        $schemas['h4_pro_business_hours'] = $items(__('Hours (day::hours | ...)', 'help4-builder-suite'), 'Monday-Friday::9:00 AM-5:00 PM|Saturday::By appointment|Sunday::Closed');
        $schemas['h4_pro_price_menu'] = $items(__('Items (name::description::price | ...)', 'help4-builder-suite'), 'Starter::A focused starting point::$49|Growth::More capacity::$99');
        $schemas['h4_pro_timeline'] = $items(__('Events (title::description::date | ...)', 'help4-builder-suite'), 'Plan::Confirm goals::Day 1|Build::Create and review::Days 2-5');
        $schemas['h4_pro_lightbox_gallery'] = $items(__('Images (caption::image URL | ...)', 'help4-builder-suite'), 'Project One::https://picsum.photos/seed/h4lg1/800/600');
        $schemas['h4_pro_social_icons'] = $items(__('Networks (label::profile URL | ...)', 'help4-builder-suite'), 'Facebook::https://facebook.com|LinkedIn::https://linkedin.com');
        foreach (['h4_pro_modal', 'h4_pro_offcanvas'] as $tag) {
            $schemas[$tag] = [
                ['key' => 'button', 'label' => __('Trigger label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Open', 'help4-builder-suite')],
                ['key' => 'title', 'label' => __('Panel title', 'help4-builder-suite'), 'type' => 'text', 'default' => __('More information', 'help4-builder-suite')],
                ['key' => 'content', 'label' => __('Panel content', 'help4-builder-suite'), 'type' => 'textarea', 'default' => __('Add focused content here.', 'help4-builder-suite')],
            ];
        }
        $schemas['h4_pro_content_toggle'] = [
            ['key' => 'first_label', 'label' => __('First label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Monthly', 'help4-builder-suite')],
            ['key' => 'first', 'label' => __('First content', 'help4-builder-suite'), 'type' => 'textarea', 'default' => ''],
            ['key' => 'second_label', 'label' => __('Second label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Annual', 'help4-builder-suite')],
            ['key' => 'second', 'label' => __('Second content', 'help4-builder-suite'), 'type' => 'textarea', 'default' => ''],
        ];
        $schemas['h4_pro_tooltip'] = [
            ['key' => 'label', 'label' => __('Visible label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Hover or focus', 'help4-builder-suite')],
            ['key' => 'tip', 'label' => __('Tooltip text', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Helpful context.', 'help4-builder-suite')],
            ['key' => 'position', 'label' => __('Position', 'help4-builder-suite'), 'type' => 'select', 'options' => ['top', 'right', 'bottom', 'left'], 'default' => 'top'],
        ];
        $schemas['h4_pro_protected_content'] = [
            ['key' => 'access', 'label' => __('Access', 'help4-builder-suite'), 'type' => 'select', 'options' => ['public', 'logged_in', 'edit_posts', 'manage_options'], 'default' => 'logged_in'],
            ['key' => 'content', 'label' => __('Protected content', 'help4-builder-suite'), 'type' => 'textarea', 'default' => ''],
            ['key' => 'fallback', 'label' => __('Fallback message', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Sign in to continue.', 'help4-builder-suite')],
        ];
        $schemas['h4_pro_image_compare'] = [
            ['key' => 'before', 'label' => __('Before image URL', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
            ['key' => 'after', 'label' => __('After image URL', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
            ['key' => 'before_label', 'label' => __('Before label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Before', 'help4-builder-suite')],
            ['key' => 'after_label', 'label' => __('After label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('After', 'help4-builder-suite')],
            ['key' => 'position', 'label' => __('Starting position', 'help4-builder-suite'), 'type' => 'number', 'default' => '50'],
        ];
        foreach (['h4_pro_progress', 'h4_pro_counter', 'h4_pro_circle_progress'] as $tag) {
            $schemas[$tag] = [
                ['key' => 'label', 'label' => __('Label', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
                ['key' => 'value', 'label' => __('Value', 'help4-builder-suite'), 'type' => 'number', 'default' => '75'],
                ['key' => 'prefix', 'label' => __('Prefix', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
                ['key' => 'suffix', 'label' => __('Suffix', 'help4-builder-suite'), 'type' => 'text', 'default' => $tag === 'h4_pro_counter' ? '+' : '%'],
                ['key' => 'color', 'label' => __('Accent color', 'help4-builder-suite'), 'type' => 'color', 'default' => '#1565c0'],
            ];
        }
        $schemas['h4_pro_animated_heading'] = [
            ['key' => 'prefix', 'label' => __('Prefix', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Build', 'help4-builder-suite')],
            ['key' => 'words', 'label' => __('Rotating words (word | word)', 'help4-builder-suite'), 'type' => 'textarea', 'default' => 'faster|smarter|without lock-in'],
            ['key' => 'suffix', 'label' => __('Suffix', 'help4-builder-suite'), 'type' => 'text', 'default' => __('with Help4', 'help4-builder-suite')],
            ['key' => 'speed', 'label' => __('Rotation speed (ms)', 'help4-builder-suite'), 'type' => 'number', 'default' => '2400'],
        ];
        $schemas['h4_pro_table_of_contents'] = [
            ['key' => 'title', 'label' => __('Title', 'help4-builder-suite'), 'type' => 'text', 'default' => __('On this page', 'help4-builder-suite')],
            ['key' => 'selector', 'label' => __('Heading selector', 'help4-builder-suite'), 'type' => 'text', 'default' => 'h2,h3'],
        ];
        $schemas['h4_pro_reading_progress'] = [
            ['key' => 'position', 'label' => __('Position', 'help4-builder-suite'), 'type' => 'select', 'options' => ['top', 'bottom', 'inline'], 'default' => 'top'],
            ['key' => 'color', 'label' => __('Progress color', 'help4-builder-suite'), 'type' => 'color', 'default' => '#1565c0'],
        ];
        $schemas['h4_pro_scroll_top'] = [
            ['key' => 'label', 'label' => __('Accessible label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Scroll to top', 'help4-builder-suite')],
            ['key' => 'position', 'label' => __('Position', 'help4-builder-suite'), 'type' => 'select', 'options' => ['left', 'right'], 'default' => 'right'],
            ['key' => 'offset', 'label' => __('Edge offset (px)', 'help4-builder-suite'), 'type' => 'number', 'default' => '18'],
            ['key' => 'show_after', 'label' => __('Show after scrolling (px)', 'help4-builder-suite'), 'type' => 'number', 'default' => '480'],
            ['key' => 'color', 'label' => __('Button color', 'help4-builder-suite'), 'type' => 'color', 'default' => '#1565c0'],
        ];
        $schemas['h4_pro_alert'] = [
            ['key' => 'type', 'label' => __('Type', 'help4-builder-suite'), 'type' => 'select', 'options' => ['info', 'success', 'warning', 'danger'], 'default' => 'info'],
            ['key' => 'title', 'label' => __('Title', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Good to know', 'help4-builder-suite')],
            ['key' => 'text', 'label' => __('Message', 'help4-builder-suite'), 'type' => 'textarea', 'default' => ''],
            ['key' => 'dismissible', 'label' => __('Dismissible', 'help4-builder-suite'), 'type' => 'select', 'options' => $yesNo, 'default' => '1'],
        ];
        $schemas['h4_pro_share_buttons'] = [
            ['key' => 'networks', 'label' => __('Networks', 'help4-builder-suite'), 'type' => 'text', 'default' => 'facebook,linkedin,x,email'],
            ['key' => 'label', 'label' => __('Group label', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Share this page', 'help4-builder-suite')],
        ];
        $schemas['h4_pro_sticky_video'] = [
            ['key' => 'url', 'label' => __('Video URL', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
            ['key' => 'title', 'label' => __('Accessible title', 'help4-builder-suite'), 'type' => 'text', 'default' => __('Featured video', 'help4-builder-suite')],
            ['key' => 'poster', 'label' => __('Poster image URL', 'help4-builder-suite'), 'type' => 'text', 'default' => ''],
            ['key' => 'sticky', 'label' => __('Float while playing', 'help4-builder-suite'), 'type' => 'select', 'options' => $yesNo, 'default' => '1'],
        ];

        return $schemas;
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderInfoBox(array $atts = []): string
    {
        $atts = shortcode_atts(
            [
                'title' => 'Info Box',
                'text' => 'Addon-style info box content.',
                'icon' => 'dashicons-lightbulb',
                'button_label' => '',
                'button_url' => '#',
            ],
            $atts,
            'h4_ea_info_box'
        );

        $title = esc_html((string) $atts['title']);
        $text = esc_html((string) $atts['text']);
        $icon = sanitize_html_class((string) $atts['icon']);
        $buttonLabel = esc_html((string) $atts['button_label']);
        $buttonUrl = esc_url((string) $atts['button_url']);

        return '<article class="h4-addon-card h4-addon-info-box">'
            . '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>'
            . '<h3>' . $title . '</h3>'
            . '<p>' . $text . '</p>'
            . ($buttonLabel !== '' ? '<a class="h4-addon-button" href="' . $buttonUrl . '">' . $buttonLabel . '</a>' : '')
            . '</article>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderFlipBox(array $atts = []): string
    {
        $atts = shortcode_atts(
            [
                'front_title' => 'Front',
                'front_text' => 'Front panel',
                'back_title' => 'Back',
                'back_text' => 'Back panel',
            ],
            $atts,
            'h4_ea_flip_box'
        );

        return '<div class="h4-addon-flip-box"><div class="h4-addon-flip-box__inner">'
            . '<section class="h4-addon-flip-box__face is-front"><h3>' . esc_html((string) $atts['front_title']) . '</h3><p>' . esc_html((string) $atts['front_text']) . '</p></section>'
            . '<section class="h4-addon-flip-box__face is-back"><h3>' . esc_html((string) $atts['back_title']) . '</h3><p>' . esc_html((string) $atts['back_text']) . '</p></section>'
            . '</div></div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderAdvancedTabs(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Overview::Tab body'));
        if (! $items) {
            $items = [['label' => 'Overview', 'value' => 'Tab body']];
        }

        $buttons = [];
        $panels = [];
        foreach ($items as $index => $item) {
            $active = $index === 0 ? ' is-active' : '';
            $id = 'h4ea-tab-' . wp_rand(1000, 9999) . '-' . $index;
            $buttons[] = '<button type="button" class="h4-addon-tab-btn' . $active . '" data-h4-addon-tab-target="' . esc_attr($id) . '">' . esc_html($item['label']) . '</button>';
            $panels[] = '<div id="' . esc_attr($id) . '" class="h4-addon-tab-panel' . $active . '">' . wp_kses_post($item['value']) . '</div>';
        }

        return '<div class="h4-addon-tabs" data-h4-addon-tabs="1"><div class="h4-addon-tab-btns">' . implode('', $buttons) . '</div><div class="h4-addon-tab-panels">' . implode('', $panels) . '</div></div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderAdvancedAccordion(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Question::Answer'));
        if (! $items) {
            $items = [['label' => 'Question', 'value' => 'Answer']];
        }

        $rows = [];
        foreach ($items as $index => $item) {
            $open = $index === 0 ? ' open' : '';
            $rows[] = '<details class="h4-addon-accordion-item"' . $open . '><summary>' . esc_html($item['label']) . '</summary><div class="h4-addon-accordion-body">' . wp_kses_post($item['value']) . '</div></details>';
        }
        return '<div class="h4-addon-accordion">' . implode('', $rows) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderTestimonialSlider(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Client::Great result.'));
        if (! $items) {
            $items = [['label' => 'Client', 'value' => 'Great result.']];
        }
        $cards = [];
        foreach ($items as $item) {
            $cards[] = '<article class="h4-addon-testimonial"><blockquote>' . esc_html($item['value']) . '</blockquote><cite>' . esc_html($item['label']) . '</cite></article>';
        }
        return '<div class="h4-addon-slider-track">' . implode('', $cards) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderLogoCarousel(array $atts = []): string
    {
        $sources = preg_split('/\s*,\s*/', (string) ($atts['logos'] ?? '')) ?: [];
        $logos = [];
        foreach ($sources as $src) {
            $url = esc_url((string) $src);
            if ($url !== '') {
                $logos[] = '<img loading="lazy" src="' . $url . '" alt="">';
            }
        }
        if (! $logos) {
            return $this->renderEmptyState(
                __('Logo Carousel', 'help4-builder-suite'),
                __('Add one or more logo image URLs (comma separated) in the widget settings.', 'help4-builder-suite'),
                '[h4_ea_logo_carousel logos="https://example.com/logo-1.png,https://example.com/logo-2.png"]'
            );
        }
        return '<div class="h4-addon-logo-carousel">' . implode('', $logos) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderDataTable(array $atts = []): string
    {
        $headers = array_values(array_filter(array_map('trim', explode('|', (string) ($atts['headers'] ?? 'Feature|Value')))));
        $rowsRaw = array_values(array_filter(array_map('trim', explode(';', (string) ($atts['rows'] ?? 'Uptime|99.99%')))));

        $thead = '<tr>';
        foreach ($headers as $header) {
            $thead .= '<th>' . esc_html($header) . '</th>';
        }
        $thead .= '</tr>';

        $tbody = '';
        foreach ($rowsRaw as $rowRaw) {
            $cols = array_map('trim', explode('|', $rowRaw));
            $tbody .= '<tr>';
            foreach ($cols as $col) {
                $tbody .= '<td>' . esc_html($col) . '</td>';
            }
            $tbody .= '</tr>';
        }

        return '<div class="h4-addon-table-wrap"><table class="h4-addon-table"><thead>' . $thead . '</thead><tbody>' . $tbody . '</tbody></table></div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderPostGrid(array $atts = []): string
    {
        $atts = shortcode_atts(
            [
                'post_type' => 'post',
                'limit' => '6',
            ],
            $atts,
            'h4_ea_post_grid'
        );

        $posts = get_posts(
            [
                'post_type' => sanitize_key((string) $atts['post_type']),
                'post_status' => 'publish',
                'posts_per_page' => max(1, min(24, (int) $atts['limit'])),
            ]
        );
        if (! is_array($posts) || ! $posts) {
            return $this->renderEmptyState(
                __('Post Grid', 'help4-builder-suite'),
                __('No published posts were found for this source. Publish content or change post type/limit.', 'help4-builder-suite'),
                '[h4_ea_post_grid limit="6" post_type="post"]'
            );
        }

        $cards = [];
        foreach ($posts as $post) {
            $cards[] = '<article class="h4-addon-post-card"><h4><a href="' . esc_url(get_permalink($post)) . '">' . esc_html(get_the_title($post)) . '</a></h4><p>' . esc_html(wp_trim_words((string) get_the_excerpt($post), 20)) . '</p></article>';
        }

        return '<div class="h4-addon-post-grid">' . implode('', $cards) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderCountdown(array $atts = []): string
    {
        $atts = shortcode_atts(
            [
                'end' => gmdate('Y-m-d H:i:s', strtotime('+30 days')),
                'label' => 'Countdown',
            ],
            $atts,
            'h4_ea_countdown'
        );

        $end = strtotime((string) $atts['end']);
        if (! is_int($end) || $end <= 0) {
            return $this->renderEmptyState(
                __('Countdown', 'help4-builder-suite'),
                __('Set a valid end date using format YYYY-MM-DD HH:MM:SS.', 'help4-builder-suite'),
                '[h4_ea_countdown end="2030-01-01 00:00:00" label="Launch Window"]'
            );
        }
        $remaining = max(0, $end - time());
        $days = (int) floor($remaining / DAY_IN_SECONDS);
        $hours = (int) floor(($remaining % DAY_IN_SECONDS) / HOUR_IN_SECONDS);
        $minutes = (int) floor(($remaining % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS);

        return '<div class="h4-addon-countdown"><strong>' . esc_html((string) $atts['label']) . ':</strong> '
            . esc_html((string) $days) . 'd '
            . esc_html((string) $hours) . 'h '
            . esc_html((string) $minutes) . 'm</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderFilterableGallery(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Design::https://picsum.photos/seed/fg1/500/320'));
        if (! $items) {
            return $this->renderEmptyState(
                __('Filterable Gallery', 'help4-builder-suite'),
                __('Add items using category::image-url format separated by pipes.', 'help4-builder-suite'),
                '[h4_ea_filterable_gallery items="Design::https://example.com/1.jpg|Marketing::https://example.com/2.jpg"]'
            );
        }

        $cells = [];
        foreach ($items as $item) {
            $src = esc_url((string) $item['value']);
            if ($src === '') {
                continue;
            }
            $cells[] = '<figure class="h4-addon-gallery-item"><img loading="lazy" src="' . $src . '" alt=""><figcaption>' . esc_html($item['label']) . '</figcaption></figure>';
        }
        if (! $cells) {
            return $this->renderEmptyState(
                __('Filterable Gallery', 'help4-builder-suite'),
                __('The gallery list exists but contains no valid image URLs.', 'help4-builder-suite')
            );
        }
        return '<div class="h4-addon-gallery-grid">' . implode('', $cells) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderImageAccordion(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Panel::https://picsum.photos/seed/ia1/560/360'));
        if (! $items) {
            return $this->renderEmptyState(
                __('Image Accordion', 'help4-builder-suite'),
                __('Add items using title::image-url format separated by pipes.', 'help4-builder-suite'),
                '[h4_ea_image_accordion items="Hosting::https://example.com/hosting.jpg|Security::https://example.com/security.jpg"]'
            );
        }

        $rows = [];
        foreach ($items as $item) {
            $src = esc_url((string) $item['value']);
            if ($src === '') {
                continue;
            }
            $rows[] = '<figure class="h4-addon-image-accordion-item"><img loading="lazy" src="' . $src . '" alt=""><figcaption>' . esc_html($item['label']) . '</figcaption></figure>';
        }
        if (! $rows) {
            return $this->renderEmptyState(
                __('Image Accordion', 'help4-builder-suite'),
                __('The accordion list exists but contains no valid image URLs.', 'help4-builder-suite')
            );
        }
        return '<div class="h4-addon-image-accordion">' . implode('', $rows) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderHotspot(array $atts = []): string
    {
        $atts = shortcode_atts(
            [
                'image' => '',
                'points' => '50,50,Hotspot',
            ],
            $atts,
            'h4_ea_hotspot'
        );
        $image = esc_url((string) $atts['image']);
        if ($image === '') {
            return $this->renderEmptyState(
                __('Hotspot', 'help4-builder-suite'),
                __('Set the base image URL first, then add point coordinates.', 'help4-builder-suite'),
                '[h4_ea_hotspot image="https://example.com/diagram.jpg" points="24,35,Primary node|61,48,Secondary node"]'
            );
        }
        $points = preg_split('/\s*\|\s*/', (string) $atts['points']) ?: [];
        $dots = [];
        foreach ($points as $point) {
            $parts = array_map('trim', explode(',', (string) $point, 3));
            if (count($parts) < 3) {
                continue;
            }
            $x = max(0, min(100, (float) $parts[0]));
            $y = max(0, min(100, (float) $parts[1]));
            $label = esc_html((string) $parts[2]);
            $dots[] = '<button type="button" class="h4-addon-hotspot-dot" style="left:' . esc_attr((string) $x) . '%;top:' . esc_attr((string) $y) . '%;" title="' . $label . '"></button>';
        }

        return '<div class="h4-addon-hotspot"><img loading="lazy" src="' . $image . '" alt=""><div class="h4-addon-hotspot-layer">' . implode('', $dots) . '</div></div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderContentTicker(array $atts = []): string
    {
        $items = array_values(array_filter(array_map('trim', explode('|', (string) ($atts['items'] ?? '')))));
        if (! $items) {
            return $this->renderEmptyState(
                __('Content Ticker', 'help4-builder-suite'),
                __('Add ticker items separated by the pipe character.', 'help4-builder-suite'),
                '[h4_ea_content_ticker items="Managed migrations included|Security hardening by default|Checkout routed through your configured endpoint"]'
            );
        }
        $out = [];
        foreach ($items as $item) {
            $out[] = '<li>' . esc_html($item) . '</li>';
        }
        return '<div class="h4-addon-ticker"><ul>' . implode('', $out) . '</ul></div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderStepFlow(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Plan::Choose the page job|Build::Drop in a starter|Launch::Check mobile and SEO'));
        if (! $items) {
            return $this->renderEmptyState(
                __('Step Flow', 'help4-builder-suite'),
                __('Add steps using title::description format separated by pipes.', 'help4-builder-suite'),
                '[h4_ea_step_flow items="Pick::Choose starter|Edit::Replace content|Publish::Check mobile"]'
            );
        }

        $out = [];
        foreach ($items as $index => $item) {
            $out[] = '<li><span>' . esc_html((string) ($index + 1)) . '</span><strong>' . esc_html($item['label']) . '</strong><p>' . esc_html($item['value']) . '</p></li>';
        }

        return '<ol class="h4-addon-step-flow">' . implode('', $out) . '</ol>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderStatsStrip(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? '135::Starter specs|230::Default icons|Live::Ready to edit'));
        if (! $items) {
            return $this->renderEmptyState(
                __('Stats Strip', 'help4-builder-suite'),
                __('Add stats using number::label format separated by pipes.', 'help4-builder-suite'),
                '[h4_ea_stats_strip items="135::Starter specs|230::Default icons"]'
            );
        }

        $out = [];
        foreach ($items as $item) {
            $out[] = '<div class="h4-addon-stat"><strong>' . esc_html($item['label']) . '</strong><span>' . esc_html($item['value']) . '</span></div>';
        }

        return '<div class="h4-addon-stats-strip">' . implode('', $out) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderComparisonCards(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Option A::What it gives the customer|Option B::Where it fits best'));
        if (! $items) {
            return $this->renderEmptyState(
                __('Comparison Cards', 'help4-builder-suite'),
                __('Add cards using title::body format separated by pipes.', 'help4-builder-suite'),
                '[h4_ea_comparison_cards items="Starter::Fast launch|Managed::Done-for-you support"]'
            );
        }

        $out = [];
        foreach ($items as $item) {
            $out[] = '<article class="h4-addon-compare-card"><h3>' . esc_html($item['label']) . '</h3><p>' . esc_html($item['value']) . '</p></article>';
        }

        return '<div class="h4-addon-comparison-cards">' . implode('', $out) . '</div>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderTemplatePartNav(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Headers::Global navigation starters|Footers::Trust and legal blocks|404s::Rescue pages|Canvas::Focused landing pages'));
        if (! $items) {
            return $this->renderEmptyState(
                __('Template Part Nav', 'help4-builder-suite'),
                __('Add template groups using label::description format separated by pipes.', 'help4-builder-suite'),
                '[h4_ea_template_part_nav items="Headers::Navigation|Footers::Support links"]'
            );
        }

        $out = [];
        foreach ($items as $item) {
            $out[] = '<a class="h4-addon-template-part" href="#' . esc_attr(sanitize_title($item['label'])) . '"><strong>' . esc_html($item['label']) . '</strong><span>' . esc_html($item['value']) . '</span></a>';
        }

        return '<nav class="h4-addon-template-part-nav" aria-label="' . esc_attr__('Template part groups', 'help4-builder-suite') . '">' . implode('', $out) . '</nav>';
    }

    /**
     * @param array<string, mixed> $atts
     */
    public function renderScreenshotCards(array $atts = []): string
    {
        $items = $this->parsePairs((string) ($atts['items'] ?? 'Homepage::https://picsum.photos/seed/h4shot1/640/420|Mobile view::https://picsum.photos/seed/h4shot2/640/420'));
        if (! $items) {
            return $this->renderEmptyState(
                __('Screenshot Cards', 'help4-builder-suite'),
                __('Add screenshots using title::image-url format separated by pipes.', 'help4-builder-suite'),
                '[h4_ea_screenshot_cards items="Homepage::https://example.com/home.png|Checkout::https://example.com/checkout.png"]'
            );
        }

        $out = [];
        foreach ($items as $item) {
            $src = esc_url((string) $item['value']);
            if ($src === '') {
                continue;
            }
            $out[] = '<figure class="h4-addon-screenshot-card"><img loading="lazy" decoding="async" src="' . $src . '" alt=""><figcaption>' . esc_html($item['label']) . '</figcaption></figure>';
        }
        if (! $out) {
            return $this->renderEmptyState(
                __('Screenshot Cards', 'help4-builder-suite'),
                __('The screenshot list exists but contains no valid image URLs.', 'help4-builder-suite')
            );
        }

        return '<div class="h4-addon-screenshot-cards">' . implode('', $out) . '</div>';
    }

    /**
     * Render the first-party professional widget set from one bounded dispatcher.
     *
     * @param array<string, mixed> $atts
     */
    public function renderProfessionalWidget(array $atts = [], string $content = '', string $tag = ''): string
    {
        $tag = sanitize_key($tag);
        $uid = 'h4pro-' . wp_rand(10000, 99999);

        switch ($tag) {
            case 'h4_pro_dual_heading':
                $atts = shortcode_atts(['first' => 'Build faster.', 'second' => 'Own the result.', 'tag' => 'h2', 'first_color' => '#111827', 'second_color' => '#1565c0'], $atts, $tag);
                $headingTag = in_array((string) $atts['tag'], ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], true) ? (string) $atts['tag'] : 'h2';
                $firstColor = sanitize_hex_color((string) $atts['first_color']) ?: '#111827';
                $secondColor = sanitize_hex_color((string) $atts['second_color']) ?: '#1565c0';
                return '<' . $headingTag . ' class="h4-pro-dual-heading"><span style="color:' . esc_attr($firstColor) . '">' . esc_html((string) $atts['first']) . '</span> <strong style="color:' . esc_attr($secondColor) . '">' . esc_html((string) $atts['second']) . '</strong></' . $headingTag . '>';

            case 'h4_pro_creative_button':
                $atts = shortcode_atts(['label' => 'Get Started', 'url' => '#', 'style' => 'fill', 'icon' => 'arrow', 'new_tab' => '0'], $atts, $tag);
                $style = $this->enumToken((string) $atts['style'], ['fill', 'outline', 'ghost', 'underline'], 'fill');
                $icon = $this->enumToken((string) $atts['icon'], ['none', 'arrow', 'download', 'external', 'play'], 'arrow');
                $icons = ['arrow' => '&rarr;', 'download' => '&#8595;', 'external' => '&#8599;', 'play' => '&#9654;'];
                $target = rest_sanitize_boolean($atts['new_tab']) ? ' target="_blank" rel="noopener noreferrer"' : '';
                $iconHtml = $icon !== 'none' ? '<span aria-hidden="true">' . ($icons[$icon] ?? '') . '</span>' : '';
                return '<a class="h4-pro-button is-' . esc_attr($style) . '" href="' . esc_url((string) $atts['url']) . '"' . $target . '><span>' . esc_html((string) $atts['label']) . '</span>' . $iconHtml . '</a>';

            case 'h4_pro_feature_list':
                return $this->renderFeatureList((string) ($atts['items'] ?? 'Fast launch::Start from a proven layout|Live editing::See changes before saving'));

            case 'h4_pro_icon_list':
                $items = $this->parsePairs((string) ($atts['items'] ?? 'Documentation::#docs|Support::#support'));
                $rows = [];
                foreach ($items as $item) {
                    $rows[] = '<li><span aria-hidden="true">&#10003;</span><a href="' . esc_url($item['value']) . '">' . esc_html($item['label']) . '</a></li>';
                }
                return $rows ? '<ul class="h4-pro-icon-list">' . implode('', $rows) . '</ul>' : $this->renderEmptyState(__('Icon List', 'help4-builder-suite'), __('Add label::URL items separated by pipes.', 'help4-builder-suite'));

            case 'h4_pro_image_box':
                $atts = shortcode_atts(['title' => 'Image Box', 'text' => '', 'image' => '', 'url' => '#', 'alt' => ''], $atts, $tag);
                $image = esc_url((string) $atts['image']);
                $media = $image !== '' ? '<img loading="lazy" decoding="async" src="' . $image . '" alt="' . esc_attr((string) $atts['alt']) . '">' : '<span class="h4-pro-media-placeholder" aria-hidden="true"></span>';
                return '<article class="h4-pro-image-box">' . $media . '<div><h3>' . esc_html((string) $atts['title']) . '</h3><p>' . esc_html((string) $atts['text']) . '</p><a href="' . esc_url((string) $atts['url']) . '">' . esc_html__('Learn more', 'help4-builder-suite') . '</a></div></article>';

            case 'h4_pro_team_grid':
                return $this->renderPeopleGrid((string) ($atts['items'] ?? ''), false);

            case 'h4_pro_testimonial_grid':
                return $this->renderPeopleGrid((string) ($atts['items'] ?? ''), true);

            case 'h4_pro_business_hours':
                $items = $this->parsePairs((string) ($atts['items'] ?? 'Monday-Friday::9:00 AM-5:00 PM|Saturday::By appointment|Sunday::Closed'));
                $rows = [];
                foreach ($items as $item) {
                    $rows[] = '<div><dt>' . esc_html($item['label']) . '</dt><dd>' . esc_html($item['value']) . '</dd></div>';
                }
                return '<dl class="h4-pro-business-hours">' . implode('', $rows) . '</dl>';

            case 'h4_pro_price_menu':
                $records = $this->parseRecords((string) ($atts['items'] ?? ''), 3);
                $rows = [];
                foreach ($records as $record) {
                    $rows[] = '<article><div><h3>' . esc_html($record[0]) . '</h3><p>' . esc_html($record[1]) . '</p></div><strong>' . esc_html($record[2]) . '</strong></article>';
                }
                return $rows ? '<div class="h4-pro-price-menu">' . implode('', $rows) . '</div>' : $this->renderEmptyState(__('Price Menu', 'help4-builder-suite'), __('Add name::description::price items separated by pipes.', 'help4-builder-suite'));

            case 'h4_pro_timeline':
                $records = $this->parseRecords((string) ($atts['items'] ?? ''), 3);
                $rows = [];
                foreach ($records as $record) {
                    $rows[] = '<li><span class="h4-pro-timeline-dot" aria-hidden="true"></span><time>' . esc_html($record[2]) . '</time><h3>' . esc_html($record[0]) . '</h3><p>' . esc_html($record[1]) . '</p></li>';
                }
                return $rows ? '<ol class="h4-pro-timeline">' . implode('', $rows) . '</ol>' : $this->renderEmptyState(__('Timeline', 'help4-builder-suite'), __('Add title::description::date items separated by pipes.', 'help4-builder-suite'));

            case 'h4_pro_modal':
            case 'h4_pro_offcanvas':
                $atts = shortcode_atts(['button' => 'Open', 'title' => 'More information', 'content' => $content], $atts, $tag);
                $mode = $tag === 'h4_pro_modal' ? 'modal' : 'offcanvas';
                $body = (string) $atts['content'] !== '' ? (string) $atts['content'] : $content;
                return '<div class="h4-pro-overlay-widget" data-h4-pro-overlay-widget="' . esc_attr($mode) . '"><button type="button" class="h4-pro-button is-fill" data-h4-pro-overlay-open aria-controls="' . esc_attr($uid) . '" aria-expanded="false">' . esc_html((string) $atts['button']) . '</button><div id="' . esc_attr($uid) . '" class="h4-pro-overlay is-' . esc_attr($mode) . '" data-h4-pro-overlay hidden><div class="h4-pro-overlay-backdrop" data-h4-pro-overlay-close></div><section class="h4-pro-overlay-panel" role="dialog" aria-modal="true" aria-labelledby="' . esc_attr($uid) . '-title" tabindex="-1"><button type="button" class="h4-pro-overlay-close" data-h4-pro-overlay-close aria-label="' . esc_attr__('Close', 'help4-builder-suite') . '">&times;</button><h2 id="' . esc_attr($uid) . '-title">' . esc_html((string) $atts['title']) . '</h2><div class="h4-pro-overlay-content">' . wp_kses_post($body) . '</div></section></div></div>';

            case 'h4_pro_content_toggle':
                $atts = shortcode_atts(['first_label' => 'Monthly', 'first' => '', 'second_label' => 'Annual', 'second' => ''], $atts, $tag);
                return '<section class="h4-pro-content-toggle" data-h4-pro-toggle><div class="h4-pro-toggle-tabs" role="tablist"><button type="button" role="tab" aria-selected="true" data-h4-pro-toggle-tab="0">' . esc_html((string) $atts['first_label']) . '</button><button type="button" role="tab" aria-selected="false" data-h4-pro-toggle-tab="1">' . esc_html((string) $atts['second_label']) . '</button></div><div role="tabpanel" data-h4-pro-toggle-panel="0">' . wp_kses_post((string) $atts['first']) . '</div><div role="tabpanel" data-h4-pro-toggle-panel="1" hidden>' . wp_kses_post((string) $atts['second']) . '</div></section>';

            case 'h4_pro_tooltip':
                $atts = shortcode_atts(['label' => 'Hover or focus', 'tip' => 'Helpful context.', 'position' => 'top'], $atts, $tag);
                $position = $this->enumToken((string) $atts['position'], ['top', 'right', 'bottom', 'left'], 'top');
                return '<span class="h4-pro-tooltip is-' . esc_attr($position) . '" tabindex="0" aria-describedby="' . esc_attr($uid) . '">' . esc_html((string) $atts['label']) . '<span id="' . esc_attr($uid) . '" role="tooltip">' . esc_html((string) $atts['tip']) . '</span></span>';

            case 'h4_pro_protected_content':
                $atts = shortcode_atts(['access' => 'logged_in', 'content' => $content, 'fallback' => 'Sign in to continue.'], $atts, $tag);
                $access = $this->enumToken((string) $atts['access'], ['public', 'logged_in', 'edit_posts', 'manage_options'], 'logged_in');
                $allowed = $access === 'public' || ($access === 'logged_in' && is_user_logged_in()) || (in_array($access, ['edit_posts', 'manage_options'], true) && current_user_can($access));
                return '<div class="h4-pro-protected-content ' . ($allowed ? 'is-unlocked' : 'is-locked') . '">' . ($allowed ? wp_kses_post((string) $atts['content']) : '<p>' . esc_html((string) $atts['fallback']) . '</p>') . '</div>';

            case 'h4_pro_image_compare':
                $atts = shortcode_atts(['before' => '', 'after' => '', 'before_label' => 'Before', 'after_label' => 'After', 'position' => '50'], $atts, $tag);
                $before = esc_url((string) $atts['before']);
                $after = esc_url((string) $atts['after']);
                if ($before === '' || $after === '') {
                    return $this->renderEmptyState(__('Image Comparison', 'help4-builder-suite'), __('Choose both before and after images.', 'help4-builder-suite'));
                }
                $position = max(0, min(100, absint($atts['position'])));
                return '<figure class="h4-pro-image-compare" data-h4-pro-image-compare style="--h4-pro-compare:' . esc_attr((string) $position) . '%"><img class="is-after" src="' . $after . '" alt="' . esc_attr((string) $atts['after_label']) . '"><div class="h4-pro-image-compare-before"><img src="' . $before . '" alt="' . esc_attr((string) $atts['before_label']) . '"></div><span class="is-before-label">' . esc_html((string) $atts['before_label']) . '</span><span class="is-after-label">' . esc_html((string) $atts['after_label']) . '</span><input type="range" min="0" max="100" value="' . esc_attr((string) $position) . '" aria-label="' . esc_attr__('Image comparison position', 'help4-builder-suite') . '"></figure>';

            case 'h4_pro_lightbox_gallery':
                $items = $this->parsePairs((string) ($atts['items'] ?? ''));
                $images = [];
                foreach ($items as $item) {
                    $src = esc_url($item['value']);
                    if ($src !== '') {
                        $images[] = '<a href="' . $src . '" data-h4-pro-lightbox-item><img loading="lazy" decoding="async" src="' . $src . '" alt="' . esc_attr($item['label']) . '"><span>' . esc_html($item['label']) . '</span></a>';
                    }
                }
                return $images ? '<div class="h4-pro-lightbox-gallery" data-h4-pro-lightbox>' . implode('', $images) . '</div>' : $this->renderEmptyState(__('Lightbox Gallery', 'help4-builder-suite'), __('Add caption::image URL items separated by pipes.', 'help4-builder-suite'));

            case 'h4_pro_progress':
                $atts = shortcode_atts(['label' => 'Progress', 'value' => '75', 'prefix' => '', 'suffix' => '%', 'color' => '#1565c0'], $atts, $tag);
                $value = max(0, min(100, (float) $atts['value']));
                $color = sanitize_hex_color((string) $atts['color']) ?: '#1565c0';
                return '<div class="h4-pro-progress" data-h4-pro-progress="' . esc_attr((string) $value) . '" style="--h4-pro-accent:' . esc_attr($color) . '"><div><span>' . esc_html((string) $atts['label']) . '</span><strong>' . esc_html((string) $atts['prefix'] . $value . (string) $atts['suffix']) . '</strong></div><progress max="100" value="' . esc_attr((string) $value) . '">' . esc_html((string) $value) . '%</progress></div>';

            case 'h4_pro_counter':
                $atts = shortcode_atts(['label' => '', 'value' => '250', 'prefix' => '', 'suffix' => '+', 'color' => '#1565c0'], $atts, $tag);
                $value = max(-999999999, min(999999999, (float) $atts['value']));
                return '<div class="h4-pro-counter" data-h4-pro-counter="' . esc_attr((string) $value) . '" data-prefix="' . esc_attr((string) $atts['prefix']) . '" data-suffix="' . esc_attr((string) $atts['suffix']) . '" style="--h4-pro-accent:' . esc_attr(sanitize_hex_color((string) $atts['color']) ?: '#1565c0') . '"><strong>' . esc_html((string) $atts['prefix'] . '0' . (string) $atts['suffix']) . '</strong><span>' . esc_html((string) $atts['label']) . '</span></div>';

            case 'h4_pro_circle_progress':
                $atts = shortcode_atts(['label' => 'Readiness', 'value' => '86', 'prefix' => '', 'suffix' => '%', 'color' => '#1565c0'], $atts, $tag);
                $value = max(0, min(100, (float) $atts['value']));
                return '<div class="h4-pro-circle-progress" role="img" aria-label="' . esc_attr((string) $atts['label'] . ': ' . $value . '%') . '" style="--h4-pro-value:' . esc_attr((string) ($value * 3.6)) . 'deg;--h4-pro-accent:' . esc_attr(sanitize_hex_color((string) $atts['color']) ?: '#1565c0') . '"><strong>' . esc_html((string) $atts['prefix'] . $value . (string) $atts['suffix']) . '</strong><span>' . esc_html((string) $atts['label']) . '</span></div>';

            case 'h4_pro_animated_heading':
                $atts = shortcode_atts(['prefix' => 'Build', 'words' => 'faster|smarter|without lock-in', 'suffix' => 'with Help4', 'speed' => '2400'], $atts, $tag);
                $words = array_values(array_filter(array_map('trim', explode('|', (string) $atts['words']))));
                $speed = max(800, min(10000, absint($atts['speed'])));
                return '<h2 class="h4-pro-animated-heading" data-h4-pro-animated-heading data-words="' . esc_attr(wp_json_encode($words)) . '" data-speed="' . esc_attr((string) $speed) . '"><span>' . esc_html((string) $atts['prefix']) . '</span> <strong>' . esc_html((string) ($words[0] ?? 'faster')) . '</strong> <span>' . esc_html((string) $atts['suffix']) . '</span></h2>';

            case 'h4_pro_table_of_contents':
                $atts = shortcode_atts(['title' => 'On this page', 'selector' => 'h2,h3'], $atts, $tag);
                $allowedSelectors = array_values(array_intersect(array_map('trim', explode(',', (string) $atts['selector'])), ['h2', 'h3', 'h4', 'h5', 'h6']));
                $selector = $allowedSelectors ? implode(',', $allowedSelectors) : 'h2,h3';
                return '<nav class="h4-pro-toc" aria-label="' . esc_attr((string) $atts['title']) . '" data-h4-pro-toc data-selector="' . esc_attr($selector) . '"><strong>' . esc_html((string) $atts['title']) . '</strong><ol></ol></nav>';

            case 'h4_pro_reading_progress':
                $atts = shortcode_atts(['position' => 'top', 'color' => '#1565c0'], $atts, $tag);
                $position = $this->enumToken((string) $atts['position'], ['top', 'bottom', 'inline'], 'top');
                return '<div class="h4-pro-reading-progress is-' . esc_attr($position) . '" data-h4-pro-reading-progress style="--h4-pro-accent:' . esc_attr(sanitize_hex_color((string) $atts['color']) ?: '#1565c0') . '"><span></span></div>';

            case 'h4_pro_scroll_top':
                $atts = shortcode_atts(['label' => __('Scroll to top', 'help4-builder-suite'), 'position' => 'right', 'offset' => '18', 'show_after' => '480', 'color' => '#1565c0'], $atts, $tag);
                $position = $this->enumToken((string) $atts['position'], ['left', 'right'], 'right');
                $offset = max(0, min(240, absint($atts['offset'])));
                $showAfter = max(0, min(10000, absint($atts['show_after'])));
                $color = sanitize_hex_color((string) $atts['color']) ?: '#1565c0';
                return '<button type="button" class="h4-pro-scroll-top is-' . esc_attr($position) . '" data-h4-pro-scroll-top data-show-after="' . esc_attr((string) $showAfter) . '" style="--h4-pro-scroll-offset:' . esc_attr((string) $offset) . 'px;--h4-pro-accent:' . esc_attr($color) . '" aria-label="' . esc_attr((string) $atts['label']) . '">&#8593;</button>';

            case 'h4_pro_alert':
                $atts = shortcode_atts(['type' => 'info', 'title' => 'Good to know', 'text' => '', 'dismissible' => '1'], $atts, $tag);
                $type = $this->enumToken((string) $atts['type'], ['info', 'success', 'warning', 'danger'], 'info');
                $close = rest_sanitize_boolean($atts['dismissible']) ? '<button type="button" data-h4-pro-alert-close aria-label="' . esc_attr__('Dismiss', 'help4-builder-suite') . '">&times;</button>' : '';
                return '<aside class="h4-pro-alert is-' . esc_attr($type) . '" role="status">' . $close . '<strong>' . esc_html((string) $atts['title']) . '</strong><p>' . esc_html((string) $atts['text']) . '</p></aside>';

            case 'h4_pro_social_icons':
                $items = $this->parsePairs((string) ($atts['items'] ?? ''));
                $links = [];
                foreach ($items as $item) {
                    $url = esc_url($item['value']);
                    if ($url !== '') {
                        $initials = function_exists('mb_substr') ? mb_substr($item['label'], 0, 2) : substr($item['label'], 0, 2);
                        $links[] = '<a href="' . $url . '" target="_blank" rel="noopener noreferrer" aria-label="' . esc_attr($item['label']) . '">' . esc_html($initials) . '</a>';
                    }
                }
                return '<div class="h4-pro-social-icons">' . implode('', $links) . '</div>';

            case 'h4_pro_share_buttons':
                $atts = shortcode_atts(['networks' => 'facebook,linkedin,x,email', 'label' => 'Share this page'], $atts, $tag);
                return $this->renderShareButtons((string) $atts['networks'], (string) $atts['label']);

            case 'h4_pro_sticky_video':
                $atts = shortcode_atts(['url' => '', 'title' => 'Featured video', 'poster' => '', 'sticky' => '1'], $atts, $tag);
                $url = esc_url((string) $atts['url']);
                if ($url === '') {
                    return $this->renderEmptyState(__('Sticky Video', 'help4-builder-suite'), __('Add a YouTube, Vimeo, or direct video URL.', 'help4-builder-suite'));
                }
                $embed = wp_oembed_get($url, ['width' => 1280]);
                if (! is_string($embed) || $embed === '') {
                    $embed = '<video controls playsinline preload="metadata" src="' . $url . '"' . ((string) $atts['poster'] !== '' ? ' poster="' . esc_url((string) $atts['poster']) . '"' : '') . '></video>';
                }
                return '<figure class="h4-pro-sticky-video' . (rest_sanitize_boolean($atts['sticky']) ? ' is-sticky-enabled' : '') . '" data-h4-pro-sticky-video><div class="h4-pro-video-frame">' . wp_kses($embed, $this->allowedEmbedHtml()) . '</div><figcaption>' . esc_html((string) $atts['title']) . '</figcaption></figure>';
        }

        return $this->renderEmptyState(__('Help4 Pro Widget', 'help4-builder-suite'), __('This widget type is not registered.', 'help4-builder-suite'));
    }

    private function renderFeatureList(string $raw): string
    {
        $items = $this->parsePairs($raw);
        $rows = [];
        foreach ($items as $item) {
            $rows[] = '<li><span aria-hidden="true">&#10003;</span><div><strong>' . esc_html($item['label']) . '</strong><p>' . esc_html($item['value']) . '</p></div></li>';
        }
        return $rows ? '<ul class="h4-pro-feature-list">' . implode('', $rows) . '</ul>' : $this->renderEmptyState(__('Feature List', 'help4-builder-suite'), __('Add title::description items separated by pipes.', 'help4-builder-suite'));
    }

    private function renderPeopleGrid(string $raw, bool $testimonials): string
    {
        $records = $this->parseRecords($raw, 3);
        $rows = [];
        foreach ($records as $record) {
            if ($testimonials) {
                $rows[] = '<figure class="h4-pro-testimonial"><blockquote>' . esc_html($record[1]) . '</blockquote><figcaption><strong>' . esc_html($record[0]) . '</strong><span>' . esc_html($record[2]) . '</span></figcaption></figure>';
                continue;
            }
            $image = esc_url($record[2]);
            $initial = function_exists('mb_substr') ? mb_substr($record[0], 0, 1) : substr($record[0], 0, 1);
            $media = $image !== '' ? '<img loading="lazy" decoding="async" src="' . $image . '" alt="' . esc_attr($record[0]) . '">' : '<span class="h4-pro-avatar" aria-hidden="true">' . esc_html($initial) . '</span>';
            $rows[] = '<article class="h4-pro-team-member">' . $media . '<h3>' . esc_html($record[0]) . '</h3><p>' . esc_html($record[1]) . '</p></article>';
        }
        $title = $testimonials ? __('Testimonial Grid', 'help4-builder-suite') : __('Team Grid', 'help4-builder-suite');
        return $rows ? '<div class="h4-pro-card-grid">' . implode('', $rows) . '</div>' : $this->renderEmptyState($title, __('Add three-part records separated by pipes.', 'help4-builder-suite'));
    }

    /**
     * @return array<int, array<int, string>>
     */
    private function parseRecords(string $raw, int $fields): array
    {
        $items = preg_split('/\s*\|\s*/', trim($raw)) ?: [];
        $records = [];
        foreach ($items as $item) {
            $parts = array_map('trim', explode('::', (string) $item, $fields));
            if (count($parts) !== $fields || $parts[0] === '') {
                continue;
            }
            $records[] = $parts;
        }
        return $records;
    }

    private function enumToken(string $value, array $allowed, string $default): string
    {
        $value = sanitize_key($value);
        return in_array($value, $allowed, true) ? $value : $default;
    }

    private function renderShareButtons(string $rawNetworks, string $label): string
    {
        $allowed = ['facebook', 'linkedin', 'x', 'email'];
        $networks = array_values(array_intersect(array_map('sanitize_key', explode(',', $rawNetworks)), $allowed));
        $url = rawurlencode((string) get_permalink());
        $title = rawurlencode((string) get_the_title());
        $targets = [
            'facebook' => 'https://www.facebook.com/sharer/sharer.php?u=' . $url,
            'linkedin' => 'https://www.linkedin.com/sharing/share-offsite/?url=' . $url,
            'x' => 'https://x.com/intent/post?url=' . $url . '&text=' . $title,
            'email' => 'mailto:?subject=' . $title . '&body=' . $url,
        ];
        $links = [];
        foreach ($networks as $network) {
            $external = $network !== 'email' ? ' target="_blank" rel="noopener noreferrer"' : '';
            $links[] = '<a class="is-' . esc_attr($network) . '" href="' . esc_url($targets[$network]) . '"' . $external . '>' . esc_html(ucfirst($network)) . '</a>';
        }
        return '<nav class="h4-pro-share-buttons" aria-label="' . esc_attr($label) . '">' . implode('', $links) . '</nav>';
    }

    /**
     * @return array<string, array<string, bool>>
     */
    private function allowedEmbedHtml(): array
    {
        return [
            'iframe' => ['src' => true, 'title' => true, 'width' => true, 'height' => true, 'frameborder' => true, 'allow' => true, 'allowfullscreen' => true, 'loading' => true],
            'video' => ['src' => true, 'poster' => true, 'controls' => true, 'playsinline' => true, 'preload' => true],
            'source' => ['src' => true, 'type' => true],
        ];
    }

    private function renderEmptyState(string $title, string $message, string $shortcode = ''): string
    {
        $out = '<div class="h4-addon-empty-state" style="padding:14px 16px;border:1px dashed #9db2c9;border-radius:10px;background:#f7fbff;color:#24425f;">';
        $out .= '<strong style="display:block;margin:0 0 6px;">' . esc_html($title) . '</strong>';
        $out .= '<p style="margin:0;">' . esc_html($message) . '</p>';
        if ($shortcode !== '') {
            $out .= '<code style="display:block;margin-top:8px;padding:6px 8px;border-radius:6px;background:#e8f1f9;color:#0e2f4c;">' . esc_html($shortcode) . '</code>';
        }
        $out .= '</div>';
        return $out;
    }

    /**
     * @param string $raw
     * @return array<int, array{label: string, value: string}>
     */
    private function parsePairs(string $raw): array
    {
        $parts = preg_split('/\s*\|\s*/', trim($raw)) ?: [];
        $items = [];
        foreach ($parts as $part) {
            $pair = explode('::', (string) $part, 2);
            if (count($pair) < 2) {
                continue;
            }
            $label = trim((string) $pair[0]);
            $value = trim((string) $pair[1]);
            if ($label === '' || $value === '') {
                continue;
            }
            $items[] = [
                'label' => $label,
                'value' => $value,
            ];
        }
        return $items;
    }
}
