<?php

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

if ( ! class_exists( 'AIEO_Side_Cart_Display' ) ) :

class AIEO_Side_Cart_Display {

    const SESSION_CACHE_KEY = 'aieo_dmm_side_cart_cache_v2';
    const SESSION_CACHE_TTL = 30;

    
    private static $instance = null;

    public static function instance() {
        if ( self::$instance === null ) { self::$instance = new self(); }
        return self::$instance;
    }

    private function __construct() {}

    
    private static function setting( $key, $default = null ) {
        if ( class_exists( 'AIEO_DMM_Settings' ) ) {
            return AIEO_DMM_Settings::get( $key, $default );
        }
        static $blob = null;
        if ( $blob === null ) {
            $blob = get_option( 'aieo_dmm_settings', array() );
            if ( ! is_array( $blob ) ) { $blob = array(); }
        }
        return array_key_exists( $key, $blob ) ? $blob[ $key ] : $default;
    }

    private static function module_enabled( $id ) {
        if ( class_exists( 'AIEO_DMM_Settings' ) ) {
            return AIEO_DMM_Settings::is_module_enabled( $id );
        }
        return true;
    }

    
    private static function asset_dir() { return trailingslashit( dirname( __DIR__ ) ); }
    private static function asset_uri() { return trailingslashit( dirname( plugins_url( '', __FILE__ ) ) ); }

    
    private function render_cart_items_html() {
        if ( class_exists( 'AIEO_Runtime_Side_Cart' ) ) {
            return (string) AIEO_Runtime_Side_Cart::instance()->render_cart_items();
        }
        if ( class_exists( 'AIEO_DMM_Side_Cart' ) ) {
            return (string) ( new AIEO_DMM_Side_Cart() )->render_cart_items();
        }
        return '';
    }

    
    public function register_display() {
        
        if ( ! self::module_enabled( 'side_cart' ) && ! self::setting( 'side_cart_enabled', false ) ) {
            return;
        }
        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
        add_action( 'wp_footer',          array( $this, 'render_on_footer' ), 20 );
        if ( $this->chat_slot_enabled() ) {
            add_filter( 'pre_option_aieo_chat_floating_enabled', array( $this, 'filter_force_chat_floating_on' ) );
            add_action( 'wp_footer', array( $this, 'render_chat_overrides_style' ), 1000 );
        }
        if ( self::setting( 'side_cart_quick_view_enabled', false ) ) {
            add_action( 'woocommerce_after_shop_loop_item', array( $this, 'render_archive_quick_view_trigger' ), 12 );
        }
    }

    

    
    public static function bar_items_catalog() {
        return array(
            array('slug' => 'nav',    'label' => __('Navigator (categories)',  'ai-eshop-optimizer'), 'enabled' => true),
            array('slug' => 'search', 'label' => __('Search (FiboSearch)',     'ai-eshop-optimizer'), 'enabled' => true),
            array('slug' => 'chat',   'label' => __('AI Chat',                 'ai-eshop-optimizer'), 'enabled' => false),
            array('slug' => 'consent','label' => __('Cookie settings',         'ai-eshop-optimizer'), 'enabled' => false),
            array('slug' => 'cart',   'label' => __('Cart (side-cart trigger)','ai-eshop-optimizer'), 'enabled' => true),
        );
    }

    
    public static function consent_button_active() {
        if ( ! self::module_enabled( 'side_cart' ) && ! self::setting( 'side_cart_enabled', false ) ) {
            return false;
        }
        $saved = self::setting( 'side_cart_bar_items', null );
        if ( is_array( $saved ) ) {
            foreach ( $saved as $row ) {
                if ( is_array( $row ) && isset( $row['slug'] ) && 'consent' === $row['slug'] ) {
                    return ! empty( $row['enabled'] );
                }
            }
        }
        return false;
    }

    
    public function resolve_bar_items_config() {
        $catalog = self::bar_items_catalog();
        $by_slug = array();
        foreach ($catalog as $g) { $by_slug[$g['slug']] = $g; }

        $saved = self::setting('side_cart_bar_items', null);
        if (!is_array($saved)) { $saved = array(); }

        $resolved = array();
        $seen = array();
        foreach ($saved as $row) {
            if (!is_array($row) || empty($row['slug'])) { continue; }
            $slug = sanitize_key($row['slug']);
            if (!isset($by_slug[$slug]) || isset($seen[$slug])) { continue; }
            $resolved[] = array(
                'slug'    => $slug,
                'label'   => $by_slug[$slug]['label'],
                'enabled' => isset($row['enabled']) ? (bool) $row['enabled'] : (bool) $by_slug[$slug]['enabled'],
            );
            $seen[$slug] = true;
        }
        foreach ($catalog as $g) {
            if (isset($seen[$g['slug']])) { continue; }
            $resolved[] = array(
                'slug'    => $g['slug'],
                'label'   => $g['label'],
                'enabled' => (bool) $g['enabled'],
            );
        }
        return $resolved;
    }

    
    public function resolve_chat_desktop_position() {
        $val = (string) self::setting('side_cart_chat_desktop_position', 'left');
        return in_array($val, array('left', 'above'), true) ? $val : 'left';
    }

    
    public function resolve_mobile_bar_style() {
        $custom   = (bool) self::setting('side_cart_mbar_custom', false);
        $height   = max(40, min(96, (int) self::setting('side_cart_mbar_height', 58)));
        $bg_set   = (string) self::setting('side_cart_mbar_bg',       '');
        $icon_set = (string) self::setting('side_cart_mbar_icon',     '');
        $iconact_set = (string) self::setting('side_cart_mbar_icon_active', '');
        $bbg_set  = (string) self::setting('side_cart_mbar_badge_bg', '');
        $bfg_set  = (string) self::setting('side_cart_mbar_badge_fg', '');
        $bubble_on = (bool) self::setting('side_cart_mbar_cart_bubble', true);

        
        
        $hex = function ($v) { return ($v !== '' && sanitize_hex_color($v)) ? $v : ''; };
        $bg  = ($custom && $hex($bg_set))   ? $bg_set   : 'var(--wp--preset--color--accent, #F46767)';
        $ico = ($custom && $hex($icon_set)) ? $icon_set : 'var(--wp--preset--color--bg, #ffffff)';
        
        
        
        
        
        $ico_active = $custom ? ($hex($iconact_set) ?: '#F46767') : '';
        $bbg = ($custom && $hex($bbg_set))  ? $bbg_set  : 'var(--wp--preset--color--fg, #1f2937)';
        $bfg = ($custom && $hex($bfg_set))  ? $bfg_set  : 'var(--wp--preset--color--bg, #ffffff)';

        
        
        
        $glass    = (bool) self::setting('side_cart_mbar_glass', false);
        $opacity  = max(0, min(100, (int) self::setting('side_cart_mbar_opacity', 70)));
        $blur     = max(0, min(40,  (int) self::setting('side_cart_mbar_blur',    12)));
        $bg_out   = $glass ? sprintf('color-mix(in srgb, %s %d%%, transparent)', $bg, $opacity) : $bg;
        $backdrop = $glass ? sprintf('blur(%dpx)', $blur) : 'none';

        
        
        
        $sel_on = (bool) self::setting('side_cart_mbar_sel_enable', false);
        $sel    = array();
        foreach (array('nav', 'search', 'chat', 'consent', 'cart') as $slug) {
            $sel[$slug] = array(
                'bg' => $hex((string) self::setting("side_cart_mbar_{$slug}_sel_bg", '')) ?: '#ffffff',
                'fg' => $hex((string) self::setting("side_cart_mbar_{$slug}_sel_fg", '')) ?: '#1f2937',
                'bc' => $hex((string) self::setting("side_cart_mbar_{$slug}_sel_bc", '')) ?: '#ffffff',
            );
        }

        $items   = $this->resolve_bar_items_config();
        $enabled = array_values(array_filter($items, function ($i) { return !empty($i['enabled']); }));
        $only_cart = (count($enabled) === 1 && $enabled[0]['slug'] === 'cart');
        $is_bubble = ($only_cart && $bubble_on);
        $is_single = (count($enabled) === 1 && !$is_bubble);

        return array(
            'bg'         => $bg_out,
            'icon'       => $ico,
            'icon_active' => $ico_active,
            'badge_bg'   => $bbg,
            'badge_fg'   => $bfg,
            'backdrop'   => $backdrop,
            'sel_on'     => $sel_on,
            'sel'        => $sel,
            
            'height_css' => $is_bubble ? '0px' : ($height . 'px'),
            'is_bubble'  => $is_bubble,
            'is_single'  => $is_single,
            'justify'    => $is_single ? 'center' : 'space-between',
        );
    }

    
    private function chat_slot_enabled() {
        foreach ($this->resolve_bar_items_config() as $item) {
            if ($item['slug'] === 'chat' && !empty($item['enabled'])) {
                return true;
            }
        }
        return false;
    }

