<?php
namespace DiviBooster\DiviBooster\Modules\Tabs\TabIcons;

const DIVI5_TABS_SLUG      = 'divi/tabs';
const DIVI5_TAB_SLUG       = 'divi/tab';
const DIVI5_ICON_ATTR      = 'diviboosterTabIcon';
const DIVI5_ICON_FONT_ATTR = 'diviboosterTabIconFont';
const DIVI5_ACTIVE_ICON_FONT_ATTR = 'diviboosterActiveTabIconFont';
const DIVI5_ICON_SPACING_ATTR = 'diviboosterTabIconSpacing';
const DIVI5_ACTIVE_ICON_SPACING_ATTR = 'diviboosterActiveTabIconSpacing';
const DIVI5_ICON_DESIGN_GROUP_SLUG = 'designTabIcon';
const DIVI5_ACTIVE_ICON_DESIGN_GROUP_SLUG = 'designActiveTabIcon';
const DIVI5_BUILDER_HOOKS_PACKAGE_HANDLE = 'divibooster-tab-icons-builder-hooks';

// Builder content group for the per-tab icon picker.
const DIVI5_ICON_GROUP_SLUG = 'diviboosterTabIconGroup';

const DIVI4_TABS_SLUG = 'et_pb_tabs';
const DIVI4_TAB_SLUG = 'et_pb_tab';
const DIVI4_ICON_FIELD = 'divibooster_tab_icon';
const DIVI4_PARENT_ICON_FONT_PREFIX = 'divibooster_tab_icon';
const DIVI4_PARENT_ACTIVE_ICON_FONT_PREFIX = 'divibooster_active_tab_icon';
const DIVI4_CHILD_ICON_FONT_PREFIX = 'divibooster_tab_item_icon';
const DIVI4_PARENT_ICON_SPACING_PREFIX = 'divibooster_tab_icon_spacing';
const DIVI4_PARENT_ACTIVE_ICON_SPACING_PREFIX = 'divibooster_active_tab_icon_spacing';
const DIVI4_CHILD_ICON_SPACING_PREFIX = 'divibooster_tab_item_icon_spacing';
const DIVI4_ICON_CONTENT_TOGGLE = 'divibooster_tab_icon';
const DIVI4_ICON_DESIGN_TOGGLE = 'divibooster_tab_icon_design';
const DIVI4_ACTIVE_ICON_DESIGN_TOGGLE = 'divibooster_active_tab_icon_design';

// === Divi 4: UI, Render, Conversion ===
add_filter('et_builder_get_parent_modules', __NAMESPACE__ . '\register_divi4_parent_module_ui', 20, 2);
add_filter('et_builder_get_child_modules', __NAMESPACE__ . '\register_divi4_child_module_ui', 20, 2);
add_filter('et_pb_all_fields_unprocessed_' . DIVI4_TABS_SLUG, __NAMESPACE__ . '\add_divi4_tabs_fields');
add_filter('et_pb_all_fields_unprocessed_' . DIVI4_TAB_SLUG, __NAMESPACE__ . '\add_divi4_tab_fields');
add_filter('et_module_shortcode_output', __NAMESPACE__ . '\render_divi4_tabs_with_icons', 20, 3);
add_filter('divi.moduleLibrary.conversion.moduleConversionOutline', __NAMESPACE__ . '\add_divi4_to_divi5_conversion_outline', 20, 2);

// === Divi 5: Builder UI and Assets ===
add_action('wp_enqueue_scripts', __NAMESPACE__ . '\enqueue_divi5_builder_script', 999);
add_action('divi_visual_builder_assets_before_enqueue_scripts', __NAMESPACE__ . '\register_divi5_builder_dependency_script', 0);
add_filter('divi_visual_builder_package_build_params_divi-module-library', __NAMESPACE__ . '\add_divi5_builder_package_dependency');
add_filter('divi_frontend_assets_dynamic_assets_global_assets_list', __NAMESPACE__ . '\ensure_divi5_fa_icon_assets', 20, 3);
add_filter('divi_frontend_assets_dynamic_assets_late_global_assets_list', __NAMESPACE__ . '\ensure_divi5_fa_icon_assets', 20, 3);

function enqueue_divi5_builder_script() {
    static $enqueued = false;

    if ($enqueued) {
        return;
    }

    if (!is_divi5_builder_preview()) {
        return;
    }

    // Wait for the hooks package used by the inline builder extension.
    if (!function_exists('wp_script_is') || !wp_script_is('divi-vendor-wp-hooks', 'registered')) {
        return;
    }

    if (!function_exists('wp_register_script') || !function_exists('wp_enqueue_script') || !function_exists('wp_add_inline_script') || !function_exists('sanitize_title')) {
        return;
    }

    $inline_js = get_divi5_inline_js();
    if (!is_string($inline_js) || $inline_js === '') {
        return;
    }

    if (function_exists('wp_script_is') && wp_script_is('divi-module-library', 'enqueued') && wp_script_is(DIVI5_BUILDER_HOOKS_PACKAGE_HANDLE, 'registered')) {
        return;
    }

    $handle = sanitize_title('divi-booster-' . DIVI5_TABS_SLUG . '-' . DIVI5_ICON_ATTR);
    wp_register_script($handle, '', ['lodash', 'divi-vendor-wp-hooks'], null, true);
    wp_enqueue_script($handle);
    wp_add_inline_script($handle, $inline_js);

    // Load icon font CSS for builder previews.
    maybe_enqueue_divi5_builder_fa_css();

    $enqueued = true;
}

function register_divi5_builder_dependency_script() {
    static $inline_added = false;

    if (!function_exists('wp_register_script') || !function_exists('wp_script_is')) {
        return;
    }

    if (!wp_script_is(DIVI5_BUILDER_HOOKS_PACKAGE_HANDLE, 'registered')) {
        wp_register_script(DIVI5_BUILDER_HOOKS_PACKAGE_HANDLE, '', ['lodash', 'divi-vendor-wp-hooks'], null, true);
    }

    if ($inline_added || !is_divi5_builder_preview() || !function_exists('wp_add_inline_script')) {
        return;
    }

    $inline_js = get_divi5_inline_js();
    if (!is_string($inline_js) || $inline_js === '') {
        return;
    }

    wp_add_inline_script(DIVI5_BUILDER_HOOKS_PACKAGE_HANDLE, $inline_js, 'after');
    maybe_enqueue_divi5_builder_fa_css();

    $inline_added = true;
}

function add_divi5_builder_package_dependency($params) {
    if (!is_array($params)) {
        return $params;
    }

    if (!isset($params['script']) || !is_array($params['script'])) {
        $params['script'] = [];
    }

    $deps = isset($params['script']['deps']) && is_array($params['script']['deps']) ? $params['script']['deps'] : [];
    if (!in_array(DIVI5_BUILDER_HOOKS_PACKAGE_HANDLE, $deps, true)) {
        array_unshift($deps, DIVI5_BUILDER_HOOKS_PACKAGE_HANDLE);
    }
    $params['script']['deps'] = $deps;

    return $params;
}

function ensure_divi5_fa_icon_assets($global_asset_list, $assets_args = [], $dynamic_assets_instance = null) {
    if (!is_array($global_asset_list)) {
        return $global_asset_list;
    }

    if (!is_array($assets_args)) {
        $assets_args = [];
    }

    $assets_prefix = $assets_args['assets_prefix'] ?? '';
    if (!is_string($assets_prefix) || $assets_prefix === '') {
        if (class_exists('\ET\Builder\FrontEnd\Assets\DynamicAssetsUtils') && method_exists('\ET\Builder\FrontEnd\Assets\DynamicAssetsUtils', 'get_dynamic_assets_path')) {
            try {
                $assets_prefix = \ET\Builder\FrontEnd\Assets\DynamicAssetsUtils::get_dynamic_assets_path();
            } catch (\Throwable $e) {
                $assets_prefix = '';
            }
        } elseif (function_exists('get_template_directory')) {
            $assets_prefix = get_template_directory() . '/includes/builder/feature/dynamic-assets/assets';
        } else {
            return $global_asset_list;
        }
    }

    if (!is_string($assets_prefix) || $assets_prefix === '') {
        return $global_asset_list;
    }

    $global_asset_list['et_icons_fa'] = [
        'css' => "{$assets_prefix}/css/icons_fa_all.css",
    ];

    return $global_asset_list;
}

function maybe_enqueue_divi5_builder_fa_css() {
    enqueue_divi_fa_css('divibooster-tab-icons-builder-fa');
}

function is_divi5_builder_preview() {
    return is_divi5_enabled() && is_divi_visual_builder_enabled();
}

function is_divi5_enabled() {
    if (!function_exists('et_builder_d5_enabled')) {
        return false;
    }

    try {
        return (bool) \et_builder_d5_enabled();
    } catch (\Throwable $e) {
        return false;
    }
}

function is_divi_visual_builder_enabled() {
    if (!function_exists('et_core_is_fb_enabled')) {
        return false;
    }

    try {
        return (bool) \et_core_is_fb_enabled();
    } catch (\Throwable $e) {
        return false;
    }
}

function enqueue_divi_fa_css($handle) {
    static $loaded = false;
    if ($loaded) {
        return;
    }

    $processed_css = get_divi_fa_css();
    if ($processed_css === '') {
        return;
    }

    if (!function_exists('wp_register_style') || !function_exists('wp_enqueue_style') || !function_exists('wp_add_inline_style')) {
        return;
    }

    wp_register_style($handle, false, [], null);
    wp_enqueue_style($handle);
    wp_add_inline_style($handle, $processed_css);

    $loaded = true;
}

function get_divi_fa_css() {
    static $processed_css = null;
    if ($processed_css !== null) {
        return $processed_css;
    }

    $processed_css = '';
    if (!function_exists('trailingslashit') || !function_exists('get_template_directory') || !function_exists('get_template_directory_uri')) {
        return '';
    }

    $fa_css_file = trailingslashit(get_template_directory()) . 'includes/builder/feature/dynamic-assets/assets/css/icons_fa_all.css';
    if (!file_exists($fa_css_file) || !is_readable($fa_css_file)) {
        return '';
    }

    $css_content = @file_get_contents($fa_css_file); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
    if (!is_string($css_content) || $css_content === '') {
        return '';
    }

    $product_dir = get_template_directory_uri();
    $no_protocol_path = str_replace(['http:', 'https:'], '', $product_dir);
    $processed_css = str_ireplace('#dynamic-product-dir', $no_protocol_path, $css_content);
    if (!is_string($processed_css) || trim($processed_css) === '') {
        $processed_css = '';
        return '';
    }

    return $processed_css;
}

function get_divi4_fa_css_tag(array $icons) {
    static $printed = false;
    if ($printed || !icons_include_fontawesome($icons)) {
        return '';
    }

    $css = get_divi_fa_css();
    if ($css === '') {
        return '';
    }

    $printed = true;
    return '<style id="divibooster-tab-icons-divi4-fa">' . $css . '</style>';
}

function register_divi4_parent_module_ui($modules, $post_type = '') {
    mutate_divi4_modules($modules, DIVI4_TABS_SLUG, function($module) {
        add_divi4_toggle($module, 'advanced', DIVI4_ICON_DESIGN_TOGGLE, 'Tab Icon', 51);
        add_divi4_toggle($module, 'advanced', DIVI4_ACTIVE_ICON_DESIGN_TOGGLE, 'Active Tab Icon', 52);
    });

    return $modules;
}

function register_divi4_child_module_ui($modules, $post_type = '') {
    mutate_divi4_modules($modules, DIVI4_TAB_SLUG, function($module) {
        add_divi4_toggle($module, 'general', DIVI4_ICON_CONTENT_TOGGLE, 'Tab Icon', 25);
        add_divi4_toggle($module, 'advanced', DIVI4_ICON_DESIGN_TOGGLE, 'Tab Icon', 51);
    });

    return $modules;
}

function mutate_divi4_modules(&$modules, $slug, $callback) {
    if (!is_array($modules)) {
        return;
    }

    foreach ($modules as &$module) {
        if (is_array($module)) {
            mutate_divi4_modules($module, $slug, $callback);
            continue;
        }

        if (is_object($module) && isset($module->slug) && $module->slug === $slug) {
            $callback($module);
        }
    }
    unset($module);
}

function add_divi4_toggle($module, $tab_slug, $toggle_slug, $title, $priority = 50) {
    if (!is_object($module)) {
        return;
    }

    if (!isset($module->settings_modal_toggles) || !is_array($module->settings_modal_toggles)) {
        $module->settings_modal_toggles = [];
    }
    if (!isset($module->settings_modal_toggles[$tab_slug]) || !is_array($module->settings_modal_toggles[$tab_slug])) {
        $module->settings_modal_toggles[$tab_slug] = [];
    }
    if (!isset($module->settings_modal_toggles[$tab_slug]['toggles']) || !is_array($module->settings_modal_toggles[$tab_slug]['toggles'])) {
        $module->settings_modal_toggles[$tab_slug]['toggles'] = [];
    }

    $module->settings_modal_toggles[$tab_slug]['toggles'][$toggle_slug] = [
        'title'    => esc_html__($title, 'et_builder'),
        'priority' => $priority,
    ];
}

function add_divi4_tabs_fields($fields) {
    if (!is_array($fields)) {
        return $fields;
    }

    return array_merge(
        $fields,
        get_divi4_icon_design_fields(DIVI4_PARENT_ICON_FONT_PREFIX, DIVI4_PARENT_ICON_SPACING_PREFIX, DIVI4_ICON_DESIGN_TOGGLE),
        get_divi4_icon_design_fields(DIVI4_PARENT_ACTIVE_ICON_FONT_PREFIX, DIVI4_PARENT_ACTIVE_ICON_SPACING_PREFIX, DIVI4_ACTIVE_ICON_DESIGN_TOGGLE)
    );
}

function add_divi4_tab_fields($fields) {
    if (!is_array($fields)) {
        return $fields;
    }

    $fields[DIVI4_ICON_FIELD] = [
        'label'           => esc_html__('Tab Icon', 'et_builder'),
        'type'            => 'select_icon',
        'option_category' => 'basic_option',
        'class'           => ['et-pb-font-icon'],
        'toggle_slug'     => DIVI4_ICON_CONTENT_TOGGLE,
        'description'     => esc_html__('Choose an icon to display next to this tab title.', 'et_builder'),
    ];

    return array_merge(
        $fields,
        get_divi4_icon_design_fields(DIVI4_CHILD_ICON_FONT_PREFIX, DIVI4_CHILD_ICON_SPACING_PREFIX, DIVI4_ICON_DESIGN_TOGGLE)
    );
}

function get_divi4_icon_design_fields($font_prefix, $spacing_prefix, $toggle_slug) {
    $fields = [
        "{$font_prefix}_text_color" => [
            'label'           => esc_html__('Icon Text Color', 'et_builder'),
            'type'            => 'color-alpha',
            'option_category' => 'font_option',
            'custom_color'    => true,
            'tab_slug'        => 'advanced',
            'toggle_slug'     => $toggle_slug,
            'mobile_options'  => true,
            'hover'           => 'tabs',
            'sticky'          => true,
        ],
        "{$font_prefix}_font_size" => [
            'label'           => esc_html__('Icon Text Size', 'et_builder'),
            'type'            => 'range',
            'option_category' => 'font_option',
            'tab_slug'        => 'advanced',
            'toggle_slug'     => $toggle_slug,
            'allowed_units'   => ['%', 'em', 'rem', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ex', 'vh', 'vw'],
            'default_unit'    => 'px',
            'mobile_options'  => true,
            'hover'           => 'tabs',
            'sticky'          => true,
            'range_settings'  => [
                'min'  => '1',
                'max'  => '100',
                'step' => '1',
            ],
        ],
    ];

    if (class_exists('\ET_Builder_Module_Fields_Factory') && method_exists('\ET_Builder_Module_Fields_Factory', 'get')) {
        try {
            $text_shadow = \ET_Builder_Module_Fields_Factory::get('TextShadow');
            if ($text_shadow && method_exists($text_shadow, 'get_fields')) {
                $text_shadow_fields = $text_shadow->get_fields([
                    'label'           => 'Icon',
                    'prefix'          => $font_prefix,
                    'option_category' => 'font_option',
                    'tab_slug'        => 'advanced',
                    'toggle_slug'     => $toggle_slug,
                    'mobile_options'  => true,
                ]);
                if (is_array($text_shadow_fields)) {
                    $fields = array_merge($fields, $text_shadow_fields);
                }
            }
        } catch (\Throwable $e) {
        }

        try {
            $margin_padding = \ET_Builder_Module_Fields_Factory::get('MarginPadding');
            if ($margin_padding && method_exists($margin_padding, 'get_fields')) {
                $margin_padding_fields = $margin_padding->get_fields([
                    'label'           => 'Icon',
                    'prefix'          => $spacing_prefix,
                    'option_category' => 'layout',
                    'tab_slug'        => 'advanced',
                    'toggle_slug'     => $toggle_slug,
                    'use_margin'      => false,
                    'use_padding'     => true,
                    'mobile_options'  => true,
                    'hover'           => 'tabs',
                    'sticky'          => true,
                    'priority'        => 20,
                ]);
                if (is_array($margin_padding_fields)) {
                    $fields = array_merge($fields, $margin_padding_fields);
                }
            }
        } catch (\Throwable $e) {
        }
    }

    return normalize_divi4_field_definitions($fields);
}

function normalize_divi4_field_definitions(array $fields) {
    foreach ($fields as $key => $field) {
        if (!is_array($field)) {
            unset($fields[$key]);
        }
    }

    return $fields;
}

function render_divi4_tabs_with_icons($output, $render_slug, $module) {
    if ($render_slug === DIVI4_TAB_SLUG) {
        capture_divi4_tab_icon_data($output, $module);
        return $output;
    }

    if ($render_slug !== DIVI4_TABS_SLUG || !is_string($output) || $output === '') {
        return $output;
    }

    $tabs = isset($GLOBALS['divibooster_divi4_tab_icon_queue']) && is_array($GLOBALS['divibooster_divi4_tab_icon_queue'])
        ? $GLOBALS['divibooster_divi4_tab_icon_queue']
        : [];

    if (!$tabs) {
        return $output;
    }

    preg_match_all('/<li[^>]*>.*?<\/li>/si', $output, $li_matches);
    $tab_count = count($li_matches[0] ?? []);
    if ($tab_count < 1) {
        return $output;
    }

    $tabs_for_module = array_splice($tabs, 0, $tab_count);
    $GLOBALS['divibooster_divi4_tab_icon_queue'] = $tabs;

    $icons = [];
    $has_any_icon = false;
    foreach ($tabs_for_module as $tab) {
        $icon = $tab['icon'] ?? '';
        $icons[] = $icon;
        if (extract_divi5_icon_char($icon) !== '') {
            $has_any_icon = true;
        }
    }

    if (!$has_any_icon) {
        return $output;
    }

    $output = inject_icons_into_tabs_html($output, $icons);
    $output = get_divi4_fa_css_tag($icons) . $output;
    $css = get_divi4_tabs_icon_css($output, is_object($module) && isset($module->props) ? $module->props : [], $tabs_for_module);
    if ($css !== '') {
        $output .= '<style>' . $css . '</style>';
    }

    return $output;
}

function capture_divi4_tab_icon_data($output, $module) {
    $props = is_object($module) && isset($module->props) && is_array($module->props) ? $module->props : [];
    $order_class = '';

    if (is_string($output) && preg_match('/\bet_pb_tab_\d+\b/', $output, $matches)) {
        $order_class = $matches[0];
    }

    if (!isset($GLOBALS['divibooster_divi4_tab_icon_queue']) || !is_array($GLOBALS['divibooster_divi4_tab_icon_queue'])) {
        $GLOBALS['divibooster_divi4_tab_icon_queue'] = [];
    }

    $GLOBALS['divibooster_divi4_tab_icon_queue'][] = [
        'icon'        => $props[DIVI4_ICON_FIELD] ?? '',
        'props'       => $props,
        'order_class' => $order_class,
    ];
}

function get_divi4_tabs_icon_css($html, array $module_props, array $tabs) {
    $order_class = extract_tabs_order_class($html);
    $safe_order_class = sanitize_html_class($order_class);
    if ($safe_order_class === '') {
        return '';
    }

    $base_selector = '.' . $safe_order_class . ' .et_pb_tabs_controls li a .divibooster-tab-icon';
    $active_selector = '.' . $safe_order_class . ' .et_pb_tabs_controls li.et_pb_tab_active a .divibooster-tab-icon';

    $css = get_divi4_icon_rule_css($base_selector, $module_props, DIVI4_PARENT_ICON_FONT_PREFIX, DIVI4_PARENT_ICON_SPACING_PREFIX);

    foreach ($tabs as $index => $tab) {
        $props = isset($tab['props']) && is_array($tab['props']) ? $tab['props'] : [];
        $order = isset($tab['order_class']) ? sanitize_html_class($tab['order_class']) : '';
        $selector = $order !== ''
            ? '.' . $safe_order_class . ' .et_pb_tabs_controls li.' . $order . ' a .divibooster-tab-icon'
            : '.' . $safe_order_class . ' .et_pb_tabs_controls li:nth-child(' . ((int) $index + 1) . ') a .divibooster-tab-icon';

        $css .= get_divi4_icon_rule_css($selector, $props, DIVI4_CHILD_ICON_FONT_PREFIX, DIVI4_CHILD_ICON_SPACING_PREFIX);
    }

    $css .= get_divi4_icon_rule_css($active_selector, $module_props, DIVI4_PARENT_ACTIVE_ICON_FONT_PREFIX, DIVI4_PARENT_ACTIVE_ICON_SPACING_PREFIX);

    return $css;
}

function get_divi4_icon_rule_css($selector, array $props, $font_prefix, $spacing_prefix) {
    $selector = trim((string) $selector);
    if ($selector === '') {
        return '';
    }

    $css = '';
    $desktop = get_divi4_icon_declaration($props, $font_prefix, $spacing_prefix, '');
    if ($desktop !== '') {
        $css .= $selector . '{' . $desktop . '}';
    }

    $tablet = get_divi4_icon_declaration($props, $font_prefix, $spacing_prefix, '_tablet');
    if ($tablet !== '') {
        $css .= '@media (max-width: 980px){' . $selector . '{' . $tablet . '}}';
    }

    $phone = get_divi4_icon_declaration($props, $font_prefix, $spacing_prefix, '_phone');
    if ($phone !== '') {
        $css .= '@media (max-width: 767px){' . $selector . '{' . $phone . '}}';
    }

    $hover = get_divi4_icon_hover_declaration($props, $font_prefix, $spacing_prefix);
    if ($hover !== '') {
        $css .= $selector . ':hover{' . $hover . '}';
    }

    return $css;
}

function get_divi4_icon_declaration(array $props, $font_prefix, $spacing_prefix, $suffix = '') {
    $declaration = '';

    $color = get_divi4_prop($props, "{$font_prefix}_text_color{$suffix}");
    if ($color !== '') {
        $color = sanitize_css_declaration_value($color);
        if ($color !== '') {
            $declaration .= 'color:' . esc_html($color) . ' !important;';
        }
    }

    $font_size = get_divi4_prop($props, "{$font_prefix}_font_size{$suffix}");
    if ($font_size !== '') {
        $font_size = sanitize_css_declaration_value(process_divi_range_value($font_size));
        if ($font_size !== '') {
            $declaration .= 'font-size:' . esc_html($font_size) . ';';
        }
    }

    if ($suffix === '') {
        $declaration .= get_divi4_text_shadow_declaration($props, $font_prefix);
    }

    $padding = get_divi4_prop($props, "{$spacing_prefix}_custom_padding{$suffix}");
    if ($padding !== '') {
        $declaration .= get_divi4_spacing_declaration($padding, 'padding');
    }

    return $declaration;
}

function get_divi4_icon_hover_declaration(array $props, $font_prefix, $spacing_prefix) {
    if (!function_exists('et_pb_hover_options')) {
        return '';
    }

    try {
        $hover = et_pb_hover_options();
    } catch (\Throwable $e) {
        return '';
    }
    if (!is_object($hover) || !method_exists($hover, 'get_value')) {
        return '';
    }

    $declaration = '';

    foreach ([
        "{$font_prefix}_text_color" => 'color',
        "{$font_prefix}_font_size" => 'font-size',
    ] as $field => $property) {
        try {
            $value = $hover->get_value($field, $props, '');
        } catch (\Throwable $e) {
            continue;
        }
        if (!is_scalar($value) || trim((string) $value) === '') {
            continue;
        }

        $value = trim((string) $value);
        if ($property === 'font-size') {
            $value = process_divi_range_value($value);
        }
        $value = sanitize_css_declaration_value($value);
        if ($value === '') {
            continue;
        }
        $declaration .= $property . ':' . esc_html($value) . ($property === 'color' ? ' !important' : '') . ';';
    }

    try {
        $padding = $hover->get_value("{$spacing_prefix}_custom_padding", $props, '');
    } catch (\Throwable $e) {
        $padding = '';
    }
    if (is_scalar($padding) && trim((string) $padding) !== '') {
        $declaration .= get_divi4_spacing_declaration(trim((string) $padding), 'padding');
    }

    return $declaration;
}

function get_divi4_prop(array $props, $key) {
    if (!isset($props[$key]) || !is_scalar($props[$key])) {
        return '';
    }

    return trim((string) $props[$key]);
}

function process_divi_range_value($value) {
    if (!function_exists('et_builder_process_range_value')) {
        return $value;
    }

    try {
        $processed = et_builder_process_range_value($value);
        return is_scalar($processed) ? (string) $processed : $value;
    } catch (\Throwable $e) {
        return $value;
    }
}

function get_divi4_text_shadow_declaration(array $props, $prefix) {
    $style = get_divi4_prop($props, "{$prefix}_text_shadow_style");
    if ($style === '' || strtolower($style) === 'none') {
        return '';
    }

    $horizontal = get_divi4_prop($props, "{$prefix}_text_shadow_horizontal_length");
    $vertical = get_divi4_prop($props, "{$prefix}_text_shadow_vertical_length");
    $blur = get_divi4_prop($props, "{$prefix}_text_shadow_blur_strength");
    $color = get_divi4_prop($props, "{$prefix}_text_shadow_color");

    $value = trim(($horizontal !== '' ? $horizontal : '0em') . ' ' . ($vertical !== '' ? $vertical : '0em') . ' ' . ($blur !== '' ? $blur : '0em') . ($color !== '' ? ' ' . $color : ''));

    $value = sanitize_css_declaration_value($value);

    return $value !== '' ? 'text-shadow:' . esc_html($value) . ';' : '';
}

function get_divi4_spacing_declaration($value, $property) {
    $value = trim((string) $value);
    if ($value === '') {
        return '';
    }

    if (function_exists('et_builder_get_element_style_css')) {
        try {
            $css = et_builder_get_element_style_css($value, $property, true);
            if (is_string($css) && trim($css) !== '') {
                return rtrim(trim($css), ';') . ';';
            }
        } catch (\Throwable $e) {
        }
    }

    $parts = explode('|', $value);
    $declaration = '';
    foreach (['top', 'right', 'bottom', 'left'] as $index => $side) {
        if (!isset($parts[$index]) || trim($parts[$index]) === '') {
            continue;
        }
        $value = sanitize_css_declaration_value(trim($parts[$index]));
        if ($value === '') {
            continue;
        }
        $declaration .= $property . '-' . $side . ':' . esc_html($value) . ';';
    }

    return $declaration;
}

function sanitize_css_declaration_value($value) {
    if (!is_scalar($value)) {
        return '';
    }

    $value = trim((string) $value);
    if ($value === '' || preg_match('/[;<>{}]/', $value)) {
        return '';
    }

    return $value;
}

function add_divi4_to_divi5_conversion_outline($conversion_outline, $module_name) {
    if (!is_array($conversion_outline)) {
        return $conversion_outline;
    }

    if ($module_name === DIVI5_TABS_SLUG || $module_name === 'tabs') {
        $conversion_outline = add_divi4_advanced_option_conversion(
            $conversion_outline,
            'fonts',
            DIVI4_PARENT_ICON_FONT_PREFIX,
            DIVI5_ICON_FONT_ATTR . '.decoration.font'
        );
        $conversion_outline = add_divi4_advanced_option_conversion(
            $conversion_outline,
            'fonts',
            DIVI4_PARENT_ACTIVE_ICON_FONT_PREFIX,
            DIVI5_ACTIVE_ICON_FONT_ATTR . '.decoration.font'
        );
        $conversion_outline = add_divi4_advanced_option_conversion(
            $conversion_outline,
            'margin_padding',
            DIVI4_PARENT_ICON_SPACING_PREFIX,
            DIVI5_ICON_SPACING_ATTR . '.decoration.spacing'
        );
        $conversion_outline = add_divi4_advanced_option_conversion(
            $conversion_outline,
            'margin_padding',
            DIVI4_PARENT_ACTIVE_ICON_SPACING_PREFIX,
            DIVI5_ACTIVE_ICON_SPACING_ATTR . '.decoration.spacing'
        );
    }

    if ($module_name === DIVI5_TAB_SLUG || $module_name === 'tab') {
        if (!isset($conversion_outline['module']) || !is_array($conversion_outline['module'])) {
            $conversion_outline['module'] = [];
        }
        if (!isset($conversion_outline['valueExpansionFunctionMap']) || !is_array($conversion_outline['valueExpansionFunctionMap'])) {
            $conversion_outline['valueExpansionFunctionMap'] = [];
        }

        $conversion_outline['module'][DIVI4_ICON_FIELD] = DIVI5_ICON_ATTR . '.*';
        $conversion_outline['valueExpansionFunctionMap'][DIVI4_ICON_FIELD] = 'convertFontIcon';

        $conversion_outline = add_divi4_advanced_option_conversion(
            $conversion_outline,
            'fonts',
            DIVI4_CHILD_ICON_FONT_PREFIX,
            DIVI5_ICON_FONT_ATTR . '.decoration.font'
        );
        $conversion_outline = add_divi4_advanced_option_conversion(
            $conversion_outline,
            'margin_padding',
            DIVI4_CHILD_ICON_SPACING_PREFIX,
            DIVI5_ICON_SPACING_ATTR . '.decoration.spacing'
        );
    }

    return $conversion_outline;
}

function add_divi4_advanced_option_conversion(array $conversion_outline, $advanced_option, $d4_slug, $d5_path) {
    if (!isset($conversion_outline['advanced']) || !is_array($conversion_outline['advanced'])) {
        $conversion_outline['advanced'] = [];
    }
    if (!isset($conversion_outline['advanced'][$advanced_option]) || !is_array($conversion_outline['advanced'][$advanced_option])) {
        $conversion_outline['advanced'][$advanced_option] = [];
    }

    $conversion_outline['advanced'][$advanced_option][$d4_slug] = $d5_path;

    return $conversion_outline;
}

function get_divi5_inline_js() {
    if (!function_exists('wp_json_encode')) {
        return '';
    }

  $tabs_slug_js      = wp_json_encode(DIVI5_TABS_SLUG);
    $tab_slug_js       = wp_json_encode(DIVI5_TAB_SLUG);
    $icon_attr_js      = wp_json_encode(DIVI5_ICON_ATTR);
  $icon_font_attr_js = wp_json_encode(DIVI5_ICON_FONT_ATTR);
  $active_icon_font_attr_js = wp_json_encode(DIVI5_ACTIVE_ICON_FONT_ATTR);
  $icon_spacing_attr_js = wp_json_encode(DIVI5_ICON_SPACING_ATTR);
  $active_icon_spacing_attr_js = wp_json_encode(DIVI5_ACTIVE_ICON_SPACING_ATTR);
  $icon_design_group_slug_js = wp_json_encode(DIVI5_ICON_DESIGN_GROUP_SLUG);
  $active_icon_design_group_slug_js = wp_json_encode(DIVI5_ACTIVE_ICON_DESIGN_GROUP_SLUG);
  $icon_group_slug_js = wp_json_encode(DIVI5_ICON_GROUP_SLUG);
  $tabs_icon_font_attr_definition_js = wp_json_encode(get_divi5_tabs_icon_font_attribute_definition());
  $tabs_active_icon_font_attr_definition_js = wp_json_encode(get_divi5_tabs_active_icon_font_attribute_definition());
  $tabs_icon_spacing_attr_definition_js = wp_json_encode(get_divi5_tabs_icon_spacing_attribute_definition());
  $tabs_active_icon_spacing_attr_definition_js = wp_json_encode(get_divi5_tabs_active_icon_spacing_attribute_definition());
  $tab_icon_attr_definition_js = wp_json_encode(get_divi5_tab_icon_attribute_definition());
  $tab_item_icon_font_attr_definition_js = wp_json_encode(get_divi5_tab_item_icon_font_attribute_definition());
  $tab_item_icon_spacing_attr_definition_js = wp_json_encode(get_divi5_tab_item_icon_spacing_attribute_definition());
  if (in_array(false, [
      $tabs_slug_js,
      $tab_slug_js,
      $icon_attr_js,
      $icon_font_attr_js,
      $active_icon_font_attr_js,
      $icon_spacing_attr_js,
      $active_icon_spacing_attr_js,
      $icon_design_group_slug_js,
      $active_icon_design_group_slug_js,
      $icon_group_slug_js,
      $tabs_icon_font_attr_definition_js,
      $tabs_active_icon_font_attr_definition_js,
      $tabs_icon_spacing_attr_definition_js,
      $tabs_active_icon_spacing_attr_definition_js,
      $tab_icon_attr_definition_js,
      $tab_item_icon_font_attr_definition_js,
      $tab_item_icon_spacing_attr_definition_js,
  ], true)) {
      return '';
  }

    return <<<JS
(function() {
  const { addFilter } = window.vendor.wp.hooks;
  const { get, set, has } = window.lodash;
  const designTabIconGroupSlug = {$icon_design_group_slug_js};
  const designActiveTabIconGroupSlug = {$active_icon_design_group_slug_js};
  const tabsIconFontAttrDefinition = {$tabs_icon_font_attr_definition_js};
  const tabsActiveIconFontAttrDefinition = {$tabs_active_icon_font_attr_definition_js};
  const tabsIconSpacingAttrDefinition = {$tabs_icon_spacing_attr_definition_js};
  const tabsActiveIconSpacingAttrDefinition = {$tabs_active_icon_spacing_attr_definition_js};
  const tabIconAttrDefinition = {$tab_icon_attr_definition_js};
  const tabItemIconFontAttrDefinition = {$tab_item_icon_font_attr_definition_js};
  const tabItemIconSpacingAttrDefinition = {$tab_item_icon_spacing_attr_definition_js};
  const hookNamespace = 'divibooster/tab-icons';
  const tabsMappingHookNamespace = hookNamespace + '-tabs-mapping';
  const tabMappingHookNamespace = hookNamespace + '-tab-mapping';

  // === Icon Value Helpers ===
  function decodeHtmlEntities(value) {
    if (typeof value !== 'string') return '';
    var d = document.createElement('div');
    d.innerHTML = value;
    return d.textContent || d.innerText || '';
  }

  function fromHexCodepoint(hex) {
    var codePoint = parseInt(hex, 16);
    if (!isFinite(codePoint)) return '';
    try {
      return String.fromCodePoint(codePoint);
    } catch (e) {
      return '';
    }
  }

  function normalizeIconValue(value) {
    if (value === null || value === undefined) return '';

    if (typeof value === 'number' && isFinite(value)) {
      try {
        return String.fromCodePoint(value);
      } catch (e) {
        return '';
      }
    }

    if (typeof value === 'string') {
      var normalized = value.trim();
      if (normalized === '' || normalized.indexOf('$' + 'variable(') === 0) {
        return '';
      }

      if (normalized.indexOf('&') !== -1) {
        normalized = decodeHtmlEntities(normalized).trim();
      }

      var match = normalized.match(/^\\u([0-9a-f]{4,6})$/i) ||
        normalized.match(/^U\+([0-9a-f]{4,6})$/i) ||
        normalized.match(/^0x([0-9a-f]{4,6})$/i) ||
        normalized.match(/^([0-9a-f]{4,6})$/i);

      if (match) {
        var charFromHex = fromHexCodepoint(match[1]);
        if (charFromHex !== '') return charFromHex;
      }

      return normalized;
    }

    if (Array.isArray(value)) {
      for (var i = 0; i < value.length; i++) {
        var arrayIcon = normalizeIconValue(value[i]);
        if (arrayIcon !== '') return arrayIcon;
      }
      return '';
    }

    if (typeof value === 'object') {
      var preferredKeys = ['unicode', 'icon', 'char', 'code', 'value', 'desktop', 'tablet', 'phone', 'innerContent'];

      for (var j = 0; j < preferredKeys.length; j++) {
        var key = preferredKeys[j];
        if (Object.prototype.hasOwnProperty.call(value, key)) {
          var preferredIcon = normalizeIconValue(value[key]);
          if (preferredIcon !== '') return preferredIcon;
        }
      }

      var keys = Object.keys(value);
      for (var k = 0; k < keys.length; k++) {
        var nestedIcon = normalizeIconValue(value[keys[k]]);
        if (nestedIcon !== '') return nestedIcon;
      }
    }

    return '';
  }

  function extractIconFromAttrs(childAttrs) {
    if (!childAttrs) return '';
    try {
      return normalizeIconValue(childAttrs[{$icon_attr_js}]);
    } catch (e) {
      return '';
    }
  }

  // === Parent Tabs Builder Settings ===
  addFilter('divi.moduleLibrary.moduleMapping', tabsMappingHookNamespace, modules => {
    const path = [{$tabs_slug_js}, 'metadata'];
    if (!has(modules, path)) return modules;
    const metadata = get(modules, path);

    // Add design groups used by the tab icon controls.
    const groupsPath = [{$tabs_slug_js}, 'metadata', 'settings', 'groups'];
    const groups = get(modules, groupsPath) || {};
    groups[designTabIconGroupSlug] = {
      panel: 'design',
      priority: 12,
      groupName: designTabIconGroupSlug,
      component: {
        name: 'divi/composite',
        props: {
          groupLabel: 'Tab Icon',
          presetGroup: 'divi/font',
        }
      }
    };
    groups[designActiveTabIconGroupSlug] = {
      panel: 'design',
      priority: 13,
      groupName: designActiveTabIconGroupSlug,
      component: {
        name: 'divi/composite',
        props: {
          groupLabel: 'Active Tab Icon',
          presetGroup: 'divi/font',
        }
      }
    };
    set(modules, groupsPath, groups);

    // Map design attributes to the injected icon span.
    const attrsPath = [{$tabs_slug_js}, 'metadata', 'attributes'];
    const attrs = get(modules, attrsPath) || {};
    attrs[{$icon_font_attr_js}] = tabsIconFontAttrDefinition;
    attrs[{$icon_spacing_attr_js}] = tabsIconSpacingAttrDefinition;
    attrs[{$active_icon_font_attr_js}] = tabsActiveIconFontAttrDefinition;
    attrs[{$active_icon_spacing_attr_js}] = tabsActiveIconSpacingAttrDefinition;
    set(modules, attrsPath, attrs);

    return modules;
  });

  // === Child Tab Builder Settings ===
  addFilter('divi.moduleLibrary.moduleMapping', tabMappingHookNamespace, modules => {
    // Leave unrelated module mappings untouched.
    if (!get(modules, [{$tab_slug_js}, 'metadata'])) return modules;

    const groupsPath = [{$tab_slug_js}, 'metadata', 'settings', 'groups'];
    const groups = get(modules, groupsPath) || {};
    groups[designTabIconGroupSlug] = {
      panel: 'design',
      priority: 12,
      groupName: designTabIconGroupSlug,
      component: {
        name: 'divi/composite',
        props: {
          groupLabel: 'Tab Icon',
          presetGroup: 'divi/font',
        }
      }
    };
    set(modules, groupsPath, groups);

    const path = [{$tab_slug_js}, 'metadata', 'attributes'];
    const attrs = get(modules, path) || {};
    attrs[{$icon_attr_js}] = tabIconAttrDefinition;
    attrs[{$icon_font_attr_js}] = tabItemIconFontAttrDefinition;
    attrs[{$icon_spacing_attr_js}] = tabItemIconSpacingAttrDefinition;

    set(modules, path, attrs);
    return modules;
  });

  // === Child Tab Content Group ===
  // Scope the group registration to the tab module metadata.
  addFilter('divi.moduleLibrary.moduleSettings.groups', 'divibooster', function(groups, metadata) {
    try {
      var moduleName = metadata && metadata.name ? metadata.name : null;
      if (moduleName !== {$tab_slug_js}) return groups;
    } catch (e) {
      return groups;
    }

    if (!groups) groups = {};
    if (!groups[{$icon_group_slug_js}]) {
      groups[{$icon_group_slug_js}] = {
        groupName: {$icon_group_slug_js},
        panel: 'content',
        priority: 20,
        component: {
          name: 'divi/composite',
          props: {
            groupLabel: 'Tab Icon'
          }
        }
      };
    }
    if (!groups[designTabIconGroupSlug]) {
      groups[designTabIconGroupSlug] = {
        panel: 'design',
        priority: 12,
        groupName: designTabIconGroupSlug,
        component: {
          name: 'divi/composite',
          props: {
            groupLabel: 'Tab Icon',
            presetGroup: 'divi/font',
          }
        }
      };
    }
    return groups;
  });

  // === Builder Preview Injection ===
  // Hook the parent tabs wrapper because the tab header controls live there.
  addFilter('divi.module.wrapper.render', 'divibooster', function(moduleWrapper, param) {
    try {
      var moduleName = param && param.name ? param.name : null;

      if (moduleName !== {$tabs_slug_js}) {
        return moduleWrapper;
      }
      var WPElement = window.vendor && window.vendor.wp && window.vendor.wp.element;
      var React = (window.vendor && window.vendor.React) || WPElement;
      if (!React || !WPElement || typeof React.useEffect !== 'function' || typeof React.useRef !== 'function') {
        return moduleWrapper;
      }
      var previewModuleAttrs = param && param.attrs ? param.attrs : {};
      var moduleState = param && param.state ? param.state : '';
      var moduleElements = param && param.elements ? param.elements : null;
      try {
        if (previewModuleAttrs && typeof previewModuleAttrs.toJS === 'function') {
          previewModuleAttrs = previewModuleAttrs.toJS();
        } else if (previewModuleAttrs && typeof previewModuleAttrs.asMutable === 'function') {
          previewModuleAttrs = previewModuleAttrs.asMutable({deep: true});
        }
      } catch (e) { /* keep as-is */ }

      function stripUnsupportedIconFontFields(styleAttr) {
        if (!styleAttr || typeof styleAttr !== 'object') return styleAttr;
        var sanitized = styleAttr;
        try {
          sanitized = JSON.parse(JSON.stringify(styleAttr));
        } catch (e) {
          sanitized = Object.assign({}, styleAttr);
        }

        var fontAttr = sanitized && sanitized.font;
        if (!fontAttr || typeof fontAttr !== 'object') {
          return sanitized;
        }

        Object.keys(fontAttr).forEach(function(breakpoint) {
          var states = fontAttr[breakpoint];
          if (!states || typeof states !== 'object') return;
          Object.keys(states).forEach(function(state) {
            var stateValue = states[state];
            if (!stateValue || typeof stateValue !== 'object') {
              return;
            }
            ['family', 'weight', 'style', 'letterSpacing', 'lineHeight'].forEach(function(fieldName) {
              if (Object.prototype.hasOwnProperty.call(stateValue, fieldName)) {
                delete stateValue[fieldName];
              }
            });
          });
        });

        return sanitized;
      }

      function extractPreviewFontAttr(rawAttr) {
        if (!rawAttr || typeof rawAttr !== 'object') return {};
        if (rawAttr.decoration && rawAttr.decoration.font && typeof rawAttr.decoration.font === 'object') {
          return stripUnsupportedIconFontFields(rawAttr.decoration.font);
        }
        if (rawAttr.font || rawAttr.textShadow) {
          return stripUnsupportedIconFontFields(rawAttr);
        }
        if (rawAttr.value && typeof rawAttr.value === 'object' && (rawAttr.value.font || rawAttr.value.textShadow)) {
          return stripUnsupportedIconFontFields(rawAttr.value);
        }
        return {};
      }

      function stripUnsupportedIconSpacingFields(spacingAttr) {
        if (!spacingAttr || typeof spacingAttr !== 'object') return spacingAttr;
        var sanitized = spacingAttr;
        try {
          sanitized = JSON.parse(JSON.stringify(spacingAttr));
        } catch (e) {
          sanitized = Object.assign({}, spacingAttr);
        }

        ['desktop', 'tablet', 'phone'].forEach(function(breakpoint) {
          var states = sanitized[breakpoint];
          if (!states || typeof states !== 'object') return;
          Object.keys(states).forEach(function(state) {
            var stateValue = states[state];
            if (!stateValue || typeof stateValue !== 'object') return;
            if (Object.prototype.hasOwnProperty.call(stateValue, 'margin')) {
              delete stateValue.margin;
            }
          });
        });

        return sanitized;
      }

      function extractPreviewSpacingAttr(rawAttr) {
        if (!rawAttr || typeof rawAttr !== 'object') return {};
        if (rawAttr.decoration && rawAttr.decoration.spacing && typeof rawAttr.decoration.spacing === 'object') {
          return stripUnsupportedIconSpacingFields(rawAttr.decoration.spacing);
        }
        if (rawAttr.desktop || rawAttr.tablet || rawAttr.phone) {
          return stripUnsupportedIconSpacingFields(rawAttr);
        }
        if (rawAttr.spacing && typeof rawAttr.spacing === 'object') {
          return stripUnsupportedIconSpacingFields(rawAttr.spacing);
        }
        if (rawAttr.value && typeof rawAttr.value === 'object') {
          if (rawAttr.value.spacing && typeof rawAttr.value.spacing === 'object') {
            return stripUnsupportedIconSpacingFields(rawAttr.value.spacing);
          }
          if (rawAttr.value.desktop || rawAttr.value.tablet || rawAttr.value.phone) {
            return stripUnsupportedIconSpacingFields(rawAttr.value);
          }
        }
        return {};
      }

      // === Child Icon Data ===
      // Normalize the child attr containers passed to the parent wrapper.
      var icons = [];
      var childIconFonts = [];
      var childIconSpacings = [];
      var iconTypes = [];
      var iconWeights = [];

      function decodeHtmlEntities(value) {
        if (typeof value !== 'string') return '';
        var d = document.createElement('div');
        d.innerHTML = value;
        return d.textContent || d.innerText || '';
      }

      function fromHexCodepoint(hex) {
        var codePoint = parseInt(hex, 16);
        if (!isFinite(codePoint)) return '';
        try {
          return String.fromCodePoint(codePoint);
        } catch (e) {
          return '';
        }
      }

      function normalizeIconValue(value) {
        if (value === null || value === undefined) return '';

        if (typeof value === 'number' && isFinite(value)) {
          try {
            return String.fromCodePoint(value);
          } catch (e) {
            return '';
          }
        }

        if (typeof value === 'string') {
          var normalized = value.trim();
          if (normalized === '' || normalized.indexOf('$' + 'variable(') === 0) {
            return '';
          }

          if (normalized.indexOf('&') !== -1) {
            normalized = decodeHtmlEntities(normalized).trim();
          }

          var match = normalized.match(/^\\u([0-9a-f]{4,6})$/i) ||
            normalized.match(/^U\+([0-9a-f]{4,6})$/i) ||
            normalized.match(/^0x([0-9a-f]{4,6})$/i) ||
            normalized.match(/^([0-9a-f]{4,6})$/i);

          if (match) {
            var charFromHex = fromHexCodepoint(match[1]);
            if (charFromHex !== '') return charFromHex;
          }

          return normalized;
        }

        if (Array.isArray(value)) {
          for (var i = 0; i < value.length; i++) {
            var arrayIcon = normalizeIconValue(value[i]);
            if (arrayIcon !== '') return arrayIcon;
          }
          return '';
        }

        if (typeof value === 'object') {
          var preferredKeys = ['unicode', 'icon', 'char', 'code', 'value', 'desktop', 'tablet', 'phone', 'innerContent'];

          for (var j = 0; j < preferredKeys.length; j++) {
            var key = preferredKeys[j];
            if (Object.prototype.hasOwnProperty.call(value, key)) {
              var preferredIcon = normalizeIconValue(value[key]);
              if (preferredIcon !== '') return preferredIcon;
            }
          }

          var keys = Object.keys(value);
          for (var k = 0; k < keys.length; k++) {
            var nestedIcon = normalizeIconValue(value[keys[k]]);
            if (nestedIcon !== '') return nestedIcon;
          }
        }

        return '';
      }

      function extractIconMetaProp(value, propName) {
        if (value === null || value === undefined) return '';

        if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
          return '';
        }

        if (Array.isArray(value)) {
          for (var i = 0; i < value.length; i++) {
            var arrProp = extractIconMetaProp(value[i], propName);
            if (arrProp !== '') return arrProp;
          }
          return '';
        }

        if (typeof value === 'object') {
          if (Object.prototype.hasOwnProperty.call(value, propName)) {
            var direct = value[propName];
            if (direct !== null && direct !== undefined) {
              var directString = String(direct).trim();
              if (directString !== '') return directString;
            }
          }

          var preferredKeys = ['value', 'desktop', 'tablet', 'phone', 'innerContent', 'iconPicker'];
          for (var j = 0; j < preferredKeys.length; j++) {
            var key = preferredKeys[j];
            if (Object.prototype.hasOwnProperty.call(value, key)) {
              var preferredProp = extractIconMetaProp(value[key], propName);
              if (preferredProp !== '') return preferredProp;
            }
          }

          var keys = Object.keys(value);
          for (var k = 0; k < keys.length; k++) {
            var nestedProp = extractIconMetaProp(value[keys[k]], propName);
            if (nestedProp !== '') return nestedProp;
          }
        }

        return '';
      }

      function extractIconMetaFromValue(value) {
        return {
          char: normalizeIconValue(value),
          type: extractIconMetaProp(value, 'type'),
          weight: extractIconMetaProp(value, 'weight'),
        };
      }

      // Resolve the saved icon character from child attrs.
      function extractIconFromAttrs(childAttrs) {
        if (!childAttrs) return '';
        return extractIconMetaFromValue(childAttrs[{$icon_attr_js}]).char;
      }

      function extractIconMetaFromAttrs(childAttrs) {
        if (!childAttrs || typeof childAttrs !== 'object') {
          return { char: '', type: '', weight: '' };
        }
        return extractIconMetaFromValue(childAttrs[{$icon_attr_js}]);
      }

      function extractIconFontFromAttrs(childAttrs) {
        if (!childAttrs || typeof childAttrs !== 'object') return {};
        var rawAttr = childAttrs[{$icon_font_attr_js}];
        if (!rawAttr && childAttrs.module && typeof childAttrs.module === 'object') {
          rawAttr = childAttrs.module[{$icon_font_attr_js}];
        }
        return extractPreviewFontAttr(rawAttr);
      }

      function extractIconSpacingFromAttrs(childAttrs) {
        if (!childAttrs || typeof childAttrs !== 'object') return {};
        var rawAttr = childAttrs[{$icon_spacing_attr_js}];
        if (!rawAttr && childAttrs.module && typeof childAttrs.module === 'object') {
          rawAttr = childAttrs.module[{$icon_spacing_attr_js}];
        }
        return extractPreviewSpacingAttr(rawAttr);
      }

      function isLikelyFontAwesomeGlyph(iconChar) {
        if (typeof iconChar !== 'string' || iconChar === '') return false;
        try {
          var cp = iconChar.codePointAt(0);
          return isFinite(cp) && cp >= 0xF000 && cp <= 0xF8FF;
        } catch (e) {
          return false;
        }
      }

      function isLikelyFontAwesomeType(iconType) {
        var normalizedType = typeof iconType === 'string' ? iconType.trim().toLowerCase() : '';
        return normalizedType.indexOf('fa') === 0 || normalizedType === 'brands' || normalizedType === 'solid' || normalizedType === 'regular';
      }

      function normalizeWeightKeyword(weightValue) {
        var raw = (weightValue === null || weightValue === undefined) ? '' : String(weightValue).trim().toLowerCase();
        if (raw === '') return '';
        if (raw === 'regular' || raw === 'normal' || raw === 'book' || raw === 'light' || raw === 'thin' || raw === 'hairline') return '400';
        if (raw === 'medium') return '400';
        if (raw === 'semibold' || raw === 'semi-bold' || raw === 'demibold' || raw === 'demi-bold') return '900';
        if (raw === 'bold' || raw === 'solid') return '900';
        if (raw === 'extrabold' || raw === 'extra-bold' || raw === 'ultrabold' || raw === 'ultra-bold') return '900';
        if (raw === 'black' || raw === 'heavy') return '900';
        var parsed = raw.replace(/[^0-9]/g, '');
        if (!/^(100|200|300|400|500|600|700|800|900)$/.test(parsed)) return '';
        return parseInt(parsed, 10) >= 600 ? '900' : '400';
      }

      function getDefaultFaWeight(iconType, iconWeightHint) {
        var normalizedType = typeof iconType === 'string' ? iconType.trim().toLowerCase() : '';
        var hint = normalizeWeightKeyword(iconWeightHint);
        if (hint !== '') return hint;
        if (normalizedType === 'fab' || normalizedType === 'fa-brands' || normalizedType === 'brands' || normalizedType === 'far' || normalizedType === 'fa-regular' || normalizedType === 'regular') {
          return '400';
        }
        if (normalizedType === 'fas' || normalizedType === 'fa-solid' || normalizedType === 'solid') {
          return '900';
        }
        if (normalizedType.indexOf('fa') === 0) {
          return '900';
        }
        return '';
      }

      function resolvePreviewFaWeight(requestedWeight, iconType, iconWeightHint, iconChar) {
        var defaultWeight = getDefaultFaWeight(iconType, iconWeightHint) || (isLikelyFontAwesomeGlyph(iconChar) ? '900' : '');
        var hintedWeight = normalizeWeightKeyword(iconWeightHint);
        var requested = normalizeWeightKeyword(requestedWeight);

        if (hintedWeight !== '') return hintedWeight;
        if (requested !== '') return requested;
        if (defaultWeight !== '') return defaultWeight;
        return '';
      }

      function applyPreviewIconFont(iconEl, iconChar, iconType, iconWeight) {
        if (!iconEl || !iconEl.style) return;
        if (isLikelyFontAwesomeGlyph(iconChar) || isLikelyFontAwesomeType(iconType)) {
          var existingWeight = normalizeWeightKeyword(iconEl.style.fontWeight || iconEl.getAttribute('data-divibooster-resolved-fa-weight') || '');
          var normalizedWeight = resolvePreviewFaWeight(iconWeight, iconType, iconWeight, iconChar);
          iconEl.style.fontFamily = 'FontAwesome';
          if (normalizedWeight === '' && existingWeight !== '') {
            normalizedWeight = existingWeight;
          }
          if (normalizedWeight !== '') {
            iconEl.style.fontWeight = normalizedWeight;
            iconEl.setAttribute('data-divibooster-resolved-fa-weight', normalizedWeight);
          } else {
            iconEl.style.removeProperty('font-weight');
            iconEl.removeAttribute('data-divibooster-resolved-fa-weight');
          }
        } else {
          iconEl.style.removeProperty('font-family');
          iconEl.style.removeProperty('font-weight');
          iconEl.removeAttribute('data-divibooster-resolved-fa-weight');
        }
      }

      function hasStyleKeys(styleObj) {
        return !!(styleObj && typeof styleObj === 'object' && Object.keys(styleObj).length);
      }

      function extractDesktopInlineStyleFromFontAttr(fontAttr) {
        if (!fontAttr || typeof fontAttr !== 'object') {
          return {};
        }

        var result = {};
        var fontDesktopValue = fontAttr.font && fontAttr.font.desktop && fontAttr.font.desktop.value && typeof fontAttr.font.desktop.value === 'object'
          ? fontAttr.font.desktop.value
          : null;

        if (fontDesktopValue) {
          if (fontDesktopValue.color) result.color = String(fontDesktopValue.color);
          if (fontDesktopValue.size) result.fontSize = String(fontDesktopValue.size);
          if (fontDesktopValue.weight) result.fontWeight = String(fontDesktopValue.weight);
          if (fontDesktopValue.style) result.fontStyle = String(fontDesktopValue.style);
          if (fontDesktopValue.textTransform) result.textTransform = String(fontDesktopValue.textTransform);
          if (fontDesktopValue.textDecoration) result.textDecoration = String(fontDesktopValue.textDecoration);
        }

        var shadowDesktopValue = fontAttr.textShadow && fontAttr.textShadow.desktop && fontAttr.textShadow.desktop.value && typeof fontAttr.textShadow.desktop.value === 'object'
          ? fontAttr.textShadow.desktop.value
          : null;
        if (shadowDesktopValue) {
          var shadowStyle = shadowDesktopValue.style ? String(shadowDesktopValue.style).toLowerCase() : '';
          if (shadowStyle && shadowStyle !== 'none') {
            var horizontal = shadowDesktopValue.horizontal || '0em';
            var vertical = shadowDesktopValue.vertical || '0em';
            var blur = shadowDesktopValue.blur || '0em';
            var color = shadowDesktopValue.color ? String(shadowDesktopValue.color) : '';
            result.textShadow = String(horizontal) + ' ' + String(vertical) + ' ' + String(blur) + (color ? (' ' + color) : '');
          }
        }

        return result;
      }

      function extractDesktopInlineStyleFromSpacingAttr(spacingAttr) {
        if (!spacingAttr || typeof spacingAttr !== 'object') {
          return {};
        }

        var padding = spacingAttr.desktop && spacingAttr.desktop.value && spacingAttr.desktop.value.padding && typeof spacingAttr.desktop.value.padding === 'object'
          ? spacingAttr.desktop.value.padding
          : null;
        if (!padding) {
          return {};
        }

        var result = {};
        if (padding.top) result.paddingTop = String(padding.top);
        if (padding.right) result.paddingRight = String(padding.right);
        if (padding.bottom) result.paddingBottom = String(padding.bottom);
        if (padding.left) result.paddingLeft = String(padding.left);
        return result;
      }

      function sanitizePreviewFontAttrForIcon(fontAttr, iconChar, iconType, iconWeightHint) {
        if (!fontAttr || typeof fontAttr !== 'object') {
          return fontAttr;
        }
        if (!(isLikelyFontAwesomeGlyph(iconChar) || isLikelyFontAwesomeType(iconType))) {
          return fontAttr;
        }

        var cloned;
        try {
          cloned = JSON.parse(JSON.stringify(fontAttr));
        } catch (e) {
          return fontAttr;
        }

        if (!cloned || !cloned.font || typeof cloned.font !== 'object') {
          return cloned;
        }

        ['desktop', 'tablet', 'phone'].forEach(function(bp) {
          var states = cloned.font[bp];
          if (!states || typeof states !== 'object') {
            return;
          }
          Object.keys(states).forEach(function(stateKey) {
            var stateValue = states[stateKey];
            if (!stateValue || typeof stateValue !== 'object' || !Object.prototype.hasOwnProperty.call(stateValue, 'weight')) {
              return;
            }
            stateValue.weight = resolvePreviewFaWeight(stateValue.weight, iconType, iconWeightHint, iconChar);
          });
        });

        return cloned;
      }

      function applyPreviewChildFontStyle(iconEl, childFontAttr, iconChar, iconType, iconWeight) {
        if (!iconEl || !iconEl.style) {
          return;
        }

        function styleKeyToCssProperty(styleKey) {
          switch (styleKey) {
            case 'fontSize': return 'font-size';
            case 'fontWeight': return 'font-weight';
            case 'fontStyle': return 'font-style';
            case 'textTransform': return 'text-transform';
            case 'textDecoration': return 'text-decoration';
            case 'textShadow': return 'text-shadow';
            default: return styleKey;
          }
        }

        var previousPropsRaw = iconEl.getAttribute('data-divibooster-child-style-props') || '';
        if (previousPropsRaw !== '') {
          previousPropsRaw.split(',').forEach(function(prop) {
            var trimmed = (prop || '').trim();
            if (trimmed !== '') {
              iconEl.style.removeProperty(trimmed);
            }
          });
        }

        var inlineStyles = extractDesktopInlineStyleFromFontAttr(childFontAttr);
        if (!hasStyleKeys(inlineStyles)) {
          iconEl.removeAttribute('data-divibooster-child-style-props');
          return;
        }

        var appliedProps = [];
        Object.keys(inlineStyles).forEach(function(styleKey) {
          try {
            var styleValue = inlineStyles[styleKey];
            if (styleKey === 'fontWeight' && (isLikelyFontAwesomeGlyph(iconChar) || isLikelyFontAwesomeType(iconType))) {
              styleValue = resolvePreviewFaWeight(styleValue, iconType, iconWeight, iconChar);
            }
            iconEl.style[styleKey] = styleValue;
            appliedProps.push(styleKeyToCssProperty(styleKey));
          } catch (e) { /* ignore */ }
        });

        if (appliedProps.length) {
          iconEl.setAttribute('data-divibooster-child-style-props', appliedProps.join(','));
        } else {
          iconEl.removeAttribute('data-divibooster-child-style-props');
        }
      }

      function clearPreviewActiveIconFontStyle(iconEl) {
        if (!iconEl || !iconEl.style) {
          return;
        }

        var previousPropsRaw = iconEl.getAttribute('data-divibooster-active-style-props') || '';
        if (previousPropsRaw !== '') {
          previousPropsRaw.split(',').forEach(function(prop) {
            var trimmed = (prop || '').trim();
            if (trimmed !== '') {
              iconEl.style.removeProperty(trimmed);
            }
          });
        }
        iconEl.removeAttribute('data-divibooster-active-style-props');
      }

      function applyPreviewActiveIconFontStyle(iconEl, activeFontAttr, isActive) {
        if (!iconEl || !iconEl.style) {
          return;
        }

        function styleKeyToCssProperty(styleKey) {
          switch (styleKey) {
            case 'color': return 'color';
            case 'fontSize': return 'font-size';
            case 'fontWeight': return 'font-weight';
            case 'fontStyle': return 'font-style';
            case 'textTransform': return 'text-transform';
            case 'textDecoration': return 'text-decoration';
            case 'textShadow': return 'text-shadow';
            default: return styleKey;
          }
        }

        if (!isActive) {
          iconEl.removeAttribute('data-divibooster-active-style-props');
          return;
        }

        var inlineStyles = extractDesktopInlineStyleFromFontAttr(activeFontAttr);
        if (!hasStyleKeys(inlineStyles)) {
          iconEl.removeAttribute('data-divibooster-active-style-props');
          return;
        }

        var appliedProps = [];
        Object.keys(inlineStyles).forEach(function(styleKey) {
          try {
            var cssProp = styleKeyToCssProperty(styleKey);
            iconEl.style.setProperty(cssProp, inlineStyles[styleKey]);
            appliedProps.push(cssProp);
          } catch (e) { /* ignore */ }
        });

        if (appliedProps.length) {
          iconEl.setAttribute('data-divibooster-active-style-props', appliedProps.join(','));
        } else {
          iconEl.removeAttribute('data-divibooster-active-style-props');
        }
      }

      function clearPreviewActiveIconSpacingStyle(iconEl) {
        if (!iconEl || !iconEl.style) {
          return;
        }

        var previousPropsRaw = iconEl.getAttribute('data-divibooster-active-spacing-props') || '';
        if (previousPropsRaw !== '') {
          previousPropsRaw.split(',').forEach(function(prop) {
            var trimmed = (prop || '').trim();
            if (trimmed !== '') {
              iconEl.style.removeProperty(trimmed);
            }
          });
        }
        iconEl.removeAttribute('data-divibooster-active-spacing-props');
      }

      function applyPreviewActiveIconSpacingStyle(iconEl, activeSpacingAttr, isActive) {
        if (!iconEl || !iconEl.style) {
          return;
        }

        function styleKeyToCssProperty(styleKey) {
          switch (styleKey) {
            case 'paddingTop': return 'padding-top';
            case 'paddingRight': return 'padding-right';
            case 'paddingBottom': return 'padding-bottom';
            case 'paddingLeft': return 'padding-left';
            default: return styleKey;
          }
        }

        if (!isActive) {
          iconEl.removeAttribute('data-divibooster-active-spacing-props');
          return;
        }

        var inlineStyles = extractDesktopInlineStyleFromSpacingAttr(activeSpacingAttr);
        if (!hasStyleKeys(inlineStyles)) {
          iconEl.removeAttribute('data-divibooster-active-spacing-props');
          return;
        }

        var appliedProps = [];
        Object.keys(inlineStyles).forEach(function(styleKey) {
          try {
            var cssProp = styleKeyToCssProperty(styleKey);
            iconEl.style.setProperty(cssProp, inlineStyles[styleKey]);
            appliedProps.push(cssProp);
          } catch (e) { /* ignore */ }
        });

        if (appliedProps.length) {
          iconEl.setAttribute('data-divibooster-active-spacing-props', appliedProps.join(','));
        } else {
          iconEl.removeAttribute('data-divibooster-active-spacing-props');
        }
      }

      function applyPreviewChildSpacingStyle(iconEl, childSpacingAttr) {
        if (!iconEl || !iconEl.style) {
          return;
        }

        function styleKeyToCssProperty(styleKey) {
          switch (styleKey) {
            case 'paddingTop': return 'padding-top';
            case 'paddingRight': return 'padding-right';
            case 'paddingBottom': return 'padding-bottom';
            case 'paddingLeft': return 'padding-left';
            default: return styleKey;
          }
        }

        var previousPropsRaw = iconEl.getAttribute('data-divibooster-child-spacing-props') || '';
        if (previousPropsRaw !== '') {
          previousPropsRaw.split(',').forEach(function(prop) {
            var trimmed = (prop || '').trim();
            if (trimmed !== '') {
              iconEl.style.removeProperty(trimmed);
            }
          });
        }

        var inlineStyles = extractDesktopInlineStyleFromSpacingAttr(childSpacingAttr);
        if (!hasStyleKeys(inlineStyles)) {
          iconEl.removeAttribute('data-divibooster-child-spacing-props');
          return;
        }

        var appliedProps = [];
        Object.keys(inlineStyles).forEach(function(styleKey) {
          try {
            iconEl.style[styleKey] = inlineStyles[styleKey];
            appliedProps.push(styleKeyToCssProperty(styleKey));
          } catch (e) { /* ignore */ }
        });

        if (appliedProps.length) {
          iconEl.setAttribute('data-divibooster-child-spacing-props', appliedProps.join(','));
        } else {
          iconEl.removeAttribute('data-divibooster-child-spacing-props');
        }
      }

      // Prefer child attrs already passed to the parent wrapper.
      var childrenAttrs = param.childrenAttrs;
      if (childrenAttrs) {
        // Accept immutable or plain JS collections.
        try {
          if (typeof childrenAttrs.toJS === 'function') {
            childrenAttrs = childrenAttrs.toJS();
          } else if (typeof childrenAttrs.asMutable === 'function') {
            childrenAttrs = childrenAttrs.asMutable({deep: true});
          }
        } catch (e) { /* keep as-is */ }

        if (Array.isArray(childrenAttrs)) {
          childrenAttrs.forEach(function(child) {
            var childAttrsValue = child && child.attrs ? child.attrs : child;
            var iconMeta = extractIconMetaFromAttrs(childAttrsValue);
            icons.push(iconMeta.char);
            iconTypes.push(iconMeta.type);
            iconWeights.push(iconMeta.weight);
            childIconFonts.push(extractIconFontFromAttrs(childAttrsValue));
            childIconSpacings.push(extractIconSpacingFromAttrs(childAttrsValue));
          });
        } else if (childrenAttrs && typeof childrenAttrs === 'object') {
          // Preserve source order when attrs are keyed by child ID.
          var keys = Object.keys(childrenAttrs);
          keys.forEach(function(key) {
            var child = childrenAttrs[key];
            // Accept either raw attrs or a wrapper with an attrs property.
            if (child && child.attrs) {
              var iconMeta = extractIconMetaFromAttrs(child.attrs);
              icons.push(iconMeta.char);
              iconTypes.push(iconMeta.type);
              iconWeights.push(iconMeta.weight);
              childIconFonts.push(extractIconFontFromAttrs(child.attrs));
              childIconSpacings.push(extractIconSpacingFromAttrs(child.attrs));
            } else {
              var iconMetaFallback = extractIconMetaFromAttrs(child);
              icons.push(iconMetaFallback.char);
              iconTypes.push(iconMetaFallback.type);
              iconWeights.push(iconMetaFallback.weight);
              childIconFonts.push(extractIconFontFromAttrs(child));
              childIconSpacings.push(extractIconSpacingFromAttrs(child));
            }
          });
        }
      }

      // Capture child ids so icons can follow their matching tab labels.
      var childIds = [];
      if (param && param.childrenIds) {
        var rawIds = param.childrenIds;
        try {
          if (rawIds && typeof rawIds.toJS === 'function') {
            rawIds = rawIds.toJS();
          } else if (rawIds && typeof rawIds.asMutable === 'function') {
            rawIds = rawIds.asMutable({deep: true});
          }
        } catch (e) { /* ignore */ }

        if (Array.isArray(rawIds)) {
          childIds = rawIds.filter(function(v) { return typeof v === 'string' && v; });
        }
      }

      // Fallback to block data carried on module attrs.
      if (icons.length === 0) {
        var moduleAttrs = param.attrs || {};
        var innerBlocks = moduleAttrs.innerBlocks || [];
        // Accept nested block data when direct innerBlocks is empty.
        if ((!innerBlocks || !innerBlocks.length) && moduleAttrs.module && Array.isArray(moduleAttrs.module.innerBlocks)) {
          innerBlocks = moduleAttrs.module.innerBlocks;
        }
        if (innerBlocks && innerBlocks.length) {
          innerBlocks.forEach(function(child) {
            var childName = child.blockName || '';
            if (childName !== {$tab_slug_js}) return;
            var childAttrsValue = child.attrs || {};
            var iconMeta = extractIconMetaFromAttrs(childAttrsValue);
            icons.push(iconMeta.char);
            iconTypes.push(iconMeta.type);
            iconWeights.push(iconMeta.weight);
            childIconFonts.push(extractIconFontFromAttrs(childAttrsValue));
            childIconSpacings.push(extractIconSpacingFromAttrs(childAttrsValue));
          });
        }

        if (icons.length === 0 && moduleAttrs.childrenAttrs) {
          var childAttrsList = moduleAttrs.childrenAttrs;
          if (Array.isArray(childAttrsList)) {
            childAttrsList.forEach(function(child) {
              var childAttrsValue = child && child.attrs ? child.attrs : child;
              var iconMeta = extractIconMetaFromAttrs(childAttrsValue);
              icons.push(iconMeta.char);
              iconTypes.push(iconMeta.type);
              iconWeights.push(iconMeta.weight);
              childIconFonts.push(extractIconFontFromAttrs(childAttrsValue));
              childIconSpacings.push(extractIconSpacingFromAttrs(childAttrsValue));
            });
          }
        }
      }

      // Fallback to block payloads exposed on the render param.
      if (icons.length === 0 && param.block) {
        try {
          var candidates = [];
          if (param.block.parsed_block && Array.isArray(param.block.parsed_block.innerBlocks)) {
            candidates = candidates.concat(param.block.parsed_block.innerBlocks);
          }
          if (Array.isArray(param.block.innerBlocks)) {
            candidates = candidates.concat(param.block.innerBlocks);
          }
          if (param.block.attributes && Array.isArray(param.block.attributes.innerBlocks)) {
            candidates = candidates.concat(param.block.attributes.innerBlocks);
          }

          if (candidates.length) {
            candidates.forEach(function(child) {
              if (child && child.blockName && child.blockName !== {$tab_slug_js}) return;
              var childAttrsValue = child.attrs || {};
              var iconMeta = extractIconMetaFromAttrs(childAttrsValue);
              icons.push(iconMeta.char);
              iconTypes.push(iconMeta.type);
              iconWeights.push(iconMeta.weight);
              childIconFonts.push(extractIconFontFromAttrs(childAttrsValue));
              childIconSpacings.push(extractIconSpacingFromAttrs(childAttrsValue));
            });
          }
        } catch (e) { /* ignore */ }
      }

      // Fallback to parent attrs when they carry child block data.
      if (icons.length === 0 && param.parentAttrs && param.parentAttrs.module && Array.isArray(param.parentAttrs.module.innerBlocks)) {
        param.parentAttrs.module.innerBlocks.forEach(function(child) {
          var childName = child.blockName || '';
          if (childName !== {$tab_slug_js}) return;
          var childAttrsValue = child.attrs || {};
          var iconMeta = extractIconMetaFromAttrs(childAttrsValue);
          icons.push(iconMeta.char);
          iconTypes.push(iconMeta.type);
          iconWeights.push(iconMeta.weight);
          childIconFonts.push(extractIconFontFromAttrs(childAttrsValue));
          childIconSpacings.push(extractIconSpacingFromAttrs(childAttrsValue));
        });
      }

      // Last resort: derive child ids from rendered tab panels.
      function deriveChildIdsFromRenderedDom(anchorEl, moduleId) {
        if (!anchorEl) return [];
        var root = anchorEl.closest ? anchorEl.closest('.et_pb_tabs') : null;
        if (!root && moduleId) {
          root = document.querySelector('[data-id="' + moduleId + '"]');
        }
        if (!root) return [];
        var tabDivs = Array.prototype.slice.call(root.querySelectorAll('.et_pb_all_tabs [data-id]'));
        var ids = [];
        tabDivs.forEach(function(div) {
          if (!div || !div.getAttribute) return;
          if (!div.classList || !div.classList.contains('et_pb_tab')) return;
          var id = div.getAttribute('data-id');
          if (id) ids.push(id);
        });
        return ids;
      }

      var moduleId = (param && (param.id || (param.block && (param.block.id || param.block.clientId)))) || '';

      function findTabsControls(anchorEl, moduleId) {
        if (!anchorEl) return null;

        var directTabs = anchorEl.closest ? anchorEl.closest('.et_pb_tabs') : null;
        if (directTabs) {
          var directUl = directTabs.querySelector('.et_pb_tabs_controls');
          if (directUl) return directUl;
        }

        var node = anchorEl.parentElement;
        var depth = 0;
        while (node && node !== document.body && depth < 12) {
          if (node.querySelector) {
            var scopedUl = node.querySelector('.et_pb_tabs_controls');
            if (scopedUl) return scopedUl;
          }
          node = node.parentElement;
          depth++;
        }

        if (moduleId) {
          var idBasedSelectors = [
            '[data-block-id="' + moduleId + '"] .et_pb_tabs_controls',
            '[data-id="' + moduleId + '"] .et_pb_tabs_controls',
            '#' + moduleId + ' .et_pb_tabs_controls'
          ];

          for (var i = 0; i < idBasedSelectors.length; i++) {
            var ul = document.querySelector(idBasedSelectors[i]);
            if (ul) return ul;
          }
        }

        return null;
      }

      function findObserverTarget(anchorEl, moduleId) {
        if (!anchorEl) return null;

        var node = anchorEl.parentElement;
        var depth = 0;
        while (node && node !== document.body && depth < 12) {
          if (node.querySelector && node.querySelector('.et_pb_tabs_controls')) {
            return node;
          }
          node = node.parentElement;
          depth++;
        }

        if (moduleId) {
          var hostSelectors = [
            '[data-block-id="' + moduleId + '"]',
            '[data-id="' + moduleId + '"]',
            '#' + moduleId
          ];

          for (var i = 0; i < hostSelectors.length; i++) {
            var host = document.querySelector(hostSelectors[i]);
            if (host) return host;
          }
        }

        return null;
      }

      function syncPreviewTabControlHeights(ul) {
        if (!ul || !ul.querySelectorAll) {
          return;
        }

        try {
          var items = Array.prototype.slice.call(ul.querySelectorAll('li'));
          if (!items.length) {
            return;
          }

          items.forEach(function(li) {
            if (li && li.style) {
              li.style.height = 'auto';
            }
          });

          var maxHeight = 0;
          items.forEach(function(li) {
            if (!li) {
              return;
            }

            var link = li.querySelector ? li.querySelector('a.et_pb_tab_nav_item_link, a') : null;
            var linkHeight = 0;
            if (link) {
              var rect = link.getBoundingClientRect ? link.getBoundingClientRect() : null;
              linkHeight = Math.max(link.scrollHeight || 0, rect && rect.height ? rect.height : 0);
            }

            var liRect = li.getBoundingClientRect ? li.getBoundingClientRect() : null;
            var liHeight = Math.max(li.scrollHeight || 0, liRect && liRect.height ? liRect.height : 0);
            maxHeight = Math.max(maxHeight, linkHeight, liHeight);
          });

          if (!maxHeight || !isFinite(maxHeight)) {
            return;
          }

          var normalizedHeight = Math.ceil(maxHeight);
          items.forEach(function(li) {
            if (li && li.style) {
              li.style.height = normalizedHeight + 'px';
            }
          });
        } catch (e) {
          // Preview layout can be mid-render.
        }
      }

      function ensureTabLinkAccessibleName(linkEl, tabIndex) {
        if (!linkEl || !linkEl.getAttribute || linkEl.getAttribute('aria-label') || linkEl.getAttribute('aria-labelledby')) {
          return;
        }

        var clone = linkEl.cloneNode ? linkEl.cloneNode(true) : null;
        if (clone && clone.querySelectorAll) {
          Array.prototype.slice.call(clone.querySelectorAll('.divibooster-tab-icon')).forEach(function(iconEl) {
            if (iconEl && iconEl.remove) {
              iconEl.remove();
            }
          });
        }

        var text = clone ? (clone.textContent || '') : (linkEl.textContent || '');
        if (String(text).trim() === '') {
          linkEl.setAttribute('aria-label', 'Tab ' + (tabIndex + 1));
        }
      }

      // === Icon Injector ===
      // Use a hidden React node to keep the rendered tab controls in sync.
      function IconInjector(props) {
        var ref = React.useRef(null);

        React.useEffect(function() {
          function injectIcons() {
            try {
              var el = ref.current;
              if (!el) return;

              var ul = findTabsControls(el, props.moduleId);
              if (!ul) return;

                // Add default preview styles once; module styles can override them.
                try {
                  var styleId = 'divibooster-tab-icon-defaults-builder';
                  if (!document.getElementById(styleId)) {
                    var st = document.createElement('style');
                    st.id = styleId;
                    st.textContent =
                      '.et_pb_tabs .et_pb_tabs_controls li a .divibooster-tab-icon{' +
                      'font-size:1em;line-height:1;display:inline-block;vertical-align:middle;margin-right:0.4em;' +
                      '}' +
                      '.et_pb_tabs .et_pb_tabs_controls li.divibooster-tab-has-icon>a{' +
                      'display:flex;align-items:center;height:100%;box-sizing:border-box;' +
                      '}' +
                      '.et_pb_tabs .et_pb_tabs_controls li a .divibooster-tab-icon:before{display:none;}';
                    document.head.appendChild(st);
                  }
                } catch (e) { /* ignore */ }

              // Prefer data written by child wrappers when it is available in the DOM.
              var derivedIcons = [];
              var derivedTypes = [];
              var derivedWeights = [];
              var derivedDetected = false;
              var effectiveChildFonts = Array.isArray(props.childIconFonts) ? props.childIconFonts.slice() : [];
              var effectiveChildSpacings = Array.isArray(props.childIconSpacings) ? props.childIconSpacings.slice() : [];
              var childFontById = {};
              var childSpacingById = {};
              if (ul && ul.closest) {
                try {
                  var tabsRoot = ul.closest('.et_pb_tabs');
                  if (tabsRoot) {
                    var tabPanels = Array.prototype.slice.call(tabsRoot.querySelectorAll('.et_pb_all_tabs .et_pb_tab[data-id]'));
                    if (tabPanels && tabPanels.length) {
                      function readIconFromTabPanel(panelEl) {
                        if (!panelEl) return '';

                        // Prefer the child writer's data attribute.
                        var direct = panelEl.getAttribute('data-divibooster-tab-icon');
                        if (direct) return normalizeIconValue(direct);

                        // Accept dataset-normalized values.
                        try {
                          var ds = panelEl.dataset || {};
                          if (ds && ds.diviboosterTabIcon) return normalizeIconValue(ds.diviboosterTabIcon);
                        } catch (e) {}

                        // Look for serialized attrs embedded in the panel.
                        var scripts = panelEl.querySelectorAll('script');
                        for (var s = 0; s < scripts.length; s++) {
                          var txt = scripts[s].textContent || '';
                          if (txt.indexOf({$icon_attr_js}) === -1) continue;
                          try {
                            // Prefer JSON when the marker is structured.
                            var obj = JSON.parse(txt);
                            if (obj && obj[{$icon_attr_js}]) return normalizeIconValue(obj[{$icon_attr_js}]);
                          } catch (e) {
                            // Fallback to a narrow attr-name match.
                            var m = txt.match(new RegExp('"' + {$icon_attr_js} + '"\\s*:\\s*"([^"]+)"'));
                            if (m && m[1]) return normalizeIconValue(m[1]);
                          }
                        }

                        return '';
                      }

                      function readIconTypeFromTabPanel(panelEl) {
                        if (!panelEl || !panelEl.getAttribute) return '';
                        var typeVal = panelEl.getAttribute('data-divibooster-tab-icon-type');
                        return typeof typeVal === 'string' ? typeVal : '';
                      }

                      function readIconWeightFromTabPanel(panelEl) {
                        if (!panelEl || !panelEl.getAttribute) return '';
                        var weightVal = panelEl.getAttribute('data-divibooster-tab-icon-weight');
                        return typeof weightVal === 'string' ? weightVal : '';
                      }

                      function readIconFontFromTabPanel(panelEl) {
                        if (!panelEl || !panelEl.getAttribute) return {};
                        var raw = panelEl.getAttribute('data-divibooster-tab-icon-font');
                        if (!raw || typeof raw !== 'string') return {};

                        try {
                          var decoded = decodeURIComponent(raw);
                          var parsed = JSON.parse(decoded);
                          return hasStyleKeys(parsed) ? parsed : {};
                        } catch (e) {
                          return {};
                        }
                      }

                      function readIconSpacingFromTabPanel(panelEl) {
                        if (!panelEl || !panelEl.getAttribute) return {};
                        var raw = panelEl.getAttribute('data-divibooster-tab-icon-spacing');
                        if (!raw || typeof raw !== 'string') return {};

                        try {
                          var decoded = decodeURIComponent(raw);
                          var parsed = JSON.parse(decoded);
                          return hasStyleKeys(parsed) ? parsed : {};
                        } catch (e) {
                          return {};
                        }
                      }

                          tabPanels.forEach(function(panelEl) {
                            var id = panelEl.getAttribute && panelEl.getAttribute('data-id');
                            var val = readIconFromTabPanel(panelEl);
                            var typeVal = readIconTypeFromTabPanel(panelEl);
                            var weightVal = readIconWeightFromTabPanel(panelEl);
                            var fontVal = readIconFontFromTabPanel(panelEl);
                            var spacingVal = readIconSpacingFromTabPanel(panelEl);
                            if (id && val) {
                              try {
                                window.__diviboosterTabIconsCache = window.__diviboosterTabIconsCache || {};
                                window.__diviboosterTabIconsCache[id] = val;
                              } catch (e) {}
                            } else if (id) {
                              try {
                                window.__diviboosterTabIconsCache = window.__diviboosterTabIconsCache || {};
                                delete window.__diviboosterTabIconsCache[id];
                              } catch (e) {}
                            }
                            if (id && typeVal) {
                              try {
                                window.__diviboosterTabIconTypesCache = window.__diviboosterTabIconTypesCache || {};
                                window.__diviboosterTabIconTypesCache[id] = typeVal;
                              } catch (e) {}
                            } else if (id) {
                              try {
                                window.__diviboosterTabIconTypesCache = window.__diviboosterTabIconTypesCache || {};
                                delete window.__diviboosterTabIconTypesCache[id];
                              } catch (e) {}
                            }
                            if (id && weightVal) {
                              try {
                                window.__diviboosterTabIconWeightsCache = window.__diviboosterTabIconWeightsCache || {};
                                window.__diviboosterTabIconWeightsCache[id] = weightVal;
                              } catch (e) {}
                            } else if (id) {
                              try {
                                window.__diviboosterTabIconWeightsCache = window.__diviboosterTabIconWeightsCache || {};
                                delete window.__diviboosterTabIconWeightsCache[id];
                              } catch (e) {}
                            }
                            if (id && hasStyleKeys(fontVal)) {
                              try {
                                window.__diviboosterTabIconFontCache = window.__diviboosterTabIconFontCache || {};
                                window.__diviboosterTabIconFontCache[id] = fontVal;
                              } catch (e) {}
                              childFontById[id] = fontVal;
                            } else if (id) {
                              try {
                                window.__diviboosterTabIconFontCache = window.__diviboosterTabIconFontCache || {};
                                delete window.__diviboosterTabIconFontCache[id];
                              } catch (e) {}
                            }
                            if (id && hasStyleKeys(spacingVal)) {
                              try {
                                window.__diviboosterTabIconSpacingCache = window.__diviboosterTabIconSpacingCache || {};
                                window.__diviboosterTabIconSpacingCache[id] = spacingVal;
                              } catch (e) {}
                              childSpacingById[id] = spacingVal;
                            } else if (id) {
                              try {
                                window.__diviboosterTabIconSpacingCache = window.__diviboosterTabIconSpacingCache || {};
                                delete window.__diviboosterTabIconSpacingCache[id];
                              } catch (e) {}
                            }
                            derivedIcons.push(val);
                            derivedTypes.push(typeVal);
                            derivedWeights.push(weightVal);
                          });

                      if (derivedIcons.some(function(v) { return v !== ''; })) {
                        // Keep render props immutable; merge derived values separately.
                        derivedDetected = true;
                      }
                    }
                  }
                } catch (e) {
                  // ignore
                }
              }

              // Prefer id-based matching over DOM order when possible.
              var iconById = {};
              var iconTypeById = {};
              var iconWeightById = {};
              var effectiveChildIds = Array.isArray(props.childIds) ? props.childIds.slice() : [];
              // Merge derived icons over prop icons.
              var effectiveIcons = [];
              var effectiveTypes = [];
              var effectiveWeights = [];
              try {
                var baseIcons = Array.isArray(props.icons) ? props.icons.slice() : (props.icons ? [props.icons] : []);
                if (derivedDetected && Array.isArray(derivedIcons)) {
                  var maxLen = Math.max(baseIcons.length, derivedIcons.length);
                  for (var __i = 0; __i < maxLen; __i++) {
                    var dval = derivedIcons[__i] || '';
                    var bval = baseIcons[__i] || '';
                    effectiveIcons[__i] = (dval && dval !== '') ? dval : bval;
                  }
                } else {
                  effectiveIcons = baseIcons;
                }
              } catch (e) {
                effectiveIcons = Array.isArray(props.icons) ? props.icons.slice() : [];
              }
              try {
                var baseTypes = Array.isArray(props.iconTypes) ? props.iconTypes.slice() : [];
                var baseWeights = Array.isArray(props.iconWeights) ? props.iconWeights.slice() : [];
                if (derivedDetected && Array.isArray(derivedIcons)) {
                  var metaMaxLen = Math.max(baseTypes.length, derivedTypes.length, baseWeights.length, derivedWeights.length);
                  for (var __m = 0; __m < metaMaxLen; __m++) {
                    effectiveTypes[__m] = (derivedTypes[__m] && derivedTypes[__m] !== '') ? derivedTypes[__m] : (baseTypes[__m] || '');
                    effectiveWeights[__m] = (derivedWeights[__m] && derivedWeights[__m] !== '') ? derivedWeights[__m] : (baseWeights[__m] || '');
                  }
                } else {
                  effectiveTypes = baseTypes;
                  effectiveWeights = baseWeights;
                }
              } catch (e) {
                effectiveTypes = Array.isArray(props.iconTypes) ? props.iconTypes.slice() : [];
                effectiveWeights = Array.isArray(props.iconWeights) ? props.iconWeights.slice() : [];
              }
              // Reuse per-child cache values for known ids.
              try {
                var cache = window.__diviboosterTabIconsCache || {};
                var typesCache = window.__diviboosterTabIconTypesCache || {};
                var weightsCache = window.__diviboosterTabIconWeightsCache || {};
                if (effectiveChildIds.length) {
                  effectiveChildIds.forEach(function(id, idx) {
                    if (!id) return;
                    if (cache[id]) {
                      iconById[id] = cache[id];
                    } else {
                      iconById[id] = effectiveIcons[idx] || '';
                    }
                    if (typesCache[id]) {
                      iconTypeById[id] = typesCache[id];
                    } else {
                      iconTypeById[id] = effectiveTypes[idx] || '';
                    }
                    if (weightsCache[id]) {
                      iconWeightById[id] = weightsCache[id];
                    } else {
                      iconWeightById[id] = effectiveWeights[idx] || '';
                    }
                  });
                }
              } catch (e) {
                // Fall back to index-based mapping.
                if (effectiveChildIds.length) {
                  effectiveChildIds.forEach(function(id, idx) {
                    if (!id) return;
                    iconById[id] = effectiveIcons[idx] || '';
                    iconTypeById[id] = effectiveTypes[idx] || '';
                    iconWeightById[id] = effectiveWeights[idx] || '';
                  });
                }
              }
              if (!effectiveChildIds.length) {
                effectiveChildIds = deriveChildIdsFromRenderedDom(el, props.moduleId);
              }
              if (effectiveChildIds.length) {
                effectiveChildIds.forEach(function(id, idx) {
                  if (!id) return;
                  if (!Object.prototype.hasOwnProperty.call(iconById, id)) {
                    iconById[id] = effectiveIcons[idx] || '';
                  }
                  if (!Object.prototype.hasOwnProperty.call(iconTypeById, id)) {
                    iconTypeById[id] = effectiveTypes[idx] || '';
                  }
                  if (!Object.prototype.hasOwnProperty.call(iconWeightById, id)) {
                    iconWeightById[id] = effectiveWeights[idx] || '';
                  }
                  if (!Object.prototype.hasOwnProperty.call(childFontById, id)) {
                    childFontById[id] = effectiveChildFonts[idx] || {};
                  }
                  if (!Object.prototype.hasOwnProperty.call(childSpacingById, id)) {
                    childSpacingById[id] = effectiveChildSpacings[idx] || {};
                  }
                });
              }

              try {
                var fontCache = window.__diviboosterTabIconFontCache || {};
                var spacingCache = window.__diviboosterTabIconSpacingCache || {};
                if (effectiveChildIds.length) {
                  effectiveChildIds.forEach(function(id) {
                    if (!id) return;
                    if (hasStyleKeys(fontCache[id])) {
                      childFontById[id] = fontCache[id];
                    }
                    if (hasStyleKeys(spacingCache[id])) {
                      childSpacingById[id] = spacingCache[id];
                    }
                  });
                }
              } catch (e) {
                // Cache is only a preview aid.
              }

              var lis = Array.prototype.slice.call(ul.querySelectorAll('li'));
              lis.forEach(function(li, idx) {
                var tabId = (li && li.getAttribute) ? li.getAttribute('data-id') : '';
                var iconCharRaw = '';
                if (tabId && Object.prototype.hasOwnProperty.call(iconById, tabId)) {
                  iconCharRaw = iconById[tabId] || '';
                } else {
                  iconCharRaw = effectiveIcons[idx] || '';
                }
                var iconTypeRaw = (tabId && Object.prototype.hasOwnProperty.call(iconTypeById, tabId))
                  ? (iconTypeById[tabId] || '')
                  : (effectiveTypes[idx] || '');
                var iconWeightRaw = (tabId && Object.prototype.hasOwnProperty.call(iconWeightById, tabId))
                  ? (iconWeightById[tabId] || '')
                  : (effectiveWeights[idx] || '');
                var childFontAttr = (tabId && Object.prototype.hasOwnProperty.call(childFontById, tabId))
                  ? (childFontById[tabId] || {})
                  : (effectiveChildFonts[idx] || {});
                var childSpacingAttr = (tabId && Object.prototype.hasOwnProperty.call(childSpacingById, tabId))
                  ? (childSpacingById[tabId] || {})
                  : (effectiveChildSpacings[idx] || {});
                var iconChar = (typeof normalizeIconValue === 'function') ? normalizeIconValue(iconCharRaw) : (typeof iconCharRaw === 'string' ? iconCharRaw : '');
                var isActiveTab = !!(li.classList && li.classList.contains('et_pb_tab_active'));

                var a = li.querySelector('a.et_pb_tab_nav_item_link, a');
                if (!a) return;

                // Keep each tab label to a single injected icon.
                var existing = a.querySelector('.divibooster-tab-icon');
                if (existing) {
                  if (iconChar === '') {
                    existing.remove();
                    try { li.classList.remove('divibooster-tab-has-icon'); } catch (e) {}
                    return;
                  }
                  // Refresh the existing icon in place.
                  try { li.classList.add('divibooster-tab-has-icon'); } catch (e) {}
                  existing.textContent = iconChar;
                  existing.setAttribute('data-icon', iconChar);
                  existing.setAttribute('aria-hidden', 'true');
                  clearPreviewActiveIconFontStyle(existing);
                  clearPreviewActiveIconSpacingStyle(existing);
                  applyPreviewIconFont(existing, iconChar, iconTypeRaw, iconWeightRaw);
                  applyPreviewChildFontStyle(existing, childFontAttr, iconChar, iconTypeRaw, iconWeightRaw);
                  applyPreviewChildSpacingStyle(existing, childSpacingAttr);
                  applyPreviewActiveIconFontStyle(existing, props.activeIconFont || {}, isActiveTab);
                  applyPreviewActiveIconSpacingStyle(existing, props.activeIconSpacing || {}, isActiveTab);
                  ensureTabLinkAccessibleName(a, idx);
                  try { if (tabId) { window.__diviboosterTabIconsCache = window.__diviboosterTabIconsCache || {}; window.__diviboosterTabIconsCache[tabId] = iconChar; } } catch (e) {}
                  return;
                }

                if (iconChar === '') {
                  try { li.classList.remove('divibooster-tab-has-icon'); } catch (e) {}
                  return;
                }

                // Insert the icon before the tab title content.
                try { li.classList.add('divibooster-tab-has-icon'); } catch (e) {}
                var span = document.createElement('span');
                span.className = 'et-pb-icon divibooster-tab-icon';
                span.textContent = iconChar;
                span.setAttribute('data-icon', iconChar);
                span.setAttribute('aria-hidden', 'true');
                clearPreviewActiveIconFontStyle(span);
                clearPreviewActiveIconSpacingStyle(span);
                applyPreviewIconFont(span, iconChar, iconTypeRaw, iconWeightRaw);
                applyPreviewChildFontStyle(span, childFontAttr, iconChar, iconTypeRaw, iconWeightRaw);
                applyPreviewChildSpacingStyle(span, childSpacingAttr);
                applyPreviewActiveIconFontStyle(span, props.activeIconFont || {}, isActiveTab);
                applyPreviewActiveIconSpacingStyle(span, props.activeIconSpacing || {}, isActiveTab);
                a.insertBefore(span, a.firstChild);
                ensureTabLinkAccessibleName(a, idx);
                try { if (tabId) { window.__diviboosterTabIconsCache = window.__diviboosterTabIconsCache || {}; window.__diviboosterTabIconsCache[tabId] = iconChar; } } catch (e) {}
              });

              syncPreviewTabControlHeights(ul);
              if (typeof window.requestAnimationFrame === 'function') {
                window.requestAnimationFrame(function() {
                  syncPreviewTabControlHeights(ul);
                });
              } else {
                setTimeout(function() {
                  syncPreviewTabControlHeights(ul);
                }, 0);
              }
            } catch (e) {
              // Keep preview rendering alive if injection fails.
            }
          }

          // Run once before observing later builder updates.
          injectIcons();

          // Re-inject after builder re-renders relevant tab markup.
          var el = ref.current;
          var observer = null;
          var debounceTimer = null;
          if (el) {
            // Observe the nearest stable host so child data changes are captured.
            var ulToObserve = findObserverTarget(el, props.moduleId) || findTabsControls(el, props.moduleId);
            if (ulToObserve) {
              observer = new MutationObserver(function(mutations) {
                if (debounceTimer) clearTimeout(debounceTimer);
                debounceTimer = setTimeout(function() {
                  try { injectIcons(); } catch (e) { /* ignore */ }
                }, 60);
              });
              try {
                observer.observe(ulToObserve, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'data-divibooster-tab-icon', 'data-divibooster-tab-icon-type', 'data-divibooster-tab-icon-weight', 'data-divibooster-tab-icon-font', 'data-divibooster-tab-icon-spacing'] });
              } catch (e) { /* ignore */ }
            }
          }

          return function() {
            if (observer) observer.disconnect();
            if (debounceTimer) clearTimeout(debounceTimer);
          };
  }, [JSON.stringify(props.icons), JSON.stringify(props.iconTypes || []), JSON.stringify(props.iconWeights || []), JSON.stringify(props.childIconFonts || []), JSON.stringify(props.childIconSpacings || []), JSON.stringify(props.activeIconFont || {}), JSON.stringify(props.activeIconSpacing || {}), props.moduleId, JSON.stringify(props.childIds || [])]);

        return WPElement.createElement('div', {
          key: 'divibooster-tab-icon-injector',
          ref: ref,
          'aria-hidden': true,
          style: { display: 'none' }
        });
      }

      var activeRawIconFontAttr = previewModuleAttrs ? previewModuleAttrs[{$active_icon_font_attr_js}] : null;
      if (!activeRawIconFontAttr && previewModuleAttrs && previewModuleAttrs.module && previewModuleAttrs.module[{$active_icon_font_attr_js}]) {
        activeRawIconFontAttr = previewModuleAttrs.module[{$active_icon_font_attr_js}];
      }
      var activeIconFontAttr = extractPreviewFontAttr(activeRawIconFontAttr);
      var activeRawIconSpacingAttr = previewModuleAttrs ? previewModuleAttrs[{$active_icon_spacing_attr_js}] : null;
      if (!activeRawIconSpacingAttr && previewModuleAttrs && previewModuleAttrs.module && previewModuleAttrs.module[{$active_icon_spacing_attr_js}]) {
        activeRawIconSpacingAttr = previewModuleAttrs.module[{$active_icon_spacing_attr_js}];
      }
      var activeIconSpacingAttr = extractPreviewSpacingAttr(activeRawIconSpacingAttr);

      var injectorEl = WPElement.createElement(IconInjector, {
        key: 'divibooster-tab-icons',
        icons: icons,
  iconTypes: iconTypes,
  iconWeights: iconWeights,
  childIconFonts: childIconFonts,
  childIconSpacings: childIconSpacings,
  activeIconFont: activeIconFontAttr,
  activeIconSpacing: activeIconSpacingAttr,
  moduleId: moduleId,
  childIds: childIds
      });
      var previewStylesEl = null;
      var previewChildStylesEl = null;
      try {
        var previewRawFontAttr = previewModuleAttrs ? previewModuleAttrs[{$icon_font_attr_js}] : null;
        if (!previewRawFontAttr && previewModuleAttrs && previewModuleAttrs.module && previewModuleAttrs.module[{$icon_font_attr_js}]) {
          previewRawFontAttr = previewModuleAttrs.module[{$icon_font_attr_js}];
        }
        var previewFontAttr = extractPreviewFontAttr(previewRawFontAttr);
        var previewRawSpacingAttr = previewModuleAttrs ? previewModuleAttrs[{$icon_spacing_attr_js}] : null;
        if (!previewRawSpacingAttr && previewModuleAttrs && previewModuleAttrs.module && previewModuleAttrs.module[{$icon_spacing_attr_js}]) {
          previewRawSpacingAttr = previewModuleAttrs.module[{$icon_spacing_attr_js}];
        }
        var previewSpacingAttr = extractPreviewSpacingAttr(previewRawSpacingAttr);
        var StyleContainer = window.divi && window.divi.module && window.divi.module.StyleContainer;
        var orderClass = moduleElements && moduleElements.orderClass ? moduleElements.orderClass : '';

        if (
          StyleContainer &&
          moduleElements &&
          typeof moduleElements.style === 'function' &&
          typeof orderClass === 'string' &&
          orderClass
        ) {
          var isEdited = moduleWrapper.props && moduleWrapper.props.isEdited;
          if (previewFontAttr && Object.keys(previewFontAttr).length) {
            var parentAdvancedStyles = [
              {
                componentName: 'divi/font',
                props: {
                  selector: orderClass + ' .et_pb_tabs_controls li a .divibooster-tab-icon',
                  attr: previewFontAttr
                }
              }
            ];
            if (previewSpacingAttr && Object.keys(previewSpacingAttr).length) {
              parentAdvancedStyles.push({
                componentName: 'divi/spacing',
                props: {
                  selector: orderClass + ' .et_pb_tabs_controls li a .divibooster-tab-icon',
                  attr: previewSpacingAttr
                }
              });
            }
            previewStylesEl = WPElement.createElement(
              StyleContainer,
              {
                key: 'divibooster-tab-icon-preview-font-styles',
                mode: 'builder',
                state: isEdited ? moduleState : '',
                noStyleTag: false
              },
              moduleElements.style({
                attrName: 'module',
                styleProps: {
                  advancedStyles: parentAdvancedStyles
                }
              })
            );
          } else if (previewSpacingAttr && Object.keys(previewSpacingAttr).length) {
            previewStylesEl = WPElement.createElement(
              StyleContainer,
              {
                key: 'divibooster-tab-icon-preview-spacing-styles',
                mode: 'builder',
                state: isEdited ? moduleState : '',
                noStyleTag: false
              },
              moduleElements.style({
                attrName: 'module',
                styleProps: {
                  advancedStyles: [
                    {
                      componentName: 'divi/spacing',
                      props: {
                        selector: orderClass + ' .et_pb_tabs_controls li a .divibooster-tab-icon',
                        attr: previewSpacingAttr
                      }
                    }
                  ]
                }
              })
            );
          }

          var childAdvancedStyles = [];
          if (Array.isArray(childIconFonts)) {
            childIconFonts.forEach(function(fontAttr, idx) {
              if (!fontAttr || !Object.keys(fontAttr).length) {
                return;
              }

              var childId = Array.isArray(childIds) && childIds[idx] ? childIds[idx] : '';
              var selector = childId
                ? orderClass + ' .et_pb_tabs_controls li[data-id="' + childId + '"] a .divibooster-tab-icon'
                : orderClass + ' .et_pb_tabs_controls li:nth-child(' + (idx + 1) + ') a .divibooster-tab-icon';
              var childIconChar = Array.isArray(icons) ? (icons[idx] || '') : '';
              var childIconType = Array.isArray(iconTypes) ? (iconTypes[idx] || '') : '';
              var childIconWeight = Array.isArray(iconWeights) ? (iconWeights[idx] || '') : '';
              var sanitizedFontAttr = sanitizePreviewFontAttrForIcon(fontAttr, childIconChar, childIconType, childIconWeight);

              childAdvancedStyles.push({
                componentName: 'divi/font',
                props: {
                  selector: selector,
                  attr: sanitizedFontAttr,
                }
              });
            });
          }
          if (Array.isArray(childIconSpacings)) {
            childIconSpacings.forEach(function(spacingAttr, idx) {
              if (!spacingAttr || !Object.keys(spacingAttr).length) {
                return;
              }

              var childId = Array.isArray(childIds) && childIds[idx] ? childIds[idx] : '';
              var selector = childId
                ? orderClass + ' .et_pb_tabs_controls li[data-id="' + childId + '"] a .divibooster-tab-icon'
                : orderClass + ' .et_pb_tabs_controls li:nth-child(' + (idx + 1) + ') a .divibooster-tab-icon';

              childAdvancedStyles.push({
                componentName: 'divi/spacing',
                props: {
                  selector: selector,
                  attr: spacingAttr,
                }
              });
            });
          }
          if (activeIconFontAttr && Object.keys(activeIconFontAttr).length) {
            childAdvancedStyles.push({
              componentName: 'divi/font',
              props: {
                selector: orderClass + ' .et_pb_tabs_controls li.et_pb_tab_active a .divibooster-tab-icon',
                attr: activeIconFontAttr,
              }
            });
          }
          if (activeIconSpacingAttr && Object.keys(activeIconSpacingAttr).length) {
            childAdvancedStyles.push({
              componentName: 'divi/spacing',
              props: {
                selector: orderClass + ' .et_pb_tabs_controls li.et_pb_tab_active a .divibooster-tab-icon',
                attr: activeIconSpacingAttr,
              }
            });
          }

          if (childAdvancedStyles.length) {
            previewChildStylesEl = WPElement.createElement(
              StyleContainer,
              {
                key: 'divibooster-tab-icon-preview-child-font-styles',
                mode: 'builder',
                state: isEdited ? moduleState : '',
                noStyleTag: false
              },
              moduleElements.style({
                attrName: 'module',
                styleProps: {
                  advancedStyles: childAdvancedStyles,
                },
              })
            );
          }
        }
      } catch (e) { /* ignore */ }

      // Normalize children before appending preview helpers.
      var existingChildren = moduleWrapper.props.children;
      if (!Array.isArray(existingChildren)) {
        existingChildren = existingChildren ? [existingChildren] : [];
      }
      var appendedChildren = [].concat(existingChildren, [injectorEl]);
      if (previewStylesEl) {
        appendedChildren = appendedChildren.concat([previewStylesEl]);
      }
      if (previewChildStylesEl) {
        appendedChildren = appendedChildren.concat([previewChildStylesEl]);
      }

      return WPElement.cloneElement(
        moduleWrapper,
        {},
        appendedChildren
      );
    } catch (err) {
      return moduleWrapper;
    }
  });

  // === Builder Child Data Bridge ===
  // Copy child icon data onto its rendered tab panel for the parent preview injector.
  addFilter('divi.module.wrapper.render', 'divibooster', function(moduleWrapper, param) {
    try {
      var moduleName = param && param.name ? param.name : null;
      if (moduleName !== {$tab_slug_js}) {
        return moduleWrapper;
      }

      var WPElement = window.vendor && window.vendor.wp && window.vendor.wp.element;
      var React = (window.vendor && window.vendor.React) || WPElement;
      if (!React || !WPElement || typeof React.useEffect !== 'function' || typeof React.useRef !== 'function') {
        return moduleWrapper;
      }

      // Keep the child writer resilient if helper scope changes.
      function minimalNormalize(value) {
        if (value === null || value === undefined) return '';
        if (typeof value === 'string') return value;
        if (typeof value === 'number' && isFinite(value)) {
          try { return String.fromCodePoint(value); } catch (e) { return ''; }
        }
        if (typeof value === 'object') {
          if (value.unicode) return String(value.unicode);
          if (value.value) return String(value.value);
          if (value.desktop && value.desktop.value) return String(value.desktop.value);
          if (value.innerContent && value.innerContent.desktop && value.innerContent.desktop.value) return String(value.innerContent.desktop.value);
          if (value.innerContent && value.innerContent.value) return String(value.innerContent.value);
        }
        return '';
      }

      function resolveIconFromTabAttrs(attrs) {
        if (!attrs) return '';
        var raw = attrs[{$icon_attr_js}];
        var val = (typeof normalizeIconValue === 'function') ? normalizeIconValue(raw) : minimalNormalize(raw);
        if (typeof val !== 'string') return '';
        return val.trim();
      }

      function resolveIconMetaProp(value, propName) {
        if (value === null || value === undefined) return '';
        if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return '';

        if (Array.isArray(value)) {
          for (var i = 0; i < value.length; i++) {
            var arrProp = resolveIconMetaProp(value[i], propName);
            if (arrProp !== '') return arrProp;
          }
          return '';
        }

        if (typeof value === 'object') {
          if (Object.prototype.hasOwnProperty.call(value, propName)) {
            var direct = value[propName];
            if (direct !== null && direct !== undefined) {
              var asString = String(direct).trim();
              if (asString !== '') return asString;
            }
          }

          var preferredKeys = ['value', 'desktop', 'tablet', 'phone', 'innerContent', 'iconPicker'];
          for (var j = 0; j < preferredKeys.length; j++) {
            var key = preferredKeys[j];
            if (Object.prototype.hasOwnProperty.call(value, key)) {
              var preferredProp = resolveIconMetaProp(value[key], propName);
              if (preferredProp !== '') return preferredProp;
            }
          }

          var keys = Object.keys(value);
          for (var k = 0; k < keys.length; k++) {
            var nestedProp = resolveIconMetaProp(value[keys[k]], propName);
            if (nestedProp !== '') return nestedProp;
          }
        }

        return '';
      }

      function resolveIconTypeFromTabAttrs(attrs) {
        if (!attrs) return '';
        return resolveIconMetaProp(attrs[{$icon_attr_js}], 'type');
      }

      function resolveIconWeightFromTabAttrs(attrs) {
        if (!attrs) return '';
        return resolveIconMetaProp(attrs[{$icon_attr_js}], 'weight');
      }

      function stripFontFamilyFromStyleAttrForChild(styleAttr) {
        if (!styleAttr || typeof styleAttr !== 'object') return styleAttr;
        var sanitized = styleAttr;
        try {
          sanitized = JSON.parse(JSON.stringify(styleAttr));
        } catch (e) {
          sanitized = Object.assign({}, styleAttr);
        }
        var fontAttr = sanitized && sanitized.font;
        if (!fontAttr || typeof fontAttr !== 'object') {
          return sanitized;
        }
        Object.keys(fontAttr).forEach(function(breakpoint) {
          var states = fontAttr[breakpoint];
          if (!states || typeof states !== 'object') return;
          Object.keys(states).forEach(function(state) {
            var stateValue = states[state];
            if (!stateValue || typeof stateValue !== 'object') {
              return;
            }
            ['family', 'weight', 'style', 'letterSpacing', 'lineHeight'].forEach(function(fieldName) {
              if (Object.prototype.hasOwnProperty.call(stateValue, fieldName)) {
                delete stateValue[fieldName];
              }
            });
          });
        });
        return sanitized;
      }

      function extractPreviewFontAttrForChild(rawAttr) {
        if (!rawAttr || typeof rawAttr !== 'object') return {};
        if (rawAttr.decoration && rawAttr.decoration.font && typeof rawAttr.decoration.font === 'object') {
          return stripFontFamilyFromStyleAttrForChild(rawAttr.decoration.font);
        }
        if (rawAttr.font || rawAttr.textShadow) {
          return stripFontFamilyFromStyleAttrForChild(rawAttr);
        }
        if (rawAttr.value && typeof rawAttr.value === 'object' && (rawAttr.value.font || rawAttr.value.textShadow)) {
          return stripFontFamilyFromStyleAttrForChild(rawAttr.value);
        }
        return {};
      }

      function resolveIconFontFromTabAttrs(attrs) {
        if (!attrs || typeof attrs !== 'object') return {};
        var raw = attrs[{$icon_font_attr_js}];
        if (!raw && attrs.module && typeof attrs.module === 'object') {
          raw = attrs.module[{$icon_font_attr_js}];
        }
        return extractPreviewFontAttrForChild(raw);
      }

      function stripSpacingMarginFromStyleAttrForChild(spacingAttr) {
        if (!spacingAttr || typeof spacingAttr !== 'object') return spacingAttr;
        var sanitized = spacingAttr;
        try {
          sanitized = JSON.parse(JSON.stringify(spacingAttr));
        } catch (e) {
          sanitized = Object.assign({}, spacingAttr);
        }
        ['desktop', 'tablet', 'phone'].forEach(function(breakpoint) {
          var states = sanitized[breakpoint];
          if (!states || typeof states !== 'object') return;
          Object.keys(states).forEach(function(state) {
            var stateValue = states[state];
            if (!stateValue || typeof stateValue !== 'object') return;
            if (Object.prototype.hasOwnProperty.call(stateValue, 'margin')) {
              delete stateValue.margin;
            }
          });
        });
        return sanitized;
      }

      function extractPreviewSpacingAttrForChild(rawAttr) {
        if (!rawAttr || typeof rawAttr !== 'object') return {};
        if (rawAttr.decoration && rawAttr.decoration.spacing && typeof rawAttr.decoration.spacing === 'object') {
          return stripSpacingMarginFromStyleAttrForChild(rawAttr.decoration.spacing);
        }
        if (rawAttr.desktop || rawAttr.tablet || rawAttr.phone) {
          return stripSpacingMarginFromStyleAttrForChild(rawAttr);
        }
        if (rawAttr.spacing && typeof rawAttr.spacing === 'object') {
          return stripSpacingMarginFromStyleAttrForChild(rawAttr.spacing);
        }
        if (rawAttr.value && typeof rawAttr.value === 'object') {
          if (rawAttr.value.spacing && typeof rawAttr.value.spacing === 'object') {
            return stripSpacingMarginFromStyleAttrForChild(rawAttr.value.spacing);
          }
          if (rawAttr.value.desktop || rawAttr.value.tablet || rawAttr.value.phone) {
            return stripSpacingMarginFromStyleAttrForChild(rawAttr.value);
          }
        }
        return {};
      }

      function resolveIconSpacingFromTabAttrs(attrs) {
        if (!attrs || typeof attrs !== 'object') return {};
        var raw = attrs[{$icon_spacing_attr_js}];
        if (!raw && attrs.module && typeof attrs.module === 'object') {
          raw = attrs.module[{$icon_spacing_attr_js}];
        }
        return extractPreviewSpacingAttrForChild(raw);
      }

      function IconAttrWriter(props) {
        var ref = React.useRef(null);

        React.useEffect(function() {
          try {
            var el = ref.current;
            if (!el) return;

            // Find the rendered tab panel wrapper.
            var tabEl = el.closest ? el.closest('.et_pb_tab') : null;
            if (!tabEl) {
              // Fallback to a short parent walk.
              tabEl = el.parentElement;
              var depth = 0;
              while (tabEl && depth < 8 && !(tabEl.classList && tabEl.classList.contains('et_pb_tab'))) {
                tabEl = tabEl.parentElement;
                depth++;
              }
            }
            if (!tabEl || !tabEl.setAttribute) return;

            var iconRaw = props.icon || '';
            var icon = (typeof normalizeIconValue === 'function') ? normalizeIconValue(iconRaw) : (typeof iconRaw === 'string' ? iconRaw : '');
            if (icon) {
              tabEl.setAttribute('data-divibooster-tab-icon', icon);
            } else {
              tabEl.removeAttribute('data-divibooster-tab-icon');
            }
            if (props.iconType) {
              tabEl.setAttribute('data-divibooster-tab-icon-type', props.iconType);
            } else {
              tabEl.removeAttribute('data-divibooster-tab-icon-type');
            }
            if (props.iconWeight) {
              tabEl.setAttribute('data-divibooster-tab-icon-weight', props.iconWeight);
            } else {
              tabEl.removeAttribute('data-divibooster-tab-icon-weight');
            }
            var fontAttr = props.iconFont && typeof props.iconFont === 'object' ? props.iconFont : {};
            if (Object.keys(fontAttr).length) {
              try {
                tabEl.setAttribute('data-divibooster-tab-icon-font', encodeURIComponent(JSON.stringify(fontAttr)));
              } catch (e) {
                tabEl.removeAttribute('data-divibooster-tab-icon-font');
              }
            } else {
              tabEl.removeAttribute('data-divibooster-tab-icon-font');
            }
            var spacingAttr = props.iconSpacing && typeof props.iconSpacing === 'object' ? props.iconSpacing : {};
            if (Object.keys(spacingAttr).length) {
              try {
                tabEl.setAttribute('data-divibooster-tab-icon-spacing', encodeURIComponent(JSON.stringify(spacingAttr)));
              } catch (e) {
                tabEl.removeAttribute('data-divibooster-tab-icon-spacing');
              }
            } else {
              tabEl.removeAttribute('data-divibooster-tab-icon-spacing');
            }
          } catch (e) {
            // ignore
          }
        }, [props.icon, props.iconType, props.iconWeight, JSON.stringify(props.iconFont || {}), JSON.stringify(props.iconSpacing || {})]);

        return WPElement.createElement('div', { ref: ref, 'aria-hidden': true, style: { display: 'none' } });
      }

      var iconValue = resolveIconFromTabAttrs(param && param.attrs ? param.attrs : null);
      var iconTypeValue = resolveIconTypeFromTabAttrs(param && param.attrs ? param.attrs : null);
      var iconWeightValue = resolveIconWeightFromTabAttrs(param && param.attrs ? param.attrs : null);
      var iconFontValue = resolveIconFontFromTabAttrs(param && param.attrs ? param.attrs : null);
      var iconSpacingValue = resolveIconSpacingFromTabAttrs(param && param.attrs ? param.attrs : null);
      var writerEl = WPElement.createElement(IconAttrWriter, {
        key: 'divibooster-tab-icon-writer',
        icon: iconValue,
        iconType: iconTypeValue,
        iconWeight: iconWeightValue,
        iconFont: iconFontValue,
        iconSpacing: iconSpacingValue
      });

      var existingChildren = moduleWrapper.props.children;
      if (!Array.isArray(existingChildren)) {
        existingChildren = existingChildren ? [existingChildren] : [];
      }

      return WPElement.cloneElement(
        moduleWrapper,
        {},
        [].concat(existingChildren, [writerEl])
      );
    } catch (e) {
      return moduleWrapper;
    }
  });
})();
JS;
}