    public function filter_force_chat_floating_on() { return '1'; }

    
    public function render_chat_overrides_style() {
        $css_path = self::asset_dir() . 'assets/css/aieo-chat-overrides.css';
        if ( ! file_exists( $css_path ) ) { return; }
        $url = self::asset_uri() . 'assets/css/aieo-chat-overrides.css?ver=' . filemtime( $css_path );
        echo '<link rel="stylesheet" href="' . esc_url( $url ) . '" data-aieo-chat-overrides="1" />' . "\n"; 
    }

    
    
    

    
    public function render_on_footer() {
        if (is_admin()) { return; }
        if (!function_exists('WC') || !WC()->cart) { return; }

        $count    = (int) WC()->cart->get_cart_contents_count();
        $subtotal = (string) WC()->cart->get_cart_subtotal();

        $fs = $this->get_free_shipping_settings();
        $thresh = (float) ($fs['free_shipping_threshold'] ?? 0);
        $raw = $this->get_cart_subtotal_raw();
        $remaining = max(0, $thresh - $raw);

        $nonce = wp_create_nonce('aieo_side_cart');

        
        
        
        $border_enabled = (bool) self::setting('side_cart_card_border_enabled', true);
        $border_color   = (string) self::setting('side_cart_card_border_color',  '#c0a37b');
        $border_hover   = (string) self::setting('side_cart_card_border_hover',  '#a88a63');
        $border_width   = $border_enabled ? '1px' : '0';
        $root_style = sprintf(
            '--aieo-card-border-width: %s; --aieo-card-border-color: %s; --aieo-card-border-hover: %s;',
            $border_width,
            esc_attr($border_color),
            esc_attr($border_hover)
        );

        
        
        
        
        $mbar = $this->resolve_mobile_bar_style();
        ?>
        <style id="aieo-sc-mbar-vars">:root{--aieo-mbar-bg:<?php echo $mbar['bg']; ?>;--aieo-mbar-icon:<?php echo $mbar['icon']; ?>;--aieo-mbar-badge-bg:<?php echo $mbar['badge_bg']; ?>;--aieo-mbar-badge-fg:<?php echo $mbar['badge_fg']; ?>;--aieo-mbar-height:<?php echo $mbar['height_css']; ?>;--aieo-mbar-backdrop:<?php echo $mbar['backdrop']; ?>;}<?php if ($mbar['is_bubble']): ?>@media(max-width:720px){body{padding-bottom:0!important}}<?php endif; ?></style>
        <div class="aieo-sc-root" id="aieo-sc-root"
             data-nonce="<?php echo esc_attr($nonce); ?>"
             data-threshold="<?php echo esc_attr((string) $thresh); ?>"
             data-currency-symbol="<?php echo esc_attr(get_woocommerce_currency_symbol()); ?>"
             data-msg-initial="<?php echo esc_attr((string) ($fs['msg_initial']  ?? '')); ?>"
             data-msg-progress="<?php echo esc_attr((string) ($fs['msg_progress'] ?? '')); ?>"
             data-msg-success="<?php echo esc_attr((string) ($fs['msg_success']  ?? '')); ?>"
             style="<?php echo esc_attr($root_style); ?>">

            <!-- Floating trigger SHELL.
                 On desktop: the cart trigger renders as a floating circle
                 bottom-right; the bar buttons (nav/search/chat) are hidden
                 by their own .aieo-sc-bar-* CSS rules. The dedicated
                 desktop chat launcher (.aieo-sc-chat-desktop, sibling
                 below) carries the AI-chat circle on the desktop side.
                 On mobile (≤720px): the shell becomes a full-width sticky
                 bar pinned to the viewport bottom; ALL configured items
                 (in admin order) line up flat across the bar. Cart
                 trigger keeps its `.aieo-sc-trigger` class so its inline
                 TRIGGER_STYLES still target it correctly when reordered.
                 The HTML order matches the saved config — flex
                 `justify-content: space-between` distributes them. -->
            <div class="aieo-sc-floating-shell<?php echo $mbar['is_bubble'] ? ' aieo-sc-shell--bubble' : ($mbar['is_single'] ? ' aieo-sc-shell--single' : ''); ?>">
                <?php
                $bar_items     = $this->resolve_bar_items_config();
                $chat_desk_pos = $this->resolve_chat_desktop_position();
                foreach ( $bar_items as $item ) :
                    if ( empty( $item['enabled'] ) ) { continue; }
                    
                    
                    
                    
                    
                    if ( $item['slug'] === 'consent' && ! ( class_exists( 'AIEO_Consent' ) && 'yes' === AIEO_Consent::settings()['reopen_enabled'] ) ) { continue; }
                    switch ( $item['slug'] ) {
                        case 'nav': ?>
                            <button type="button" class="aieo-sc-bar-burger aieo-sc-bar-item" data-aieo-sc-toggle="nav"
                                    aria-label="<?php esc_attr_e( 'Πλοήγηση καταλόγου', 'ai-eshop-optimizer' ); ?>">
                                <svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                    <line x1="3" y1="6"  x2="21" y2="6"/>
                                    <line x1="3" y1="12" x2="21" y2="12"/>
                                    <line x1="3" y1="18" x2="14" y2="18"/>
                                </svg>
                            </button>
                        <?php break;
                        case 'search': ?>
                            <button type="button" class="aieo-sc-bar-search aieo-sc-bar-item" data-aieo-sc-toggle="search"
                                    aria-label="<?php esc_attr_e( 'Αναζήτηση', 'ai-eshop-optimizer' ); ?>">
                                <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                    <circle cx="11" cy="11" r="7"/>
                                    <line x1="21" y1="21" x2="16.65" y2="16.65"/>
                                </svg>
                            </button>
                        <?php break;
                        case 'chat': ?>
                            <button type="button" class="aieo-sc-bar-chat aieo-sc-bar-item" data-aieo-sc-toggle="chat"
                                    aria-label="<?php esc_attr_e( 'Συνομιλία βοηθού', 'ai-eshop-optimizer' ); ?>">
                                <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                    <path d="M21 11.5a8.5 8.5 0 0 1-13.55 6.86L3 19l1.32-3.96A8.5 8.5 0 1 1 21 11.5z"/>
                                </svg>
                            </button>
                        <?php break;
                        case 'consent': ?>
                            <?php 
                                  
                                  
                                  ?>
                            <button type="button" class="aieo-cmp-reopen aieo-sc-bar-cookie aieo-sc-bar-item" data-aieo-sc-toggle="consent"
                                    style="<?php echo esc_attr( class_exists( 'AIEO_Consent' ) ? AIEO_Consent::reopen_button_vars() : '' ); ?>"
                                    aria-label="<?php esc_attr_e( 'Ρυθμίσεις cookies', 'ai-eshop-optimizer' ); ?>">
                                <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                    <path d="M12 2a10 10 0 1 0 10 10c0-.32-.02-.64-.05-.95a2.8 2.8 0 0 1-3.92-3.92A2.8 2.8 0 0 1 12.95 2.05 10.3 10.3 0 0 0 12 2z"/>
                                    <circle cx="8.5" cy="9.5" r="1"/>
                                    <circle cx="12.5" cy="13.5" r="1"/>
                                    <circle cx="9" cy="15.5" r="0.9"/>
                                    <circle cx="15.5" cy="10" r="0.9"/>
                                </svg>
                            </button>
                        <?php break;
                        case 'cart': ?>
                            <button type="button" class="aieo-sc-trigger aieo-sc-bar-item" data-aieo-sc-toggle="cart"
                                    aria-label="<?php esc_attr_e( 'Open cart', 'ai-eshop-optimizer' ); ?>">
                                <span class="aieo-sc-trigger-icon" aria-hidden="true">
                                    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6">
                                        <path d="M6 7h12l-1.2 10.5a2 2 0 0 1-2 1.8H9.2a2 2 0 0 1-2-1.8L6 7z"></path>
                                        <path d="M9 10V6a3 3 0 1 1 6 0v4"></path>
                                    </svg>
                                </span>
                                <span class="aieo-sc-trigger-count" aria-live="polite"><?php echo esc_html( (string) $count ); ?></span>
                            </button>
                        <?php break;
                    }
                endforeach;
                ?>
            </div>

            <?php
            
            $chat_enabled = false;
            foreach ( $bar_items as $bi ) {
                if ( $bi['slug'] === 'chat' && ! empty( $bi['enabled'] ) ) { $chat_enabled = true; break; }
            }
            if ( $chat_enabled ) : ?>
                <button type="button" class="aieo-sc-chat-desktop"
                        data-aieo-sc-toggle="chat"
                        data-position-mode="<?php echo esc_attr( $chat_desk_pos ); ?>"
                        aria-label="<?php esc_attr_e( 'Συνομιλία βοηθού', 'ai-eshop-optimizer' ); ?>">
                    <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                        <path d="M21 11.5a8.5 8.5 0 0 1-13.55 6.86L3 19l1.32-3.96A8.5 8.5 0 1 1 21 11.5z"/>
                    </svg>
                </button>
            <?php endif; ?>

            <!-- Fullscreen search modal — hosts the existing FiboSearch
                 form (`.dgwt-wcas-search-wrapp`) for mobile shoppers
                 who don't have the desktop header search visible. The
                 modal is empty until first open; the form is *moved*
                 (not cloned) into it so FiboSearch's autocomplete JS
                 keeps working on the same DOM node. On close the form
                 goes back to its original home so desktop is unaffected
                 if the viewport is widened. -->
            <div id="aieo-search-modal" class="aieo-search-modal" role="dialog" aria-modal="true"
                 aria-label="<?php esc_attr_e('Αναζήτηση', 'ai-eshop-optimizer'); ?>" hidden>
                <div class="aieo-search-modal__backdrop" data-aieo-search-close></div>
                <div class="aieo-search-modal__panel">
                    <button type="button" class="aieo-search-modal__close" data-aieo-search-close
                            aria-label="<?php esc_attr_e('Κλείσιμο αναζήτησης', 'ai-eshop-optimizer'); ?>">×</button>
                    <div class="aieo-search-modal__slot" data-aieo-search-slot>
                        <!-- FiboSearch form is moved here on open. -->
                    </div>
                </div>
            </div>

            <?php
            
            ?>

            <script>
            (function () {
                // Critical positioning + layout properties that absolutely
                // must be on the shell at narrow viewports. The MOBILE
                // entries get applied as inline `!important` styles so
                // they survive WP Rocket's "Remove Unused CSS" pass —
                // which on some pages strips our @media (max-width: 720px)
                // block, leaving the shell with the desktop default of
                // `display: contents` (which renders it as no-box, so
                // the trigger floats off in the corner via its own
                // desktop CSS instead of sitting inside the bar).
                // Stable mobile bar — fixed at viewport bottom, fixed
                // height. Padding deliberately uses CONSTANT values
                // (no env(safe-area-inset-bottom) in the math) so iOS
                // Safari can't grow the bar mid-scroll when the
                // address bar transitions update its safe-area
                // reading. We pad bottom 8px regardless of device;
                // the home-bar overlap is acceptable for now and far
                // less jarring than a bar that resizes on scroll.
                var MOBILE_STYLES = {
                    position:           'fixed',
                    left:               '0px',
                    right:              '0px',
                    bottom:             '0px',
                    width:              '100%',
                    height:             'var(--aieo-mbar-height, 58px)',
                    'min-height':       'var(--aieo-mbar-height, 58px)',
                    'max-height':       'var(--aieo-mbar-height, 58px)',
                    transform:          'none',
                    'z-index':          '999996',
                    display:            'flex',
                    'flex-wrap':        'nowrap',
                    'flex-direction':   'row',
                    'align-items':      'center',
                    'justify-content':  '<?php echo $mbar['justify']; ?>',
                    padding:            '8px 16px',
                    background:         '<?php echo $mbar['bg']; ?>',
                    'backdrop-filter':        '<?php echo $mbar['backdrop']; ?>',
                    '-webkit-backdrop-filter':'<?php echo $mbar['backdrop']; ?>',
                    'box-sizing':       'border-box',
                    'box-shadow':       '0 -2px 12px rgba(0, 0, 0, 0.10)',
                    color:              '<?php echo $mbar['icon']; ?>'
                };
                // Trigger flat-mobile styles — same reasoning. Without
                // these, the trigger keeps its desktop `position: fixed;
                // right: 20px; bottom: 20px` and renders as a separate
                // floating circle while only burger + search occupy the bar.
                var TRIGGER_STYLES = {
                    // 'relative' (not 'static') so the absolutely-positioned
                    // count badge anchors to the trigger itself — otherwise it
                    // anchors to the fixed shell and floats to the bar's far
                    // right even when a lone centred cart icon sits mid-bar.
                    position: 'relative',
                    right:    'auto',
                    bottom:   'auto',
                    left:     'auto',
                    top:      'auto',
                    width:    'auto',
                    height:   'auto',
                    'min-width':  '44px',
                    'min-height': '44px',
                    background:    'transparent',
                    'border-radius': '0',
                    'box-shadow':  'none',
                    color:    '<?php echo $mbar['icon']; ?>',
                    padding:  '8px',
                    'z-index': 'auto'
                };
                function applyMap(el, map) {
                    Object.keys(map).forEach(function (prop) {
                        el.style.setProperty(prop, map[prop], 'important');
                    });
                }
                // Burger + search buttons — when WP Rocket strips our
                // @media block, the burger keeps its desktop default
                // `display: none` and the search button inherits a
                // browser-default that may render zero-width. Bake the
                // mobile display + sizing inline so they always show.
                var BTN_STYLES = {
                    // Neutralise any desktop floating-circle positioning before
                    // flattening into the bar. The consent item carries
                    // `.aieo-cmp-reopen` (so it floats fixed bottom-left on
                    // desktop); on mobile every bar item must be a flat, in-flow
                    // flex child — reset position/offsets + circle chrome.
                    position:       'relative',
                    right:          'auto',
                    bottom:         'auto',
                    left:           'auto',
                    top:            'auto',
                    width:          'auto',
                    height:         'auto',
                    'border-radius':'0',
                    'box-shadow':   'none',
                    display:        'inline-flex',
                    'align-items':  'center',
                    'justify-content': 'center',
                    background:     'transparent',
                    border:         '0',
                    padding:        '8px',
                    'min-width':    '44px',
                    'min-height':   '44px',
                    color:          '<?php echo $mbar['icon']; ?>',
                    cursor:         'pointer'
                };
                function applyInline(shell) {
                    var isMobile = window.matchMedia('(max-width: 720px)').matches;
                    if (!isMobile) return;

                    // Bubble mode (cart-only + operator opt-in): leave the
                    // shell as display:contents so the trigger renders as the
                    // desktop floating circle on mobile too — skip every
                    // flat-bar inline override below.
                    if (shell.classList && shell.classList.contains('aieo-sc-shell--bubble')) { return; }

                    // Operator-controlled override — the Checkout
                    // Layout module's "Hide side cart on /checkout"
                    // toggle adds `aieo-checkout-hide-side-cart` to
                    // <body>. Without this guard our hard-baked
                    // inline `display: flex !important` on MOBILE_STYLES
                    // beats the stylesheet `display: none !important`
                    // from aieo-checkout-layout.css (inline `!important`
                    // outranks stylesheet `!important` in the cascade),
                    // so the bar stays visible regardless of the
                    // checkout-layout setting. Skip the inline apply
                    // AND set display:none ourselves so the JS-side
                    // wins.
                    var bodyEl = document.body;
                    if (bodyEl && bodyEl.classList.contains('aieo-checkout-hide-side-cart')) {
                        shell.style.setProperty('display', 'none', 'important');
                        return;
                    }

                    applyMap(shell, MOBILE_STYLES);
                    // Every configured bar button (.aieo-sc-bar-item that
                    // ISN'T the cart trigger) gets BTN_STYLES — covers
                    // burger, search, chat, plus any future additions.
                    shell.querySelectorAll('.aieo-sc-bar-item').forEach(function (btn) {
                        if (btn.classList.contains('aieo-sc-trigger')) return;
                        applyMap(btn, BTN_STYLES);
                    });
                    var trigger = shell.querySelector('.aieo-sc-trigger');
                    if (trigger) applyMap(trigger, TRIGGER_STYLES);
                }
                function hoistShell() {
                    var shell = document.querySelector('.aieo-sc-floating-shell');
                    if (!shell) return;
                    applyInline(shell);
                }
                if (document.readyState === 'loading') {
                    document.addEventListener('DOMContentLoaded', hoistShell);
                } else {
                    hoistShell();
                }
                var mq2 = window.matchMedia('(max-width: 720px)');
                if (mq2.addEventListener) {
                    mq2.addEventListener('change', hoistShell);
                }
            })();

            // Bar / floating-circle dispatch.
            //
            // Each item in the bottom bar (and the desktop AI-chat circle)
            // is wired through a single `[data-aieo-sc-toggle="<id>"]`
            // delegated handler. The item's id picks an entry in the
            // SURFACES table below — each entry exposes isOpen / open /
            // close. Click semantics are uniformly toggle: open the
            // surface if closed, close it if already open.
            //
            // Single-overlay rule: opening any surface closes every
            // other open surface first, so we never stack two
            // full-screen panels (search + chat, or nav + search, etc.).
            //
            // The cart trigger is special — the side-cart drawer's own
            // open/close lives in aieo-side-cart.js and listens for a
            // click on `.aieo-sc-trigger`; we don't intercept that here.
            (function () {
                function nav()            { return document.getElementById('aieo-navigator-root'); }
                function searchModal()    { return document.getElementById('aieo-search-modal'); }
                function addonChatWindow(){ return document.getElementById('aieo-floating-chat-window'); }
                function addonChatTrigger(){ return document.getElementById('aieo-floating-chat-trigger'); }
                function addonChatClose() { return document.getElementById('aieo-chat-close'); }
                function sideCartPanel()  { return document.querySelector('.aieo-sc-panel'); }

                // Per-option "selected" (active) colours. Applied INLINE so
                // they beat the RUCSS-defense inline button styles; the whole
                // feature is gated on SC_SEL_ON (the operator opt-in).
                var SC_SEL_ON = <?php echo $mbar['sel_on'] ? 'true' : 'false'; ?>;
                var SC_SEL    = <?php echo wp_json_encode($mbar['sel']); ?>;
                var SC_ICON   = '<?php echo $mbar['icon']; ?>';
                var SC_ICON_ACTIVE = '<?php echo $mbar['icon_active']; ?>';
                function barItem(id) {
                    var shell = document.querySelector('.aieo-sc-floating-shell');
                    return shell ? shell.querySelector('[data-aieo-sc-toggle="' + id + '"]') : null;
                }
                function setActive(id, on) {
                    var item = barItem(id);
                    if (!item) return;
                    item.classList.toggle('is-active', !!on);

                    // Selected colours apply ONLY to the flat mobile bar. The
                    // cart trigger doubles as the desktop fixed circle AND the
                    // cart-only bubble; stamping inline colours on those would
                    // override their stylesheet look (e.g. the #0e0e0e circle).
                    var shell = document.querySelector('.aieo-sc-floating-shell');
                    var flatBar = window.matchMedia('(max-width: 720px)').matches
                        && shell && !shell.classList.contains('aieo-sc-shell--bubble');

                    if (SC_SEL_ON) {
                        // Advanced: per-slug selected bg / fg / border.
                        if (!flatBar) {
                            // Desktop circle / bubble — clear any stray inline so the
                            // stylesheet circle reasserts instead of being pinned.
                            item.style.removeProperty('background');
                            item.style.removeProperty('color');
                            item.style.removeProperty('border');
                            return;
                        }
                        var c = SC_SEL[id];
                        if (on && c) {
                            item.style.setProperty('background', c.bg, 'important');
                            item.style.setProperty('color', c.fg, 'important');
                            item.style.setProperty('border', '1px solid ' + c.bc, 'important');
                        } else {
                            // Resting flat-bar look (mirrors BTN_STYLES / TRIGGER_STYLES).
                            item.style.setProperty('background', 'transparent', 'important');
                            item.style.setProperty('color', SC_ICON, 'important');
                            item.style.setProperty('border', '0', 'important');
                        }
                        return;
                    }

                    // Simple "Selected icon colour": tint the ACTIVE button's icon
                    // (menu / search / chat / cookies / cart) so it cues that
                    // tapping again closes the panel. Inline + !important to beat
                    // the RUCSS-defense inline button colour. Flat mobile bar only —
                    // the desktop circle / cart bubble keep their stylesheet look.
                    if (!SC_ICON_ACTIVE) return;
                    if (flatBar) {
                        item.style.setProperty('color', on ? SC_ICON_ACTIVE : SC_ICON, 'important');
                    } else {
                        item.style.removeProperty('color');
                    }
                }

                // FiboSearch wrapper bookkeeping — module-scoped so
                // open/close can move it back and forth without
                // re-querying every time.
                var searchEl   = null;
                var searchHome = null;
                function ensureSearchEl() {
                    if (searchEl) return;
                    searchEl   = document.querySelector('.dgwt-wcas-search-wrapp');
                    searchHome = searchEl ? searchEl.parentNode : null;
                }

                var SURFACES = {
                    nav: {
                        isOpen: function () { var r = nav(); return !!(r && !r.hasAttribute('hidden')); },
                        open:   function () { window.dispatchEvent(new CustomEvent('aieo:navigator:open')); },
                        close:  function () { window.dispatchEvent(new CustomEvent('aieo:navigator:close')); }
                    },
                    search: {
                        isOpen: function () { var m = searchModal(); return !!(m && !m.hasAttribute('hidden')); },
                        open:   function () {
                            var m = searchModal(); if (!m) return;
                            ensureSearchEl();
                            var slot = m.querySelector('[data-aieo-search-slot]');
                            if (searchEl && slot && searchEl.parentNode !== slot) {
                                slot.appendChild(searchEl);
                            }
                            // Dock the modal UNDER the (possibly sticky) site header:
                            // measure the header's bottom edge in the viewport and
                            // expose it as --rx-search-modal-top so the input panel +
                            // suggestions sit below the header instead of over it. Set
                            // on <html> so the body-appended FiboSearch suggestions
                            // (which live outside the modal) read the same value.
                            // The `aieo-search-open` class lets the CSS lift the sticky
                            // header above the modal while it is open.
                            var hdr = document.querySelector('header.wp-block-template-part');
                            var hBottom = hdr ? Math.max(0, Math.round(hdr.getBoundingClientRect().bottom)) : 0;
                            document.documentElement.style.setProperty('--rx-search-modal-top', hBottom + 'px');
                            document.documentElement.classList.add('aieo-search-open');
                            m.removeAttribute('hidden');
                            document.body.style.overflow = 'hidden';
                            requestAnimationFrame(function () {
                                var input = m.querySelector('input[type="search"], .dgwt-wcas-search-input');
                                if (input) input.focus({ preventScroll: true });
                            });
                        },
                        close: function () {
                            var m = searchModal(); if (!m) return;
                            var slot = m.querySelector('[data-aieo-search-slot]');
                            if (searchEl && searchHome && searchEl.parentNode === slot) {
                                searchHome.appendChild(searchEl);
                            }
                            m.setAttribute('hidden', '');
                            document.documentElement.classList.remove('aieo-search-open');
                            document.body.style.overflow = '';
                        }
                    },
                    chat: {
                        // Drive the chat addon's existing floating popup
                        // (#aieo-floating-chat-window) from our bar + desktop
                        // launcher. The addon's own trigger + welcome bubble
                        // are CSS-hidden — so the only way the popup ever
                        // opens is via our click here.
                        isOpen: function () {
                            var w = addonChatWindow();
                            if (!w) return false;
                            // The addon uses jQuery .fadeIn/.fadeOut: closed
                            // state has style.display === 'none', open has
                            // 'block'. getComputedStyle handles both inline
                            // and stylesheet-set values; offsetParent is
                            // unreliable for position: fixed elements.
                            return getComputedStyle(w).display !== 'none';
                        },
                        open: function () {
                            var t = addonChatTrigger();
                            if (t) { t.click(); }   // lazy-init + fadeIn handled by addon
                        },
                        close: function () {
                            var c = addonChatClose();
                            if (c) { c.click(); return; }
                            // Fallback if close button isn't in DOM yet — tap window directly.
                            if (window.jQuery) {
                                window.jQuery('#aieo-floating-chat-window').fadeOut(200);
                            }
                        }
                    },
                    cart: {
                        // Cart drawer toggle is owned by aieo-side-cart.js;
                        // letting that handler run is the entire point of
                        // not preventDefault-ing the click. So `cart`
                        // doesn't participate in the dispatch — the click
                        // falls through to the existing trigger handler.
                        isOpen: function () { var p = sideCartPanel(); return !!(p && !p.hasAttribute('hidden')); },
                        open:   function () { /* handled by side-cart.js */ },
                        close:  function () { /* handled by side-cart.js */ }
                    },
                    consent: {
                        // Cookie settings — opens the first-party consent
                        // preferences "over the bar" via the global API the
                        // consent banner exposes (window.aieoConsent).
                        isOpen: function () { return !!(window.aieoConsent && window.aieoConsent.isOpen && window.aieoConsent.isOpen()); },
                        open:   function () { if (window.aieoConsent && window.aieoConsent.open)  { window.aieoConsent.open(); } },
                        close:  function () { if (window.aieoConsent && window.aieoConsent.close) { window.aieoConsent.close(); } }
                    }
                };

                function closeAllExcept(id) {
                    Object.keys(SURFACES).forEach(function (k) {
                        if (k === id || k === 'cart') return; // cart manages itself
                        var s = SURFACES[k];
                        if (s.isOpen && s.isOpen()) { s.close(); }
                        setActive(k, false);
                    });
                }

                // Delegated click — works on the mobile bar AND the
                // desktop chat-launcher circle (both expose data-aieo-sc-toggle).
                document.addEventListener('click', function (e) {
                    var btn = e.target && e.target.closest && e.target.closest('[data-aieo-sc-toggle]');
                    if (!btn) return;
                    var id = btn.getAttribute('data-aieo-sc-toggle');
                    var surface = SURFACES[id];
                    if (!surface) return;
                    if (id === 'cart') {
                        // Don't intercept — let aieo-side-cart.js handle.
                        return;
                    }
                    e.preventDefault();
                    if (surface.isOpen()) {
                        surface.close();
                        setActive(id, false);
                    } else {
                        closeAllExcept(id);
                        surface.open();
                        setActive(id, true);
                    }
                    // The chat addon attaches a `$(document).on('click', ...)`
                    // outside-click handler that closes its popup whenever a
                    // click lands outside the trigger or window — and our bar
                    // chat icon counts as "outside". stopImmediatePropagation
                    // here prevents that handler from firing for THIS click,
                    // so the popup we just opened doesn't slam shut a tick later.
                    if (id === 'chat') { e.stopImmediatePropagation(); }
                });

                // Backdrop / × / Esc close — delegated for both modals.
                document.addEventListener('click', function (e) {
                    if (e.target.closest('[data-aieo-search-close]')) {
                        e.preventDefault();
                        SURFACES.search.close();
                        setActive('search', false);
                    } else if (e.target.closest('[data-aieo-chat-close]')) {
                        e.preventDefault();
                        SURFACES.chat.close();
                        setActive('chat', false);
                    }
                });
                document.addEventListener('keydown', function (e) {
                    if (e.key !== 'Escape') return;
                    if (SURFACES.search.isOpen()) { SURFACES.search.close(); setActive('search', false); }
                    else if (SURFACES.chat.isOpen()) { SURFACES.chat.close(); setActive('chat', false); }
                });

                // Cart "selected" — the side-cart drawer toggles
                // `.aieo-sc-root.is-open`; mirror that onto the cart bar item
                // so its selected colours track the open/close of the panel.
                var scRoot = document.getElementById('aieo-sc-root');
                if (scRoot && window.MutationObserver) {
                    new MutationObserver(function () {
                        setActive('cart', scRoot.classList.contains('is-open'));
                    }).observe(scRoot, { attributes: true, attributeFilter: ['class'] });
                }

                // Cookie item "selected" — the consent banner toggles its own
                // [hidden] (e.g. when the shopper hits Save / Decline inside the
                // panel), so mirror that onto the cookie bar item's active state.
                var cmpEl = document.getElementById('aieo-consent-banner');
                if (cmpEl && window.MutationObserver) {
                    new MutationObserver(function () {
                        setActive('consent', !cmpEl.hasAttribute('hidden'));
                    }).observe(cmpEl, { attributes: true, attributeFilter: ['hidden'] });
                }

                // ── Open-on-tab event API ──────────────────────────────────
                // Host surfaces (the AIEO Navigator overlay, the theme header
                // icons, etc.) open the drawer on a specific tab by dispatching
                // `aieo:side-cart:open:<key>`. The Navigator fires it on
                // `document` for rail-item clicks and on `window` for the
                // keyboard / right-pane path (aieo-navigator.js), so we listen
                // on BOTH. We drive the existing controls — click the
                // `.aieo-sc-trigger` to open (only when closed; it's a toggle),
                // then the matching `.aieo-sc-tab` button, whose own handler
                // (in aieo-side-cart.js) switches the body AND hydrates the
                // recently-viewed / favorites lists. This also makes the
                // long-dead "Πρόσφατα προϊόντα" navigator action work.
                // Key → tab slug: cart→bag, recently-viewed→viewed, favorites→favorites.
                (function () {
                    var TAB_FOR = {
                        'cart': 'bag', 'bag': 'bag',
                        'recently-viewed': 'viewed', 'viewed': 'viewed',
                        'favorites': 'favorites'
                    };
                    function openOnTab(tab) {
                        var root = document.getElementById('aieo-sc-root');
                        if (!root) { return; }
                        var trigger = root.querySelector('.aieo-sc-trigger');
                        if (trigger && !root.classList.contains('is-open')) { trigger.click(); }
                        if (tab) {
                            var btn = root.querySelector('.aieo-sc-tab[data-aieo-sc-tab="' + tab + '"]');
                            if (btn) { btn.click(); }
                        }
                    }
                    Object.keys(TAB_FOR).forEach(function (key) {
                        var handler = function () { openOnTab(TAB_FOR[key]); };
                        document.addEventListener('aieo:side-cart:open:' + key, handler);
                        window.addEventListener('aieo:side-cart:open:' + key, handler);
                    });
                })();
            })();
            </script>

            <!-- Side panel — starts `hidden` so its off-canvas bounding box doesn't
                 sit in the document layout while closed (no false-positive horizontal
                 overflow). JS removes the attribute on open and restores it on
                 transitionend after close. -->
            <aside class="aieo-sc-panel" aria-hidden="true" hidden>
                <!-- Reco-column toggle (Rey-style) — collapses the
                     "You might like" sidebar when the shopper wants
                     more horizontal room for the cart contents. State
                     persists across sessions in localStorage. Hidden
                     on mobile (the column is already hidden there). -->
                <button type="button" class="aieo-sc-reco-toggle"
                        data-aieo-sc-reco-toggle
                        aria-label="<?php esc_attr_e('Toggle recommendations', 'ai-eshop-optimizer'); ?>">
                    <svg viewBox="0 0 40 64" aria-hidden="true" focusable="false" class="aieo-sc-reco-toggle-icon">
                        <polygon fill="currentColor" points="39.5 32 6.83 64 0.5 57.38 26.76 32 0.5 6.62 6.83 0"></polygon>
                    </svg>
                </button>

                <!-- Left column: "You might like" recommendations -->
                <div class="aieo-sc-reco-col" data-aieo-sc-reco>
                    <div class="aieo-sc-reco-title"><?php echo esc_html($this->label('side_cart_left_heading', __('You might like..', 'ai-eshop-optimizer'))); ?></div>
                    <ul class="aieo-sc-reco-list" data-aieo-sc-reco-list>
                        <!-- filled by JS from the DMM bundle cache -->
                    </ul>
                </div>

                <!-- Right column: main cart -->
                <div class="aieo-sc-main-col">
                    <!-- Header with tabs + close -->
                    <header class="aieo-sc-header">
                        <nav class="aieo-sc-tabs" role="tablist">
                            <button type="button" class="aieo-sc-tab is-active" role="tab"
                                    data-aieo-sc-tab="bag" aria-selected="true">
                                <?php echo esc_html($this->label('side_cart_tab_bag', __('SHOPPING BAG', 'ai-eshop-optimizer'))); ?>
                                <sup class="aieo-sc-tab-count" data-aieo-sc-count><?php echo (int) $count; ?></sup>
                            </button>
                            <button type="button" class="aieo-sc-tab" role="tab"
                                    data-aieo-sc-tab="viewed" aria-selected="false">
                                <?php echo esc_html($this->label('side_cart_tab_viewed', __('RECENTLY VIEWED', 'ai-eshop-optimizer'))); ?>
                                <sup class="aieo-sc-tab-count" data-aieo-sc-rv-count>0</sup>
                            </button>
                            <!-- Favorites tab — permanent, always visible.
                                 Count reflects items in the OM PPE
                                 wishlist cookie; body fetched via
                                 aieo_get_products_dtos on activation. -->
                            <button type="button" class="aieo-sc-tab aieo-sc-tab-favorites" role="tab"
                                    data-aieo-sc-tab="favorites" aria-selected="false">
                                <?php echo esc_html($this->label('side_cart_tab_favorites', __('FAVORITES', 'ai-eshop-optimizer'))); ?>
                                <sup class="aieo-sc-tab-count" data-aieo-sc-fav-count>0</sup>
                            </button>
                            <?php if ( $this->is_quick_view_context() ) : ?>
                            <!-- Quick View tab — placed LAST so the
                                 permanent tabs above are stable. Hidden
                                 until the shopper activates a "Discover
                                 it" trigger; JS flips `hidden` off and
                                 keeps the static label (the product name
                                 lives in the body, not the tab).
                                 Only emitted on pages where the "Discover
                                 it" trigger can actually exist (shop,
                                 product_cat, product_tag, product_brand,
                                 pa_need archives) — pointless to include
                                 the tab + empty body on PDP / cart /
                                 checkout / account pages, where the user
                                 has no way to activate it. -->
                            <button type="button" class="aieo-sc-tab aieo-sc-tab-qv" role="tab"
                                    data-aieo-sc-tab="quickview" aria-selected="false" hidden
                                    data-aieo-sc-qv-tab>
                                <span class="aieo-sc-qv-tab-label"><?php echo esc_html($this->label('side_cart_qv_tab', __('QUICK VIEW', 'ai-eshop-optimizer'))); ?></span>
                            </button>
                            <?php endif; ?>
                        </nav>
                        <button type="button" class="aieo-sc-close" aria-label="<?php esc_attr_e('Close cart', 'ai-eshop-optimizer'); ?>">→</button>
                    </header>

                    <!-- Tab bodies wrapper — owns the scroll area on
                         desktop so the footer below stays pinned. -->
                    <div class="aieo-sc-tab-bodies">
                        <div class="aieo-sc-body" data-aieo-sc-body="bag">
                            <!-- Free shipping bar -->
                            <div class="aieo-sc-fs" data-aieo-sc-fs>
                                <p class="aieo-sc-fs-msg" data-aieo-sc-fs-msg>
                                    <?php
                                    $msg_initial = (string) ($fs['msg_initial'] ?? '');
                                    $formatted = str_replace(
                                        array('{amount}', '{remaining}'),
                                        array(wc_price($thresh), wc_price($remaining)),
                                        $msg_initial
                                    );
                                    echo wp_kses_post($formatted);
                                    ?>
                                </p>
                                <div class="aieo-sc-fs-bar"><span data-aieo-sc-fs-fill style="width: 0%"></span></div>
                            </div>

                            <!-- Free-gift offers — banner per active offer
                                 (port of OM_Brand_Gifts_Cart::render_side_cart_bars
                                 from om-discount-manager). The wrapper stays in
                                 the DOM even when there are no offers so the
                                 ajax_get response can swap its inner HTML. -->
                            <div class="aieo-sc-brand-gifts" data-aieo-sc-brand-gifts>
                                <?php
                                if (class_exists('AIEO_DMM_Brand_Gifts')) {
                                    
                                    echo AIEO_DMM_Brand_Gifts::instance()->render_side_cart_section();
                                }
                                ?>
                            </div>