// === Divi 5: Attribute Registration ===
add_filter('block_type_metadata_settings', __NAMESPACE__ . '\register_divi5_attributes', 10, 1);

function register_divi5_attributes($settings) {
    if (!is_array($settings)) {
        return $settings;
    }

    $module_name       = $settings['name'] ?? '';
    $module_attributes = $settings['attributes'] ?? false;

    if (!$module_name || !is_array($module_attributes)) {
        return $settings;
    }

    // Parent tabs own shared and active icon design attrs.
    if ($module_name === DIVI5_TABS_SLUG) {
        $settings['attributes'][DIVI5_ICON_FONT_ATTR] = get_divi5_tabs_icon_font_attribute_definition();
        $settings['attributes'][DIVI5_ACTIVE_ICON_FONT_ATTR] = get_divi5_tabs_active_icon_font_attribute_definition();
        $settings['attributes'][DIVI5_ICON_SPACING_ATTR] = get_divi5_tabs_icon_spacing_attribute_definition();
        $settings['attributes'][DIVI5_ACTIVE_ICON_SPACING_ATTR] = get_divi5_tabs_active_icon_spacing_attribute_definition();
    }

    // Child tabs own per-tab content and design attrs.
    if ($module_name === DIVI5_TAB_SLUG) {
        $settings['attributes'][DIVI5_ICON_ATTR] = get_divi5_tab_icon_attribute_definition();
        $settings['attributes'][DIVI5_ICON_FONT_ATTR] = get_divi5_tab_item_icon_font_attribute_definition();
        $settings['attributes'][DIVI5_ICON_SPACING_ATTR] = get_divi5_tab_item_icon_spacing_attribute_definition();
    }

    return $settings;
}