                            <!-- Cart items -->
                            <div class="aieo-sc-content" data-aieo-sc-content>
                                <?php echo $this->render_cart_items_html(); ?>
                            </div>

                            <!-- Free-gift reward cards (A1) — locked
                                 placeholders before threshold, claimable
                                 cards once met. Lives in its own slot
                                 so the cart-items markup stays clean. -->
                            <div class="aieo-sc-rewards" data-aieo-sc-rewards>
                                <?php
                                if (class_exists('AIEO_DMM_Brand_Gifts')) {
                                    
                                    echo AIEO_DMM_Brand_Gifts::instance()->render_cart_reward_cards();
                                }
                                ?>
                            </div>

                            <!-- Continue shopping -->
                            <a href="<?php echo esc_url(wc_get_page_permalink('shop')); ?>" class="aieo-sc-continue">
                                <?php echo esc_html($this->label('side_cart_continue', __('CONTINUE SHOPPING', 'ai-eshop-optimizer'))); ?>
                            </a>

                            <!-- You may also like carousel — sits at the
                                 end of the bag body's scroll so the
                                 shopper reaches it after the items list. -->
                            <section class="aieo-sc-maylike" data-aieo-sc-maylike>
                                <header class="aieo-sc-maylike-head">
                                    <span class="aieo-sc-maylike-title"><?php echo esc_html($this->label('side_cart_maylike_title', __('YOU MAY ALSO LIKE...', 'ai-eshop-optimizer'))); ?></span>
                                    <span class="aieo-sc-maylike-nav">
                                        <button type="button" class="aieo-sc-maylike-arrow aieo-sc-maylike-prev" data-aieo-sc-maylike-prev aria-label="<?php esc_attr_e('Previous', 'ai-eshop-optimizer'); ?>">&#8249;</button>
                                        <span class="aieo-sc-maylike-dots" data-aieo-sc-maylike-dots></span>
                                        <button type="button" class="aieo-sc-maylike-arrow aieo-sc-maylike-next" data-aieo-sc-maylike-next aria-label="<?php esc_attr_e('Next', 'ai-eshop-optimizer'); ?>">&#8250;</button>
                                    </span>
                                </header>
                                <div class="aieo-sc-maylike-track" data-aieo-sc-maylike-track>
                                    <!-- filled by JS from the DMM bundle cache -->
                                </div>
                            </section>
                        </div>

                        <div class="aieo-sc-body" data-aieo-sc-body="viewed" hidden>
                            <!-- Filled by JS (localStorage-first via the existing DMM recently-viewed endpoint). -->
                        </div>

                        <?php if ( $this->is_quick_view_context() ) : ?>
                        <!-- Quick view body — populated by JS from the
                             clicked archive product card; no server
                             round-trip for the basic display. Gated on
                             the same archive-context check as the tab
                             above — see is_quick_view_context(). -->
                        <div class="aieo-sc-body" data-aieo-sc-body="quickview" hidden>
                            <!-- filled by JS from the .product card DOM -->
                        </div>
                        <?php endif; ?>

                        <!-- Favorites body — populated by JS from
                             OM PPE wishlist cookie + endpoint. -->
                        <div class="aieo-sc-body" data-aieo-sc-body="favorites" hidden>
                            <!-- filled by JS from the wishlist cookie -->
                        </div>
                    </div>

                    <!-- SHARED footer — lives outside the tab bodies so
                         subtotal + coupon + CART/CHECKOUT (plus the
                         YOU MAY ALSO LIKE carousel) are visible from
                         every tab. Desktop pins it to the bottom of
                         the main column (always visible regardless of
                         how many items the active body has). Mobile
                         lets it sit at the natural end of the scroll. -->
                    <footer class="aieo-sc-footer">
                        <div class="aieo-sc-subtotal-row">
                            <span class="aieo-sc-subtotal-label"><?php echo esc_html($this->label('side_cart_subtotal_label', __('SUBTOTAL:', 'ai-eshop-optimizer'))); ?></span>
                            <span class="aieo-sc-subtotal-val" data-aieo-sc-subtotal><?php echo wp_kses_post($subtotal); ?></span>
                        </div>
                        <button type="button" class="aieo-sc-coupon-toggle" data-aieo-sc-coupon-toggle>
                            <?php echo esc_html($this->label('side_cart_coupon_link', __('Have a Coupon?', 'ai-eshop-optimizer'))); ?>
                        </button>
                        <form class="aieo-sc-coupon-form" data-aieo-sc-coupon-form style="display:none;">
                            <input type="text" name="coupon_code" placeholder="<?php esc_attr_e('Coupon code', 'ai-eshop-optimizer'); ?>">
                            <button type="submit"><?php esc_html_e('APPLY', 'ai-eshop-optimizer'); ?></button>
                        </form>
                        <?php
                        
                        
                        
                        
                        $show_cart_btn = (bool) self::setting('side_cart_show_cart_btn', true);
                        
                        
                        