function get_divi5_tab_icon_attribute_definition() {
    return [
        'type'     => 'object',
        'settings' => [
            'innerContent' => [
                'groupType' => 'group-items',
                'items'     => [
                    'dbTabIconPicker' => [
                        'groupSlug'   => DIVI5_ICON_GROUP_SLUG,
                        'attrName'    => DIVI5_ICON_ATTR,
                        'label'       => 'Tab Icon',
                        'description' => 'Choose an icon to display in the tab label next to the tab title.'
                            . get_divi5_tab_icon_documentation_link('https://divibooster.com/add-icons-to-the-divi-tabs-module-tab-titles/'),
                        'features'    => [
                            'hover'      => false,
                            'sticky'     => false,
                            'responsive' => false,
                            'preset'     => 'content',
                        ],
                        'render'      => true,
                        'priority'    => 20,
            'component'   => [
              'type' => 'field',
              'name' => 'divi/icon-picker',
            ],
                        'defaultAttr' => [ 'desktop' => [ 'value' => '' ] ],
                    ],
                ],
            ],
        ],
    ];
}

function get_divi5_tab_icon_documentation_link($documentation_url) {
    $helper = '\\DiviBooster\\DiviTabsModuleTabIcons\\Branding\\get_description_documentation_link';

    if (function_exists($helper)) {
        return $helper($documentation_url);
    }

    return '';
}