                        $aieo_save_btn = ( class_exists('AIEO_Carts_Frontend') && AIEO_Carts_Frontend::instance()->show_in_sidecart() )
                            ? AIEO_Carts_Frontend::instance()->side_cart_button_html() : '';
                        $aieo_has_left = ( '' !== $aieo_save_btn ) || $show_cart_btn;
                        ?>
                        <div class="aieo-sc-actions<?php echo $aieo_has_left ? '' : ' aieo-sc-actions-checkout-only'; ?><?php echo ( '' !== $aieo_save_btn ) ? ' aieo-sc-actions-save' : ''; ?>">
                            <?php if ('' !== $aieo_save_btn): ?>
                                <?php echo $aieo_save_btn; ?>
                            <?php elseif ($show_cart_btn): ?>
                                <a href="<?php echo esc_url(wc_get_cart_url()); ?>" class="aieo-sc-btn aieo-sc-btn-cart">
                                    <?php echo esc_html($this->label('side_cart_btn_cart', __('CART', 'ai-eshop-optimizer'))); ?>
                                </a>
                            <?php endif; ?>
                            <a href="<?php echo esc_url(wc_get_checkout_url()); ?>" class="aieo-sc-btn aieo-sc-btn-checkout">
                                <?php echo esc_html($this->label('side_cart_btn_checkout', __('CHECKOUT', 'ai-eshop-optimizer'))); ?>
                                <span aria-hidden="true">⟶</span>
                            </a>
                        </div>
                    </footer>
                </div>
            </aside>

            <!-- Scrim -->
            <div class="aieo-sc-scrim" aria-hidden="true"></div>
        </div>
        <?php
    }

    
    public function render_archive_quick_view_trigger() {
        global $product;
        if (!$product || !is_a($product, 'WC_Product')) { return; }
        $pid = (int) $product->get_id();
        if ($pid <= 0) { return; }
        $label = $this->label('side_cart_quick_view_label', __('Discover it', 'ai-eshop-optimizer'));
        echo '<button type="button"'
           . ' class="aieo-quick-view-trigger"'
           . ' data-aieo-quick-view="' . esc_attr((string) $pid) . '">'
           . esc_html($label)
           . '</button>';
    }

    
    
    

    public function enqueue_assets() {
        if (is_admin()) { return; }

        $css_path = self::asset_dir() . 'assets/css/aieo-side-cart.css';
        $css_ver  = file_exists($css_path) ? (string) filemtime($css_path) : AIEO_VERSION;
        wp_enqueue_style(
            'aieo-side-cart',
            self::asset_uri() . 'assets/css/aieo-side-cart.css',
            array(),
            $css_ver
        );

        
        
        
        
        $btn_css = $this->build_action_button_css();
        if ($btn_css !== '') {
            wp_add_inline_style('aieo-side-cart', $btn_css);
        }

        $js_path = self::asset_dir() . 'assets/js/aieo-side-cart.js';
        $js_ver  = file_exists($js_path) ? (string) filemtime($js_path) : AIEO_VERSION;

        
        
        
        
        $iapi_on = method_exists('AIEO_DMM', 'is_iapi_enabled') && AIEO_DMM::is_iapi_enabled('side_cart');
        if ($iapi_on
            && function_exists('wp_enqueue_script_module')
            && defined('AIEO_RUNTIME_DIR')
            && file_exists(AIEO_RUNTIME_DIR . 'assets/js/aieo-side-cart-iapi.js')
        ) {
            wp_register_script_module(
                '@aieo/side-cart-iapi',
                AIEO_RUNTIME_URI . 'assets/js/aieo-side-cart-iapi.js',
                array('@wordpress/interactivity', '@aieo/iapi-bootstrap'),
                (string) filemtime(AIEO_RUNTIME_DIR . 'assets/js/aieo-side-cart-iapi.js')
            );
            wp_enqueue_script_module('@aieo/side-cart-iapi');
        } else {
            wp_enqueue_script(
                'aieo-side-cart',
                self::asset_uri() . 'assets/js/aieo-side-cart.js',
                array('jquery'),
                $js_ver,
                true
            );
        }

        
        
        
        
        
        
        
        
        $contents_rel = 'assets/css/aieo-side-cart-contents.css';
        if ((!defined('SCRIPT_DEBUG') || !SCRIPT_DEBUG)
            && file_exists(self::asset_dir() . 'assets/css/aieo-side-cart-contents.min.css')) {
            $contents_rel = 'assets/css/aieo-side-cart-contents.min.css';
        }
        $contents_css_path = self::asset_dir() . $contents_rel;
        $contents_css_ver  = file_exists($contents_css_path) ? (string) filemtime($contents_css_path) : (defined('AIEO_VERSION') ? AIEO_VERSION : '1.0');
        $contents_css_url  = add_query_arg('ver', $contents_css_ver, self::asset_uri() . $contents_rel);

        $side_cart_data = array(
            'ajaxurl'           => admin_url('admin-ajax.php'),
            'contentsCss'       => $contents_css_url,
            'debounce'          => 500,
            
            
            
            
            
            'nonce'             => wp_create_nonce('aieo_side_cart'),
            
            
            
            'recommendationsNonce' => wp_create_nonce('aieo_recommendations'),
            
            
            'openOnAdd'         => (bool) self::setting('side_cart_open_on_add', false),
            
            
            
            'favEnabledTypes'   => class_exists('AIEO_Wishlist') ? AIEO_Wishlist::enabled_types_list() : null,
            'i18n'     => array(
                'emptyBag'  => __('Your bag is empty.', 'ai-eshop-optimizer'),
                
                
                
                'addToBag'  => $this->label('side_cart_btn_addbag', __('ADD TO BAG', 'ai-eshop-optimizer')),
                'rvEmpty'   => $this->label('side_cart_rv_empty',   __('No recently viewed products.', 'ai-eshop-optimizer')),
                'qvViewProduct' => $this->label('side_cart_qv_view_product', __('View product details', 'ai-eshop-optimizer')),
                'rvAtc'     => $this->label('side_cart_rv_atc',     __('ADD TO BAG', 'ai-eshop-optimizer')),
                'favEmpty'        => $this->label('side_cart_fav_empty',  __('No favorites yet.', 'ai-eshop-optimizer')),
                
                
                
                'favBrandsTitle'  => $this->label('side_cart_fav_brands_title', __('Following', 'ai-eshop-optimizer')),
                'favBrandProducts'=> $this->label('side_cart_fav_brand_products', __('products', 'ai-eshop-optimizer')),
                'inCart'    => $this->label('side_cart_in_cart_label', __('✓ IN BAG', 'ai-eshop-optimizer')),
                'couponErr' => __('Coupon could not be applied.', 'ai-eshop-optimizer'),
                'noVariationsInStock' => __('No in-stock variations available.', 'ai-eshop-optimizer'),
                'pickVariation'       => __('— Pick variation —', 'ai-eshop-optimizer'),
                'recoSourceLabels'    => array(
                    'sidecart_recently_viewed' => __('Recently viewed (side cart)', 'ai-eshop-optimizer'),
                    'sidecart_might_like'      => __('You might like', 'ai-eshop-optimizer'),
                    'sidecart_may_also_like'   => __('You may also like', 'ai-eshop-optimizer'),
                    'sidecart_favorites'       => __('Favorites', 'ai-eshop-optimizer'),
                    'sidecart_quickview'       => __('Quick view', 'ai-eshop-optimizer'),
                ),
            ),
            
            
            
            
            'cartContents' => $this->get_cart_product_ids(),
            
            
            
            
            
            'badgePlacements' => class_exists('AIEO_DMM_Badges')
                ? AIEO_DMM_Badges::get_settings()['placements']
                : array(
                    'archive'   => array('location' => 'below_favourite', 'shape' => 'circle'),
                    'pdp'       => array('location' => 'top_left',         'shape' => 'chip'),
                    'recos'     => array('location' => 'inline',           'shape' => 'chip'),
                    'side_cart' => array('location' => 'inline',           'shape' => 'chip'),
                ),
            
            
            'badgeSizes' => class_exists('AIEO_DMM_Badges')
                ? AIEO_DMM_Badges::get_settings()['sizes']
                : array(),
            
            
            
            
            'ga4_enabled' => ( ! class_exists('AIEO_DMM_Conversions') )
                || ( 'aieo' === AIEO_DMM_Conversions::get_config()['ga_source']
                  && 'no'   !== AIEO_DMM_Conversions::get_config()['ga_send_browser'] ),
        );

        if ($iapi_on) {
            
            
            add_action('wp_print_footer_scripts', static function () use ($side_cart_data) {
                echo '<script id="aieo-side-cart-data">'
                    . 'window.aieoSideCart = ' . wp_json_encode($side_cart_data) . ';'
                    . '</script>' . "\n";
            }, 5);
        } else {
            wp_localize_script('aieo-side-cart', 'aieoSideCart', $side_cart_data);
        }
    }

    
    
    

    
    
    private function get_cart_product_ids() {
        if (!function_exists('WC') || !WC()->cart) { return array(); }
        $ids = array();
        foreach (WC()->cart->get_cart() as $item) {
            if (!empty($item['product_id'])) {
                $ids[] = (int) $item['product_id'];
            }
        }
        return array_values(array_unique($ids));
    }

    private function get_cart_subtotal_raw() {
        if (!function_exists('WC') || !WC()->cart) { return 0.0; }

        
        
        
        
        
        if (class_exists('AIEO_DMM_Pricing_Engine')) {
            $cart_ids = array();
            foreach (WC()->cart->get_cart() as $item) {
                if (!empty($item['product_id']))   { $cart_ids[] = (int) $item['product_id']; }
                if (!empty($item['variation_id'])) { $cart_ids[] = (int) $item['variation_id']; }
            }
            $cart_ids = array_unique(array_filter($cart_ids));
            if (!empty($cart_ids)) {
                AIEO_DMM_Pricing_Engine::instance()->preload_role_prices($cart_ids);
            }
        }

        $total = 0.0;
        foreach (WC()->cart->get_cart() as $item) {
            if (!empty($item['_aieo_dmm_gift_offer_id'])) { continue; }
            $product = isset($item['data']) ? $item['data'] : null;
            if (!$product || !is_a($product, 'WC_Product')) { continue; }
            $total += ((float) $product->get_price()) * max(0, (int) $item['quantity']);
        }
        return $total;
    }

    
    private function get_free_shipping_settings() {
        if (class_exists('AIEO_DMM_Free_Shipping')) {
            
            $engine = new AIEO_DMM_Free_Shipping();
            if (method_exists($engine, 'get_settings')) {
                return (array) $engine->get_settings();
            }
        }
        
        $legacy = get_option('om_fs_settings', array());
        return is_array($legacy) ? $legacy : array();
    }

    
    private function label($key, $default) {
        $val = self::setting($key, '');
        $val = is_string($val) ? trim($val) : '';
        return $val !== '' ? $val : (string) $default;
    }

    
    private function is_quick_view_context() {
        
        
        
        if (is_admin()) { return false; }
        if (function_exists('is_shop') && is_shop()) { return true; }
        
        
        if (function_exists('is_product_taxonomy') && is_product_taxonomy()) { return true; }
        if (function_exists('is_post_type_archive') && is_post_type_archive('product')) { return true; }
        return false;
    }

    
    private function build_action_button_css() {
        $rules = array();

        
        
        
        $accent = (string) self::setting('side_cart_accent_color', '');
        $reco   = (string) self::setting('side_cart_reco_title_color', '');
        $root   = array();
        if ($accent !== '') { $root[] = '--aieo-sc-accent:' . esc_attr($accent); }
        if ($reco   !== '') { $root[] = '--aieo-sc-reco-title-color:' . esc_attr($reco); }
        if (!empty($root)) { $rules[] = '.aieo-sc-root{' . implode(';', $root) . ';}'; }

        $show_cart = (bool) self::setting('side_cart_show_cart_btn', true);
        if (!$show_cart) {
            
            
            
            $rules[] = '.aieo-sc-actions.aieo-sc-actions-checkout-only{grid-template-columns:1fr !important;}';
        }

        foreach (array('cart', 'checkout', 'save') as $which) {
            
            
            
            
            
            $sel    = '.aieo-sc-root .aieo-sc-btn-' . $which;
            $sel_h  = $sel . ':hover,' . $sel . ':focus,' . $sel . ':visited';

            $bg     = (string) self::setting('side_cart_btn_' . $which . '_bg', '');
            $color  = (string) self::setting('side_cart_btn_' . $which . '_color', '');
            $bstyle = (string) self::setting('side_cart_btn_' . $which . '_border_style', '');
            $bcolor = (string) self::setting('side_cart_btn_' . $which . '_border_color', '');
            $bwidth = (int)    self::setting('side_cart_btn_' . $which . '_border_width', 0);
            $radius = self::setting('side_cart_btn_' . $which . '_radius', null);

            $base_decls = array();
            $hover_decls = array();
            if ($bg !== '') {
                $base_decls[]  = 'background:' . esc_attr($bg) . ' !important';
                $hover_decls[] = 'background:' . esc_attr($bg) . ' !important';
            }
            if ($color !== '') {
                $base_decls[]  = 'color:' . esc_attr($color) . ' !important';
                $hover_decls[] = 'color:' . esc_attr($color) . ' !important';
            }
            if ($bstyle !== '' && $bstyle !== 'none') {
                $w = max(0, min(8, (int) $bwidth));
                $c = $bcolor !== '' ? esc_attr($bcolor) : 'currentColor';
                $base_decls[] = 'border:' . $w . 'px ' . esc_attr($bstyle) . ' ' . $c . ' !important';
            } elseif ($bstyle === 'none') {
                $base_decls[] = 'border:0 !important';
            }
            if ($radius !== null && $radius !== '') {
                $base_decls[] = 'border-radius:' . max(0, min(48, (int) $radius)) . 'px !important';
            }

            if (!empty($base_decls))  { $rules[] = $sel    . '{' . implode(';', $base_decls)  . ';}'; }
            if (!empty($hover_decls)) { $rules[] = $sel_h  . '{' . implode(';', $hover_decls) . ';}'; }
        }

        return empty($rules) ? '' : implode('', $rules);
    }
}

endif;