function get_divi5_tabs_icon_font_attribute_definition() {
    return get_divi5_icon_font_attribute_definition_for_selector('{{selector}} .et_pb_tabs_controls li a .divibooster-tab-icon');
}

function get_divi5_tabs_active_icon_font_attribute_definition() {
    return get_divi5_icon_font_attribute_definition_for_selector(
        '{{selector}} .et_pb_tabs_controls li.et_pb_tab_active a .divibooster-tab-icon',
        DIVI5_ACTIVE_ICON_FONT_ATTR,
        DIVI5_ACTIVE_ICON_DESIGN_GROUP_SLUG
    );
}

function get_divi5_tab_item_icon_font_attribute_definition() {
    return get_divi5_icon_font_attribute_definition_for_selector('{{selector}} .divibooster-tab-icon');
}

function get_divi5_icon_font_attribute_definition_for_selector($selector, $attr_name = DIVI5_ICON_FONT_ATTR, $group_slug = DIVI5_ICON_DESIGN_GROUP_SLUG) {
    return [
        'type'      => 'object',
        'selector'  => $selector,
        'styleProps'=> [
            'font' => [
                'selector' => $selector,
            ],
        ],
        'settings'  => [
            'decoration' => [
                'font' => [
                    'groupType' => 'group-item',
                    'item'      => [
                        'groupSlug' => $group_slug,
                        'attrName'  => $attr_name,
                        'priority'  => 10,
                        'render'    => true,
                        'component' => [
                            'name' => 'divi/font',
                            'type' => 'group',
                            'props'=> [
                                'grouped' => false,
                                'fieldLabel' => 'Icon',
                                'fields'  => [
                                    'family' => [
                                        'render' => false,
                                    ],
                                    'weight' => [
                                        'render' => false,
                                    ],
                                    'style' => [
                                        'render' => false,
                                    ],
                                    'letterSpacing' => [
                                        'render' => false,
                                    ],
                                    'lineHeight' => [
                                        'render' => false,
                                    ],
                                    'textAlign' => [
                                        'render' => false,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ];
}

function get_divi5_tabs_icon_spacing_attribute_definition() {
    return get_divi5_icon_spacing_attribute_definition_for_selector('{{selector}} .et_pb_tabs_controls li a .divibooster-tab-icon');
}

function get_divi5_tabs_active_icon_spacing_attribute_definition() {
    return get_divi5_icon_spacing_attribute_definition_for_selector(
        '{{selector}} .et_pb_tabs_controls li.et_pb_tab_active a .divibooster-tab-icon',
        DIVI5_ACTIVE_ICON_SPACING_ATTR,
        DIVI5_ACTIVE_ICON_DESIGN_GROUP_SLUG
    );
}

function get_divi5_tab_item_icon_spacing_attribute_definition() {
    return get_divi5_icon_spacing_attribute_definition_for_selector('{{selector}} .divibooster-tab-icon');
}

function get_divi5_icon_spacing_attribute_definition_for_selector($selector, $attr_name = DIVI5_ICON_SPACING_ATTR, $group_slug = DIVI5_ICON_DESIGN_GROUP_SLUG) {
    return [
        'type'       => 'object',
        'selector'   => $selector,
        'styleProps' => [
            'spacing' => [
                'selector' => $selector,
            ],
        ],
        'settings'   => [
            'decoration' => [
                'spacing' => [
                    'groupType' => 'group-item',
                    'item'      => [
                        'groupSlug' => $group_slug,
                        'attrName'  => $attr_name,
                        'priority'  => 20,
                        'render'    => true,
                        'component' => [
                            'name'  => 'divi/spacing',
                            'type'  => 'group',
                            'props' => [
                                'grouped'    => false,
                                'fieldLabel' => 'Icon',
                                'groupLabel' => 'Icon Padding',
                                'fields'     => [
                                    'margin'  => [
                                        'render' => false,
                                    ],
                                    'padding' => [
                                        'render' => true,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ];
}

// === Divi 5: Frontend Output ===
add_filter('render_block_' . DIVI5_TABS_SLUG, __NAMESPACE__ . '\render_divi5_tabs_with_icons', 12, 3);

// === Divi 5: Builder Child Markup Bridge ===
// Print selected child icon data where the parent-side preview injector can read it.
add_filter('render_block_' . DIVI5_TAB_SLUG, __NAMESPACE__ . '\render_divi5_child_tab_expose_icon_to_builder', 12, 3);

function render_divi5_child_tab_expose_icon_to_builder($block_content, $parsed_block, $block) {
  if (!is_string($block_content) || $block_content === '') {
    return $block_content;
  }

  if (!is_array($parsed_block)) {
    return $block_content;
  }

  // Keep the data bridge out of saved/frontend markup.
  if (!is_divi_visual_builder_enabled()) {
    return $block_content;
  }

  $attrs = isset($parsed_block['attrs']) && is_array($parsed_block['attrs']) ? $parsed_block['attrs'] : [];
  $icon_val = '';

  // Accept the known icon picker storage shapes.
  $icon_attr = $attrs[DIVI5_ICON_ATTR] ?? null;
  if (is_array($icon_attr)) {
    foreach (
      [
        ['desktop','value'],
        ['value'],
        ['innerContent','desktop','value'],
        ['innerContent','value'],
        ['innerContent','iconPicker','desktop','value'],
        ['innerContent','iconPicker','value'],
      ] as $path
    ) {
      $candidate = $icon_attr;
      foreach ($path as $key) {
        if (is_array($candidate) && array_key_exists($key, $candidate)) {
          $candidate = $candidate[$key];
        } else {
          $candidate = null;
          break;
        }
      }
      if (is_string($candidate) && trim($candidate) !== '') {
        $icon_val = $candidate;
        break;
      }
    }
  } elseif (is_string($icon_attr)) {
    $icon_val = $icon_attr;
  }

  if (!is_string($icon_val) || trim($icon_val) === '') {
    return $block_content;
  }

  // Store the actual glyph character in the DOM bridge.
  $icon_char = html_entity_decode(trim($icon_val), ENT_QUOTES, 'UTF-8');
  $icon_type = extract_divi5_icon_type($icon_attr);
  $icon_weight = extract_divi5_icon_weight($icon_attr);
  $module_attrs = isset($attrs['module']) && is_array($attrs['module']) ? $attrs['module'] : [];
  $raw_font_attr = $attrs[DIVI5_ICON_FONT_ATTR] ?? ($module_attrs[DIVI5_ICON_FONT_ATTR] ?? null);
  $font_style_attr = extract_divi5_tab_icon_font_style_attr($raw_font_attr);
  $raw_spacing_attr = $attrs[DIVI5_ICON_SPACING_ATTR] ?? ($module_attrs[DIVI5_ICON_SPACING_ATTR] ?? null);
  $spacing_style_attr = extract_divi5_tab_icon_spacing_style_attr($raw_spacing_attr);
  $type_attr_fragment = $icon_type !== '' ? ' data-divibooster-tab-icon-type="' . esc_attr($icon_type) . '"' : '';
  $weight_attr_fragment = $icon_weight !== '' ? ' data-divibooster-tab-icon-weight="' . esc_attr($icon_weight) . '"' : '';
  $font_attr_fragment = '';
  if (!empty($font_style_attr)) {
    $font_json = wp_json_encode($font_style_attr);
    if (is_string($font_json) && $font_json !== '') {
      $font_attr_fragment = ' data-divibooster-tab-icon-font="' . esc_attr(rawurlencode($font_json)) . '"';
    }
  }
  $spacing_attr_fragment = '';
  if (!empty($spacing_style_attr)) {
    $spacing_json = wp_json_encode($spacing_style_attr);
    if (is_string($spacing_json) && $spacing_json !== '') {
      $spacing_attr_fragment = ' data-divibooster-tab-icon-spacing="' . esc_attr(rawurlencode($spacing_json)) . '"';
    }
  }

  // Attach bridge data to the rendered tab panel.
  $pattern = '/(<div\b[^>]*\bclass="[^"]*\bet_pb_tab\b[^"]*"[^>]*)(>)/i';
  if (!preg_match($pattern, $block_content)) {
    return $block_content;
  }

  $icon_attr_fragment = ' data-divibooster-tab-icon="' . esc_attr($icon_char) . '"' . $type_attr_fragment . $weight_attr_fragment . $font_attr_fragment . $spacing_attr_fragment;
  $updated_content = preg_replace_callback(
    $pattern,
    function($matches) use ($icon_attr_fragment) {
      return $matches[1] . $icon_attr_fragment . $matches[2];
    },
    $block_content,
    1
  );
  if (!is_string($updated_content)) {
    return $block_content;
  }

  return $updated_content;
}

function normalize_divi5_icon_char($icon_val) {
    if (!is_string($icon_val)) {
        return '';
    }

    $icon_val = trim($icon_val);
    if ($icon_val === '' || strpos($icon_val, '$variable(') === 0) {
        return '';
    }

    $decoded = html_entity_decode($icon_val, ENT_QUOTES, 'UTF-8');
    $icon_val = is_string($decoded) ? trim($decoded) : $icon_val;

    if (preg_match('/^\\\\u([0-9a-fA-F]{4,6})$/', $icon_val, $m) ||
        preg_match('/^U\+([0-9a-fA-F]{4,6})$/', $icon_val, $m) ||
        preg_match('/^0x([0-9a-fA-F]{4,6})$/', $icon_val, $m) ||
        preg_match('/^([0-9a-fA-F]{4,6})$/', $icon_val, $m)) {
        $entity = '&#x' . $m[1] . ';';
        $char = html_entity_decode($entity, ENT_QUOTES, 'UTF-8');
        return is_string($char) ? $char : '';
    }

    return $icon_val;
}

function extract_divi5_icon_char($icon_attr) {
    if (is_string($icon_attr)) {
        return normalize_divi5_icon_char($icon_attr);
    }

    if (!is_array($icon_attr)) {
        return '';
    }

    $preferred_keys = ['unicode', 'icon', 'char', 'code', 'value', 'desktop', 'tablet', 'phone', 'innerContent'];
    foreach ($preferred_keys as $key) {
        if (array_key_exists($key, $icon_attr)) {
            $char = extract_divi5_icon_char($icon_attr[$key]);
            if ($char !== '') {
                return $char;
            }
        }
    }

    foreach ($icon_attr as $value) {
        $char = extract_divi5_icon_char($value);
        if ($char !== '') {
            return $char;
        }
    }

    return '';
}

function extract_divi5_icon_meta_prop($icon_attr, $prop_name) {
    if (!is_string($prop_name) || $prop_name === '') {
        return '';
    }

    if (is_scalar($icon_attr)) {
        return '';
    }

    if (!is_array($icon_attr)) {
        return '';
    }

    if (array_key_exists($prop_name, $icon_attr)) {
        $value = $icon_attr[$prop_name];
        if (is_scalar($value) && trim((string) $value) !== '') {
            return trim((string) $value);
        }
    }

    $preferred_keys = ['value', 'desktop', 'tablet', 'phone', 'innerContent', 'iconPicker'];
    foreach ($preferred_keys as $key) {
        if (array_key_exists($key, $icon_attr)) {
            $prop = extract_divi5_icon_meta_prop($icon_attr[$key], $prop_name);
            if ($prop !== '') {
                return $prop;
            }
        }
    }

    foreach ($icon_attr as $value) {
        $prop = extract_divi5_icon_meta_prop($value, $prop_name);
        if ($prop !== '') {
            return $prop;
        }
    }

    return '';
}

function extract_divi5_icon_type($icon_attr) {
    return extract_divi5_icon_meta_prop($icon_attr, 'type');
}

function extract_divi5_icon_weight($icon_attr) {
    return extract_divi5_icon_meta_prop($icon_attr, 'weight');
}

function is_divi5_fontawesome_type($icon_type) {
    if (!is_string($icon_type) || $icon_type === '') {
        return false;
    }

    $normalized = strtolower(trim($icon_type));
    if ($normalized === '') {
        return false;
    }

    if (strpos($normalized, 'fa') === 0) {
        return true;
    }

    return in_array($normalized, ['brands', 'regular', 'solid', 'fontawesome'], true);
}

function normalize_divi5_fontawesome_weight($icon_weight, $icon_type = '') {
    if (is_scalar($icon_weight)) {
        $weight = trim((string) $icon_weight);
        if (in_array($weight, ['100', '200', '300', '400', '500', '600', '700', '800', '900'], true)) {
            return $weight;
        }
        $lower_weight = strtolower($weight);
        if (in_array($lower_weight, ['regular', 'normal'], true)) {
            return '400';
        }
        if (in_array($lower_weight, ['solid', 'bold'], true)) {
            return '900';
        }
    }

    $type = is_string($icon_type) ? strtolower(trim($icon_type)) : '';
    if (in_array($type, ['fab', 'fa-brands', 'brands', 'far', 'fa-regular', 'regular'], true)) {
        return '400';
    }
    if (in_array($type, ['fas', 'fa-solid', 'solid'], true)) {
        return '900';
    }

    // Prefer solid when the icon metadata is incomplete.
    return '900';
}

function extract_divi5_tab_icon_font_style_attr($icon_font_attr) {
    if (!is_array($icon_font_attr)) {
        return [];
    }

    if (isset($icon_font_attr['decoration']['font']) && is_array($icon_font_attr['decoration']['font'])) {
        return sanitize_divi5_tab_icon_font_style_attr($icon_font_attr['decoration']['font']);
    }

    if (isset($icon_font_attr['font']) || isset($icon_font_attr['textShadow'])) {
        return sanitize_divi5_tab_icon_font_style_attr($icon_font_attr);
    }

    if (isset($icon_font_attr['value']) && is_array($icon_font_attr['value']) && (isset($icon_font_attr['value']['font']) || isset($icon_font_attr['value']['textShadow']))) {
        return sanitize_divi5_tab_icon_font_style_attr($icon_font_attr['value']);
    }

    return [];
}

function sanitize_divi5_tab_icon_font_style_attr(array $style_attr) {
    if (!isset($style_attr['font']) || !is_array($style_attr['font'])) {
        return $style_attr;
    }

    foreach ($style_attr['font'] as $breakpoint => $states) {
        if (!is_array($states)) {
            continue;
        }
        foreach ($states as $state => $state_value) {
            if (!is_array($state_value)) {
                continue;
            }
            foreach (['family', 'weight', 'style', 'letterSpacing', 'lineHeight'] as $field_name) {
                if (array_key_exists($field_name, $state_value)) {
                    unset($style_attr['font'][$breakpoint][$state][$field_name]);
                }
            }
        }
    }

    return $style_attr;
}

function extract_divi5_tab_icon_spacing_style_attr($icon_spacing_attr) {
    if (!is_array($icon_spacing_attr)) {
        return [];
    }

    if (isset($icon_spacing_attr['decoration']['spacing']) && is_array($icon_spacing_attr['decoration']['spacing'])) {
        return sanitize_divi5_tab_icon_spacing_style_attr($icon_spacing_attr['decoration']['spacing']);
    }

    if (isset($icon_spacing_attr['desktop']) || isset($icon_spacing_attr['tablet']) || isset($icon_spacing_attr['phone'])) {
        return sanitize_divi5_tab_icon_spacing_style_attr($icon_spacing_attr);
    }

    if (isset($icon_spacing_attr['spacing']) && is_array($icon_spacing_attr['spacing'])) {
        return sanitize_divi5_tab_icon_spacing_style_attr($icon_spacing_attr['spacing']);
    }

    if (isset($icon_spacing_attr['value']) && is_array($icon_spacing_attr['value'])) {
        if (isset($icon_spacing_attr['value']['spacing']) && is_array($icon_spacing_attr['value']['spacing'])) {
            return sanitize_divi5_tab_icon_spacing_style_attr($icon_spacing_attr['value']['spacing']);
        }
        if (isset($icon_spacing_attr['value']['desktop']) || isset($icon_spacing_attr['value']['tablet']) || isset($icon_spacing_attr['value']['phone'])) {
            return sanitize_divi5_tab_icon_spacing_style_attr($icon_spacing_attr['value']);
        }
    }

    return [];
}

function sanitize_divi5_tab_icon_spacing_style_attr(array $spacing_attr) {
    foreach (['desktop', 'tablet', 'phone'] as $breakpoint) {
        if (!isset($spacing_attr[$breakpoint]) || !is_array($spacing_attr[$breakpoint])) {
            continue;
        }

        foreach ($spacing_attr[$breakpoint] as $state => $state_value) {
            if (!is_array($state_value)) {
                continue;
            }
            if (array_key_exists('margin', $state_value)) {
                unset($spacing_attr[$breakpoint][$state]['margin']);
            }
        }
    }

    return $spacing_attr;
}

function get_divi5_tab_icon_font_declaration_fallback(array $font_attr, array $font_value, $breakpoint = 'desktop') {
    $font_declaration_class = '\ET\Builder\Packages\StyleLibrary\Declarations\Font\Font';
    if (class_exists($font_declaration_class) && method_exists($font_declaration_class, 'style_declaration')) {
        try {
            $declaration = $font_declaration_class::style_declaration(
                [
                    'attr'       => $font_attr,
                    'attrValue'  => $font_value,
                    'breakpoint' => $breakpoint,
                    'state'      => 'value',
                    'important'  => false,
                    'returnType' => 'string',
                ]
            );
            return is_string($declaration) ? $declaration : '';
        } catch (\Throwable $e) {
            // Use the local declaration map below.
        }
    }

    $declaration = '';
    if (isset($font_value['weight'])) {
        $weight = sanitize_css_declaration_value($font_value['weight']);
        if ($weight !== '') {
            $declaration .= 'font-weight:' . $weight . ';';
        }
    }
    if (isset($font_value['color'])) {
        $color = sanitize_css_declaration_value($font_value['color']);
        if ($color !== '') {
            $declaration .= 'color:' . $color . ';';
        }
    }
    if (isset($font_value['size'])) {
        $size = sanitize_css_declaration_value($font_value['size']);
        if ($size !== '') {
            $declaration .= 'font-size:' . $size . ';';
        }
    }
    return $declaration;
}

function get_divi5_tab_icon_text_shadow_declaration_fallback(array $text_shadow_value) {
    $text_shadow_declaration_class = '\ET\Builder\Packages\StyleLibrary\Declarations\TextShadow\TextShadow';
    if (class_exists($text_shadow_declaration_class) && method_exists($text_shadow_declaration_class, 'style_declaration')) {
        try {
            $declaration = $text_shadow_declaration_class::style_declaration(
                [
                    'attrValue'  => $text_shadow_value,
                    'important'  => false,
                    'returnType' => 'string',
                ]
            );
            return is_string($declaration) ? $declaration : '';
        } catch (\Throwable $e) {
            // Use the local declaration map below.
        }
    }

    $style = $text_shadow_value['style'] ?? '';
    if (!is_string($style) || $style === '' || strtolower($style) === 'none') {
        return '';
    }

    $horizontal = $text_shadow_value['horizontal'] ?? '0em';
    $vertical   = $text_shadow_value['vertical'] ?? '0em';
    $blur       = $text_shadow_value['blur'] ?? '0em';
    $color      = $text_shadow_value['color'] ?? '';
    $value      = sanitize_css_declaration_value(trim($horizontal . ' ' . $vertical . ' ' . $blur . ($color ? ' ' . $color : '')));

    return $value !== '' ? 'text-shadow:' . $value . ';' : '';
}

function get_divi5_tab_icon_font_css($selector, array $font_style_attr) {
    $selector = is_string($selector) ? trim($selector) : '';
    if ($selector === '' || !$font_style_attr) {
        return '';
    }

    $font_style_class = '\ET\Builder\Packages\Module\Options\Font\FontStyle';
    if (class_exists($font_style_class) && method_exists($font_style_class, 'style')) {
        try {
            $css = $font_style_class::style(
                [
                    'selector'   => $selector,
                    'attr'       => $font_style_attr,
                    'asStyle'    => false,
                    'returnType' => 'string',
                ]
            );

            if (is_string($css) && trim($css) !== '') {
                return trim($css);
            }
        } catch (\Throwable $e) {
            // Use the local declaration assembly below.
        }
    }

    $font_attr = isset($font_style_attr['font']) && is_array($font_style_attr['font']) ? $font_style_attr['font'] : [];
    $text_shadow_attr = isset($font_style_attr['textShadow']) && is_array($font_style_attr['textShadow']) ? $font_style_attr['textShadow'] : [];

    $css = '';
    $breakpoints = [
        'desktop' => '',
        'tablet'  => '@media (max-width: 980px){%s}',
        'phone'   => '@media (max-width: 767px){%s}',
    ];

    foreach ($breakpoints as $breakpoint => $wrapper) {
        $declaration = '';
        $font_value = $font_attr[$breakpoint]['value'] ?? null;
        if (is_array($font_value)) {
            $declaration .= get_divi5_tab_icon_font_declaration_fallback($font_attr, $font_value, $breakpoint);
        }

        $text_shadow_value = $text_shadow_attr[$breakpoint]['value'] ?? null;
        if (is_array($text_shadow_value)) {
            $declaration .= get_divi5_tab_icon_text_shadow_declaration_fallback($text_shadow_value);
        }

        if ($declaration === '') {
            continue;
        }

        $rule = $selector . '{' . $declaration . '}';
        $css .= $wrapper ? sprintf($wrapper, $rule) : $rule;
    }

    return $css;
}

function get_divi5_tab_icon_spacing_declaration_fallback(array $spacing_value) {
    $padding = $spacing_value['padding'] ?? null;
    if (!is_array($padding)) {
        return '';
    }

    $declaration = '';
    foreach (['top', 'right', 'bottom', 'left'] as $side) {
        if (!isset($padding[$side]) || !is_scalar($padding[$side])) {
            continue;
        }
        $value = trim((string) $padding[$side]);
        if ($value === '') {
            continue;
        }
        $value = sanitize_css_declaration_value($value);
        if ($value === '') {
            continue;
        }
        $declaration .= 'padding-' . $side . ':' . $value . ';';
    }

    return $declaration;
}

function get_divi5_tab_icon_spacing_css($selector, array $spacing_attr) {
    $selector = is_string($selector) ? trim($selector) : '';
    if ($selector === '' || !$spacing_attr) {
        return '';
    }

    $spacing_style_class = '\ET\Builder\Packages\Module\Options\Spacing\SpacingStyle';
    if (class_exists($spacing_style_class) && method_exists($spacing_style_class, 'style')) {
        try {
            $css = $spacing_style_class::style(
                [
                    'selector'   => $selector,
                    'attr'       => $spacing_attr,
                    'asStyle'    => false,
                    'returnType' => 'string',
                ]
            );

            if (is_string($css) && trim($css) !== '') {
                return trim($css);
            }
        } catch (\Throwable $e) {
            // Use the local declaration assembly below.
        }
    }

    $css = '';
    $breakpoints = [
        'desktop' => '',
        'tablet'  => '@media (max-width: 980px){%s}',
        'phone'   => '@media (max-width: 767px){%s}',
    ];

    foreach ($breakpoints as $breakpoint => $wrapper) {
        $spacing_value = $spacing_attr[$breakpoint]['value'] ?? null;
        if (!is_array($spacing_value)) {
            continue;
        }

        $declaration = get_divi5_tab_icon_spacing_declaration_fallback($spacing_value);
        if ($declaration === '') {
            continue;
        }

        $rule = $selector . '{' . $declaration . '}';
        $css .= $wrapper ? sprintf($wrapper, $rule) : $rule;
    }

    return $css;
}

function render_divi5_tabs_with_icons($block_content, $parsed_block, $block) {
    if (!is_string($block_content) || $block_content === '') {
        return $block_content;
    }

    if (!is_array($parsed_block)) {
        return $block_content;
    }

    $inner_blocks = isset($parsed_block['innerBlocks']) && is_array($parsed_block['innerBlocks']) ? $parsed_block['innerBlocks'] : [];
    if (!$inner_blocks) {
        return $block_content;
    }

    $child_icon_data = collect_divi5_child_tab_icon_data($inner_blocks);
    $icons = $child_icon_data['icons'];

    if (!has_any_divi5_icon($icons)) {
        return $block_content;
    }

    $module_attrs = isset($parsed_block['attrs']) && is_array($parsed_block['attrs']) ? $parsed_block['attrs'] : [];
    $modified_html = inject_icons_into_tabs_html(
        $block_content,
        $icons,
        $module_attrs,
        $child_icon_data['font_attrs'],
        $child_icon_data['spacing_attrs']
    );
    return $modified_html;
}

function collect_divi5_child_tab_icon_data(array $inner_blocks) {
    $icon_data = [
        'icons'         => [],
        'font_attrs'    => [],
        'spacing_attrs' => [],
    ];

    foreach ($inner_blocks as $child_block) {
        if (!is_array($child_block)) {
            continue;
        }

        if (($child_block['blockName'] ?? '') !== DIVI5_TAB_SLUG) {
            continue;
        }

        $child_attrs = isset($child_block['attrs']) && is_array($child_block['attrs']) ? $child_block['attrs'] : [];
        $icon_data['icons'][] = $child_attrs[DIVI5_ICON_ATTR] ?? null;
        $icon_data['font_attrs'][] = extract_divi5_tab_icon_font_style_attr(get_divi5_attr($child_attrs, DIVI5_ICON_FONT_ATTR));
        $icon_data['spacing_attrs'][] = extract_divi5_tab_icon_spacing_style_attr(get_divi5_attr($child_attrs, DIVI5_ICON_SPACING_ATTR));
    }

    return $icon_data;
}

function has_any_divi5_icon(array $icons) {
    foreach ($icons as $icon_val) {
        if (extract_divi5_icon_char($icon_val) !== '') {
            return true;
        }
    }

    return false;
}

function inject_icons_into_tabs_html($html, array $icons, array $module_attrs = [], array $child_icon_font_attrs = [], array $child_icon_spacing_attrs = []) {
    if (!is_string($html) || $html === '') {
        return $html;
    }

    $pattern = '/(<ul[^>]*class="[^"]*et_pb_tabs_controls[^"]*"[^>]*>)(.*?)(<\/ul>)/si';
    if (!preg_match($pattern, $html, $matches)) {
        return $html;
    }

    $ul_open  = $matches[1];
    $ul_inner = $matches[2];
    $ul_close = $matches[3];

    preg_match_all('/<li[^>]*>.*?<\/li>/si', $ul_inner, $li_matches);
    $lis = $li_matches[0] ?? [];
    if (!$lis) {
        return $html;
    }

    $new_lis = [];
    $icon_index = 0;

    foreach ($lis as $li_html) {
        $a_matches = [];
        if (!preg_match('/<a[^>]*>(.*?)<\/a>/si', $li_html, $a_matches)) {
            $new_lis[] = $li_html;
            continue;
        }

        $a_full   = $a_matches[0];
        $a_inner  = $a_matches[1];

        $icon_val = isset($icons[$icon_index]) ? $icons[$icon_index] : '';
        $icon_index++;

        if ($icon_val === '') {
            $new_lis[] = $li_html;
            continue;
        }

        $icon_html = build_icon_span_html($icon_val);
        if ($icon_html === '') {
            $new_lis[] = $li_html;
            continue;
        }

        $trimmed_inner = trim($a_inner);

        if ($trimmed_inner === '') {
            $new_inner = $icon_html;
        } else {
            $new_inner = $icon_html . $a_inner;
        }

        $new_a = str_replace($a_inner, $new_inner, $a_full);
        if ($trimmed_inner === '' && !preg_match('/\saria-label\s*=/i', $new_a) && !preg_match('/\saria-labelledby\s*=/i', $new_a)) {
            $aria_label = esc_attr(sprintf(__('Tab %d', 'et_builder'), $icon_index));
            $labelled_a = preg_replace_callback(
                '/<a\b/i',
                function($matches) use ($aria_label) {
                    return $matches[0] . ' aria-label="' . $aria_label . '"';
                },
                $new_a,
                1
            );
            if (is_string($labelled_a)) {
                $new_a = $labelled_a;
            }
        }
        $new_li = str_replace($a_full, $new_a, $li_html);
        $new_lis[] = $new_li;
    }

    $new_ul_inner = implode('', $new_lis);
    $new_ul_html  = $ul_open . $new_ul_inner . $ul_close;
    $updated_html = preg_replace_callback(
        $pattern,
        function() use ($new_ul_html) {
            return $new_ul_html;
        },
        $html,
        1
    );
    if (!is_string($updated_html)) {
        return $html;
    }
    $html = $updated_html;

    $css = get_divi5_injected_icon_css($html, $module_attrs, $child_icon_font_attrs, $child_icon_spacing_attrs);
    if ($css !== '') {
        $html .= '<style>' . $css . '</style>';
    }

    return $html;
}

function get_divi5_injected_icon_css($html, array $module_attrs, array $child_icon_font_attrs, array $child_icon_spacing_attrs) {
    $safe_order_class = sanitize_html_class(extract_tabs_order_class($html));
    if ($safe_order_class === '') {
        return '';
    }

    $icon_selector = get_divi5_tab_icon_selector($safe_order_class);
    $css  = get_divi5_tab_icon_default_css($safe_order_class);
    $css .= get_divi5_tab_icon_font_css(
        $icon_selector,
        extract_divi5_tab_icon_font_style_attr(get_divi5_attr($module_attrs, DIVI5_ICON_FONT_ATTR))
    );
    $css .= get_divi5_tab_icon_spacing_css(
        $icon_selector,
        extract_divi5_tab_icon_spacing_style_attr(get_divi5_attr($module_attrs, DIVI5_ICON_SPACING_ATTR))
    );
    $css .= get_divi5_child_tab_icon_css($safe_order_class, $child_icon_font_attrs, __NAMESPACE__ . '\get_divi5_tab_icon_font_css');
    $css .= get_divi5_child_tab_icon_css($safe_order_class, $child_icon_spacing_attrs, __NAMESPACE__ . '\get_divi5_tab_icon_spacing_css');

    $active_icon_selector = get_divi5_tab_icon_selector($safe_order_class, null, true);
    $css .= get_divi5_tab_icon_font_css(
        $active_icon_selector,
        extract_divi5_tab_icon_font_style_attr(get_divi5_attr($module_attrs, DIVI5_ACTIVE_ICON_FONT_ATTR))
    );
    $css .= get_divi5_tab_icon_spacing_css(
        $active_icon_selector,
        extract_divi5_tab_icon_spacing_style_attr(get_divi5_attr($module_attrs, DIVI5_ACTIVE_ICON_SPACING_ATTR))
    );

    return $css;
}

function get_divi5_attr(array $attrs, $attr_name) {
    if (array_key_exists($attr_name, $attrs) && $attrs[$attr_name] !== null) {
        return $attrs[$attr_name];
    }

    if (isset($attrs['module']) && is_array($attrs['module']) && array_key_exists($attr_name, $attrs['module']) && $attrs['module'][$attr_name] !== null) {
        return $attrs['module'][$attr_name];
    }

    return null;
}

function get_divi5_tab_icon_default_css($safe_order_class) {
    $css  = '.' . $safe_order_class . ' .divibooster-tab-icon{';
    $css .= 'display:inline-block;vertical-align:middle;margin-right:0.4em;';
    $css .= 'font-size:0.9em;line-height:1;';
    $css .= '}';
    $css .= '.' . $safe_order_class . ' .divibooster-tab-icon:before{display:none;}';

    return $css;
}

function get_divi5_tab_icon_selector($safe_order_class, $tab_index = null, $active = false) {
    $tab_selector = $active ? 'li.et_pb_tab_active' : 'li';
    if ($tab_index !== null) {
        $tab_selector = 'li:nth-child(' . ((int) $tab_index + 1) . ')';
    }

    return '.' . $safe_order_class . ' .et_pb_tabs_controls ' . $tab_selector . ' a .divibooster-tab-icon';
}

function get_divi5_child_tab_icon_css($safe_order_class, array $child_style_attrs, $css_callback) {
    if (!is_callable($css_callback)) {
        return '';
    }

    $css = '';

    foreach ($child_style_attrs as $tab_index => $style_attr) {
        if (!is_array($style_attr) || empty($style_attr)) {
            continue;
        }

        $tab_index = (int) $tab_index;
        if ($tab_index < 0) {
            continue;
        }

        try {
            $style_css = call_user_func($css_callback, get_divi5_tab_icon_selector($safe_order_class, $tab_index), $style_attr);
        } catch (\Throwable $e) {
            continue;
        }
        if ($style_css !== '') {
            $css .= $style_css;
        }
    }

    return $css;
}

function extract_tabs_order_class($html) {
    if (!is_string($html) || $html === '') {
        return '';
    }

    // Scope injected CSS to the rendered tabs module instance.
    if (!preg_match('/<div[^>]*class="([^"]*\bet_pb_tabs_\d+[^"}]*)"/i', $html, $m)) {
        return '';
    }
    $class_attr = $m[1];
    if (preg_match('/et_pb_tabs_\d+[^\s"]*/', $class_attr, $cm)) {
        return $cm[0];
    }
    return '';
}

function build_icon_span_html($icon_val) {
    $icon_char = extract_divi5_icon_char($icon_val);
    if ($icon_char === '') {
        return '';
    }

    $icon_type = extract_divi5_icon_type($icon_val);
    $icon_weight = extract_divi5_icon_weight($icon_val);
    $icon_font_family = '';

    if (is_string($icon_val) && is_extended_divi_icon($icon_val)) {
        $processed = '';
        if (function_exists('et_pb_extended_process_font_icon')) {
            try {
                $processed = et_pb_extended_process_font_icon($icon_val);
            } catch (\Throwable $e) {
                $processed = '';
            }
        } elseif (function_exists('et_pb_process_font_icon')) {
            try {
                $processed = et_pb_process_font_icon($icon_val);
            } catch (\Throwable $e) {
                $processed = '';
            }
        }

        if (is_string($processed) && $processed !== '') {
            $processed_char = html_entity_decode($processed, ENT_QUOTES, 'UTF-8');
            if (is_string($processed_char) && $processed_char !== '') {
                $icon_char = $processed_char;
            }
        }

        if (function_exists('et_pb_get_icon_font_family')) {
            try {
                $icon_font_family = et_pb_get_icon_font_family($icon_val);
            } catch (\Throwable $e) {
                $icon_font_family = '';
            }
        }

        if (function_exists('et_pb_get_icon_font_weight')) {
            try {
                $icon_weight = (string) et_pb_get_icon_font_weight($icon_val);
            } catch (\Throwable $e) {
            }
        }
    }

    if (function_exists('et_pb_process_font_icon')) {
        try {
            $processed = et_pb_process_font_icon($icon_char);
        } catch (\Throwable $e) {
            $processed = '';
        }
        if (is_string($processed) && $processed !== '') {
            $processed_char = html_entity_decode($processed, ENT_QUOTES, 'UTF-8');
            if (is_string($processed_char) && $processed_char !== '') {
                $icon_char = $processed_char;
            }
        }
    }

    $is_fontawesome = $icon_font_family === 'FontAwesome' || is_divi5_fontawesome_type($icon_type) || is_divi5_fontawesome_char($icon_char);

    $inline_style = '';
    if ($is_fontawesome) {
        $resolved_weight = normalize_divi5_fontawesome_weight($icon_weight, $icon_type);
        $inline_style = ' style="font-family:FontAwesome!important;font-weight:' . esc_attr($resolved_weight) . '!important;"';
    }

    // Keep decorative glyphs out of the accessible tab name.
    $icon_html = '<span class="et-pb-icon divibooster-tab-icon" aria-hidden="true" data-icon="' . esc_attr($icon_char) . '"' . $inline_style . '>' . esc_html($icon_char) . '</span>';

    return $icon_html;
}

function is_extended_divi_icon($icon_val) {
    if (!is_string($icon_val) || !function_exists('et_pb_maybe_extended_icon')) {
        return false;
    }

    try {
        return (bool) et_pb_maybe_extended_icon($icon_val);
    } catch (\Throwable $e) {
        return false;
    }
}

function icons_include_fontawesome(array $icons) {
    foreach ($icons as $icon) {
        if (icon_uses_fontawesome($icon)) {
            return true;
        }
    }

    return false;
}

function icon_uses_fontawesome($icon_val) {
    $icon_char = extract_divi5_icon_char($icon_val);
    $icon_type = extract_divi5_icon_type($icon_val);

    if (is_divi5_fontawesome_type($icon_type) || is_divi5_fontawesome_char($icon_char)) {
        return true;
    }

    if (is_extended_divi_icon($icon_val) && function_exists('et_pb_get_icon_font_family')) {
        try {
            return et_pb_get_icon_font_family($icon_val) === 'FontAwesome';
        } catch (\Throwable $e) {
            return false;
        }
    }

    return false;
}

function is_divi5_fontawesome_char($icon_char) {
    if (!is_string($icon_char) || $icon_char === '') {
        return false;
    }
    if (!function_exists('mb_ord')) {
        return false;
    }

    $cp = mb_ord($icon_char, 'UTF-8');
    return is_int($cp) && $cp >= 0xF000 && $cp <= 0xF8FF;
}
