// AnchorKit Table of Contents JavaScript
/* AnchorKit – Table of Contents front-end
   Handles smooth-scroll, collapsible behaviour, and active heading highlighting. 
   
   Enhancement: The entire TOC title is now clickable to toggle expand/collapse,
   not just the arrow button. This provides a larger click target and improves usability.
   
   Added improvements for accessibility, keyboard navigation, and screen reader support.
*/
(function () {
    // Fallback defaults if settings are not localized (e.g., Elementor editor)
    if (typeof anchorkitTocSettings === 'undefined') {
        window.anchorkitTocSettings = {
            smooth_scroll: true,
            scroll_offset: 0,
            collapsible: true,
            initial_state: 'expanded',
            scroll_spy: true,
            sticky: false,
            sticky_position: 'content',
            sticky_offset: 20,
            toc_width_percent: null,
            entrance_animation: false,
            animation_type: 'fade',
            scroll_easing: 'ease-in-out',
            scroll_duration: 500
        };
    }

    /**
     * Custom smooth scroll with easing support (Pro feature)
     * @param {number} targetY - Target scroll position
     * @param {number} duration - Animation duration in milliseconds
     * @param {string} easing - CSS easing function (ease-in-out, linear, cubic-bezier, etc.)
     */
    function smoothScrollTo(targetY, duration, easing) {
        const startY = window.pageYOffset;
        const distance = targetY - startY;
        const startTime = performance.now();

        // Parse easing function
        const easingFunctions = {
            'linear': t => t,
            'ease': t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
            'ease-in': t => t * t,
            'ease-out': t => t * (2 - t),
            'ease-in-out': t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
            // Bouncy cubic-bezier(0.68, -0.55, 0.265, 1.55)
            'cubic-bezier(0.68, -0.55, 0.265, 1.55)': t => {
                const c1 = 0.68, c2 = -0.55, c3 = 0.265, c4 = 1.55;
                const t2 = 1 - t;
                return 3 * t2 * t2 * t * c2 + 3 * t2 * t * t * c4 + t * t * t;
            }
        };

        const easingFunc = easingFunctions[easing] || easingFunctions['ease-in-out'];

        function scroll() {
            const currentTime = performance.now();
            const elapsed = currentTime - startTime;
            const progress = Math.min(elapsed / duration, 1);
            const easedProgress = easingFunc(progress);

            window.scrollTo(0, startY + (distance * easedProgress));

            if (progress < 1) {
                requestAnimationFrame(scroll);
            }
        }

        requestAnimationFrame(scroll);
    }

    /**
     * Initialize TOC functionality for a specific container
     * Can be called multiple times for Elementor editor updates
     */
    function initializeTocContainer(toc) {
        // Prevent duplicate initialization
        if (toc.hasAttribute('data-anchorkit-initialized')) {
            return;
        }
        toc.setAttribute('data-anchorkit-initialized', 'true');

        // Merge per-instance data attributes with defaults.
        // Instance-scoped TOCs already embed their effective defaults (sourced from
        // auto-insert settings) in data attributes. Use a guarded fallback object so
        // global auto-insert defaults cannot override widget/block overrides at runtime.
        const isInstanceScoped = toc.hasAttribute('data-anchorkit-instance');
        const instanceDefaults = {
            collapsible: true,
            initial_state: 'expanded',
            hierarchical: true,
            smooth_scroll: true,
            scroll_offset: 0,
            scroll_spy: true,
            sticky: false,
            sticky_position: 'content',
            sticky_offset: 20,
            alignment: 'center',
            toc_width_percent: null,
            entrance_animation: false,
            animation_type: 'fade',
            scroll_easing: 'ease-in-out',
            scroll_duration: 500,
        };

        const defaults = isInstanceScoped ? instanceDefaults : anchorkitTocSettings;
        const getBool = (attr, fallback) => toc.hasAttribute(attr) ? toc.getAttribute(attr) === '1' : fallback;
        const getInt = (attr, fallback) => {
            const raw = toc.getAttribute(attr);
            const parsed = raw === null ? NaN : parseInt(raw, 10);
            return Number.isNaN(parsed) ? fallback : parsed;
        };
        const getStr = (attr, fallback) => {
            const raw = toc.getAttribute(attr);
            return raw !== null && raw !== '' ? raw : fallback;
        };

        const settings = {
            collapsible: getBool('data-collapsible', defaults.collapsible),
            initial_state: getStr('data-initial-state', defaults.initial_state || 'expanded'),
            hierarchical: getBool('data-hierarchical', defaults.hierarchical),
            smooth_scroll: getBool('data-smooth-scroll', defaults.smooth_scroll),
            scroll_offset: getInt('data-scroll-offset', defaults.scroll_offset || 0),
            scroll_spy: getBool('data-scroll-spy', defaults.scroll_spy),
            sticky: getBool('data-sticky', defaults.sticky),
            sticky_position: getStr('data-sticky-position', defaults.sticky_position || 'content'),
            sticky_offset: getInt('data-sticky-offset', defaults.sticky_offset || 20),
            alignment: getStr('data-alignment', defaults.alignment || 'center'),
            toc_width_percent: getInt('data-toc-width', defaults.toc_width_percent ?? null),
            entrance_animation: getBool('data-entrance-animation', defaults.entrance_animation),
            animation_type: getStr('data-animation-type', defaults.animation_type || 'fade'),
            scroll_easing: getStr('data-scroll-easing', defaults.scroll_easing || 'ease-in-out'),
            scroll_duration: getInt('data-scroll-duration', defaults.scroll_duration || 500),
        };

        // Normalize sticky position to avoid casing/whitespace issues
        settings.sticky_position = (settings.sticky_position || 'content').toString().trim().toLowerCase();

        // Ensure container classes reflect settings (Elementor preview may miss some)
        toc.classList.toggle('anchorkit-toc-hierarchical', !!settings.hierarchical);
        if (settings.collapsible) {
            toc.classList.add('anchorkit-toc-collapsible');
            // Only set initial state classes if not already set by user interaction
            if (!toc.classList.contains('anchorkit-toc-collapsed') && !toc.classList.contains('anchorkit-toc-expanded')) {
                if (settings.initial_state === 'collapsed') {
                    toc.classList.add('anchorkit-toc-collapsed');
                } else {
                    toc.classList.add('anchorkit-toc-expanded');
                }
            }
        } else {
            toc.classList.remove('anchorkit-toc-collapsible', 'anchorkit-toc-collapsed', 'anchorkit-toc-expanded');
        }

        // If smooth scroll is disabled, override page-level CSS scroll-behavior if present
        if (!settings.smooth_scroll) {
            document.documentElement.style.scrollBehavior = 'auto';
            document.body.style.scrollBehavior = 'auto';
        }

        // Runtime fallback: ensure headings have IDs so anchors work (Elementor content)
        (function ensureHeadingIds() {
            const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
            const used = new Set();
            // Build a set of excluded selectors from container data attribute if any
            const excludedSelectors = (toc.getAttribute('data-exclude-selectors') || '')
                .split(',')
                .map(s => s.trim())
                .filter(Boolean);

            headings.forEach(h => {
                if (h.id) { used.add(h.id); return; }

                // Skip headings that match any excluded selector (class or id)
                const matchesExcluded = excludedSelectors.some(sel => {
                    if (!sel) return false;
                    if (sel.startsWith('#')) {
                        return h.id && ('#' + h.id) === sel;
                    }
                    if (sel.startsWith('.')) {
                        // Support multi-class headings and case-insensitive compare
                        const cls = sel.substring(1).toLowerCase();
                        return (h.className || '')
                            .toString()
                            .toLowerCase()
                            .split(/\s+/)
                            .includes(cls);
                    }
                    return false;
                });
                if (matchesExcluded) return;

                const base = (h.textContent || '').trim().toLowerCase()
                    .replace(/[^a-z0-9\s-]/g, '')
                    .replace(/\s+/g, '-')
                    .replace(/-+/g, '-');
                if (!base) return;
                let id = base, i = 1;
                while (used.has(id) || document.getElementById(id)) {
                    id = base + '-' + i++;
                }
                h.id = id;
                used.add(id);
            });
        })();

        // Remove TOC entries that target excluded headings (robust client-side guard)
        (function filterTocItemsAgainstExcludedSelectors() {
            const excludedSelectors = (toc.getAttribute('data-exclude-selectors') || '')
                .split(',')
                .map(s => s.trim())
                .filter(Boolean);
            if (excludedSelectors.length === 0) return;

            const list = toc.querySelector('.anchorkit-toc-list');
            if (!list) return;

            const items = Array.from(list.querySelectorAll('.anchorkit-toc-item'));
            let removed = false;

            function elementMatchesExcluded(el) {
                if (!el) return false;
                return excludedSelectors.some(sel => {
                    if (!sel) return false;
                    if (sel.startsWith('#')) {
                        return el.id && ('#' + el.id) === sel;
                    }
                    if (sel.startsWith('.')) {
                        return el.classList && el.classList.contains(sel.substring(1));
                    }
                    return false;
                });
            }

            items.forEach(li => {
                const link = li.querySelector('a.anchorkit-toc-link');
                if (!link || !link.hash) return;
                const target = document.getElementById(link.hash.substring(1));
                if (elementMatchesExcluded(target)) {
                    li.remove();
                    removed = true;
                }
            });

            if (removed) {
                // Recompute height if collapsible and expanded
                scheduleListHeightUpdate(list);
            }
        })();

        // Helper: compute visible list height, respecting mobile constraints
        function computeVisibleListHeight(listElement) {
            if (!listElement) return 0;
            const isMobile = window.innerWidth <= 782;
            let height = listElement.scrollHeight;
            if (isMobile) {
                const maxHeightVh = window.innerWidth <= 480 ? 40 : 50;
                const containerPadding = window.innerWidth <= 480 ? 70 : 80;
                const maxAllowed = (window.innerHeight * maxHeightVh / 100) - containerPadding;
                height = Math.min(height, maxAllowed);
            }
            return height;
        }

        // Helper: schedule a height recompute after DOM updates
        function scheduleListHeightUpdate(listElement) {
            if (!settings.collapsible || !listElement || !toc.classList.contains('anchorkit-toc-expanded')) return;
            // Wait for DOM to apply display changes (double RAF ensures CSS is applied)
            requestAnimationFrame(function () {
                requestAnimationFrame(function () {
                    // Temporarily remove height to let the browser shrink-wrap to content
                    const previousTransition = listElement.style.transition;
                    listElement.style.transition = 'none';
                    listElement.style.height = 'auto';
                    // Force reflow and measure exact content height (only visible items count)
                    const targetHeight = listElement.scrollHeight;
                    // Restore transition and apply precise height
                    listElement.offsetHeight; // reflow
                    listElement.style.transition = previousTransition;
                    listElement.style.height = targetHeight + 'px';
                });
            });
        }

        // Collapsible functionality
        if (settings.collapsible) {
            const toggleBtn = toc.querySelector('.anchorkit-toc-toggle-button');
            const tocTitle = toc.querySelector('.anchorkit-toc-title');
            const list = toc.querySelector('.anchorkit-toc-list');

            if (toggleBtn && list && tocTitle) {
                // Initialize height based on currently visible items (respects View More state)
                if (settings.initial_state === 'collapsed') {
                    list.style.height = '0px';
                    toc.classList.add('anchorkit-toc-collapsed');
                    toc.classList.remove('anchorkit-toc-expanded');
                } else {
                    list.style.height = computeVisibleListHeight(list) + 'px';
                    toc.classList.add('anchorkit-toc-expanded');
                    toc.classList.remove('anchorkit-toc-collapsed');
                }

                // Function to toggle TOC visibility
                const toggleToc = function (e) {
                    // If click is on the toggle button itself, don't trigger a second time
                    if (e && e.target.closest('.anchorkit-toc-toggle-button') && e.currentTarget !== e.target.closest('.anchorkit-toc-toggle-button')) {
                        return;
                    }

                    const collapsed = toc.classList.contains('anchorkit-toc-collapsed');
                    const expanding = collapsed;

                    toggleBtn.setAttribute('aria-expanded', expanding);

                    if (expanding) {
                        // Expanding to the actual visible height
                        list.style.height = computeVisibleListHeight(list) + 'px';
                        toc.classList.remove('anchorkit-toc-collapsed');
                        toc.classList.add('anchorkit-toc-expanded');
                        // ARIA announcements
                        list.removeAttribute('aria-hidden');
                    } else {
                        // Collapsing
                        list.style.height = computeVisibleListHeight(list) + 'px';
                        // Force reflow for transition, then zero height
                        list.offsetHeight; // reflow
                        list.style.height = '0px';
                        toc.classList.add('anchorkit-toc-collapsed');
                        toc.classList.remove('anchorkit-toc-expanded');
                        // ARIA announcements
                        list.setAttribute('aria-hidden', 'true');
                        
                        // Ensure container height is properly updated after transition
                        // This prevents the empty block appearance
                        const handleTransitionEnd = function(e) {
                            if (e.target === list && e.propertyName === 'height') {
                                // Force container to recalculate its height
                                requestAnimationFrame(function() {
                                    // Reset any inline max-height that might be set
                                    if (toc.style.maxHeight) {
                                        toc.style.maxHeight = '';
                                    }
                                    list.removeEventListener('transitionend', handleTransitionEnd);
                                });
                            }
                        };
                        list.addEventListener('transitionend', handleTransitionEnd);
                    }
                    
                    // Safari fix: Force CSS variable recalculation after DOM changes
                    // Safari has a bug where CSS variables aren't inherited properly after reflow
                    if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
                        requestAnimationFrame(function() {
                            var computedBg = window.getComputedStyle(toc).backgroundColor;
                            toc.style.backgroundColor = computedBg;
                            requestAnimationFrame(function() {
                                toc.style.backgroundColor = '';
                            });
                        });
                    }
                };

                // Add click event to both toggle button and title
                toggleBtn.addEventListener('click', toggleToc);
                tocTitle.addEventListener('click', toggleToc);

                // Add keyboard support for the title
                tocTitle.addEventListener('keydown', function (e) {
                    if (e.key === 'Enter' || e.key === ' ') {
                        e.preventDefault();
                        toggleToc(e);
                    }
                });

                // Add Material Design ripple effect
                toggleBtn.addEventListener('mousedown', function (e) {
                    const ripple = document.createElement('span');
                    ripple.classList.add('md-ripple');
                    ripple.style.position = 'absolute';
                    ripple.style.borderRadius = '50%';
                    ripple.style.backgroundColor = 'rgba(0, 0, 0, 0.1)';
                    ripple.style.width = '20px';
                    ripple.style.height = '20px';
                    ripple.style.transform = 'scale(0)';
                    ripple.style.left = (e.offsetX - 10) + 'px';
                    ripple.style.top = (e.offsetY - 10) + 'px';
                    ripple.style.animation = 'md-ripple 0.6s ease-out';

                    // Add ripple animation to document if not exists
                    if (!document.querySelector('#md-ripple-style')) {
                        const style = document.createElement('style');
                        style.id = 'md-ripple-style';
                        style.textContent = '@keyframes md-ripple {' +
                            'to {' +
                            '    transform: scale(4);' +
                            '    opacity: 0;' +
                            '}' +
                            '}';
                        document.head.appendChild(style);
                    }

                    this.appendChild(ripple);

                    setTimeout(function () {
                        ripple.remove();
                    }, 600);
                });

                // Add window resize listener to update height calculation
                window.addEventListener('resize', function () {
                    // Only update if expanded
                    if (!toc.classList.contains('anchorkit-toc-collapsed')) {
                        // Temporarily disable transitions for accurate measurement
                        list.style.transition = 'none';
                        list.style.height = 'auto';

                        // Calculate height considering mobile constraints
                        const isMobile = window.innerWidth <= 782;
                        let newHeight = list.scrollHeight;

                        if (isMobile) {
                            // On mobile, respect the max-height constraint
                            const maxHeightVh = window.innerWidth <= 480 ? 40 : 50;
                            const containerPadding = window.innerWidth <= 480 ? 70 : 80;
                            const maxHeight = (window.innerHeight * maxHeightVh / 100) - containerPadding;
                            newHeight = Math.min(newHeight, maxHeight);
                        }

                        list.style.height = newHeight + 'px';
                        // Force reflow before re-enabling transitions
                        list.offsetHeight;
                        list.style.transition = '';
                    }
                });
            }
        }

        // Function to handle TOC link activation (both click and keyboard)
        function handleTocLinkActivation(link, shouldScroll) {
            const target = document.getElementById(link.hash.substring(1));
            if (!target) return;

            // Update active state in TOC
            const activeLinks = toc.querySelectorAll('.anchorkit-toc-active');
            activeLinks.forEach(el => el.classList.remove('anchorkit-toc-active'));
            link.classList.add('anchorkit-toc-active');

            if (shouldScroll) {
                const offsetTop = target.getBoundingClientRect().top + window.pageYOffset;
                const scroll_offset = settings.scroll_offset || 0;
                const targetY = offsetTop - scroll_offset;

                if (settings.smooth_scroll) {
                    // Use custom scroll animation if pro features are available
                    if (settings.scroll_duration && settings.scroll_easing) {
                        smoothScrollTo(targetY, settings.scroll_duration, settings.scroll_easing);
                    } else {
                        // Native smooth scroll
                        window.scrollTo({
                            top: targetY,
                            behavior: 'smooth'
                        });
                    }
                } else {
                    // Instant scroll (no smooth behavior)
                    window.scrollTo({
                        top: targetY,
                        behavior: 'auto'
                    });
                }

                // Update URL hash without jumping
                history.pushState(null, '', link.hash);

                // Set focus to the heading for a11y (after scroll completes)
                setTimeout(function () {
                    target.setAttribute('tabindex', '-1');
                    target.focus({ preventScroll: true });
                    // Add a highlight effect to the target heading
                    target.classList.add('anchorkit-toc-target-highlight');
                    setTimeout(function () {
                        target.classList.remove('anchorkit-toc-target-highlight');
                    }, 1500);
                }, settings.smooth_scroll ? 500 : 100);
            }
        }

        // TOC link click handlers (always active)
        toc.addEventListener('click', function (e) {
            const link = e.target.closest('a.anchorkit-toc-link');
            if (!link || !link.hash) return;
            e.preventDefault(); // Prevent default anchor jump
            handleTocLinkActivation(link, true);
        });

        // Handle keyboard activation of TOC links
        toc.addEventListener('keydown', function (e) {
            if (e.key === 'Enter') {
                const link = e.target.closest('a.anchorkit-toc-link');
                if (link && link.hash) {
                    e.preventDefault();
                    handleTocLinkActivation(link, true);
                }
            }
        });

        // Back to top link functionality
        const backToTopLink = toc.querySelector('.anchorkit-toc-back-to-top a');
        if (backToTopLink) {
            backToTopLink.addEventListener('click', function (e) {
                e.preventDefault();
                window.scrollTo({ top: 0, behavior: settings.smooth_scroll ? 'smooth' : 'auto' });
                // Set focus to the first heading or element at the top
                setTimeout(function () {
                    const firstHeading = document.querySelector('h1, h2, h3, h4, h5, h6');
                    if (firstHeading) {
                        firstHeading.setAttribute('tabindex', '-1');
                        firstHeading.focus({ preventScroll: true });
                    }
                }, settings.smooth_scroll ? 500 : 100);
            });
        }

        // Enhanced scroll spy to highlight current section (PRO FEATURE)
        // Only works when TOC is sticky (otherwise TOC scrolls out of view)
        if (settings.scroll_spy && settings.sticky && document.querySelectorAll('.anchorkit-toc-link').length > 0) {
            const headingElements = Array.from(document.querySelectorAll('.anchorkit-toc-link'))
                .map(link => {
                    const id = link.getAttribute('href').substring(1);
                    const element = document.getElementById(id);
                    return { link, element };
                })
                .filter(item => item.element !== null);

            // Debounce scroll events with requestAnimationFrame for better performance
            let scrollTimeout;
            window.addEventListener('scroll', function () {
                if (scrollTimeout) {
                    window.cancelAnimationFrame(scrollTimeout);
                }

                scrollTimeout = window.requestAnimationFrame(function () {
                    // Calculate offset based on viewport or custom setting
                    const scrollPos = window.scrollY;
                    const offset = parseInt(settings.scroll_offset || 100);
                    const adjustedScrollPos = scrollPos + offset;

                    // Find the current visible heading (last one that's above the scroll position)
                    let currentHeading = null;
                    for (let i = 0; i < headingElements.length; i++) {
                        const { element } = headingElements[i];
                        if (element.getBoundingClientRect().top + window.pageYOffset <= adjustedScrollPos) {
                            currentHeading = headingElements[i];
                        } else {
                            // Once we find a heading below the scroll position, we can stop searching
                            break;
                        }
                    }

                    // Update active class
                    if (currentHeading) {
                        const activeLinks = toc.querySelectorAll('.anchorkit-toc-active');
                        // Only update if it's a different heading
                        if (!activeLinks.length || activeLinks[0] !== currentHeading.link) {
                            activeLinks.forEach(el => el.classList.remove('anchorkit-toc-active'));
                            currentHeading.link.classList.add('anchorkit-toc-active');

                            // Announce to screen readers which section is currently active
                            const srAnnouncer = document.getElementById('anchorkit-toc-sr-announcer');
                            if (!srAnnouncer) {
                                const announcer = document.createElement('div');
                                announcer.id = 'anchorkit-toc-sr-announcer';
                                announcer.setAttribute('aria-live', 'polite');
                                announcer.setAttribute('class', 'screen-reader-text');
                                document.body.appendChild(announcer);
                            }
                        }
                    }
                });
            });
        }

        // PRO FEATURE: Sticky positioning
        if (settings.sticky) {
            // Skip sticky JS in the Gutenberg editor - it's just a preview
            // The editor DOM structure (iframes, containers) doesn't support sticky positioning
            const isGutenbergEditor = document.body.classList.contains('block-editor-page') ||
                document.querySelector('.editor-styles-wrapper') !== null ||
                toc.closest('.block-editor-block-preview__content') !== null;

            // Skip sticky JS in the Elementor editor - prevents TOC from covering editor controls
            const isElementorEditor = document.body.classList.contains('elementor-editor-active') ||
                document.body.classList.contains('elementor-editor-preview');

            if (!isGutenbergEditor && !isElementorEditor) {
                // Only apply sticky JS on the frontend, not in editor
                const rect = toc.getBoundingClientRect();
                const position = (settings.sticky_position || 'content').toString().trim().toLowerCase();
                const offset = settings.sticky_offset || 20;

                // Add sticky class to TOC
                toc.classList.add('anchorkit-toc-sticky');
                toc.classList.add('anchorkit-toc-sticky-' + position);

                // SPECIAL HANDLING FOR ELEMENTOR:
                // Elementor's nested container structure prevents position: sticky from working on the inner TOC
                // We need to apply sticky to the Elementor WIDGET WRAPPER (.elementor-widget-anchorkit-toc)

                // Check if this TOC is inside an Elementor widget wrapper
                const elementorWidgetWrapper = toc.closest('.elementor-widget-anchorkit-toc');

                if (elementorWidgetWrapper && (position === 'content' || position === 'left' || position === 'right')) {
                    if (position === 'content') {
                        // Apply sticky to the widget wrapper instead of the inner TOC
                        elementorWidgetWrapper.style.position = 'sticky';
                        elementorWidgetWrapper.style.top = offset + 'px';
                        elementorWidgetWrapper.style.zIndex = '99';
                        
                        // Copy width settings from TOC to wrapper so it doesn't shrink
                        // The TOC has inline width styles applied by PHP, we need to copy them to the wrapper
                        const tocInlineWidth = toc.style.width;
                        const tocMaxWidth = toc.style.maxWidth;
                        const tocMinWidth = toc.style.minWidth;
                        
                        if (tocInlineWidth) {
                            elementorWidgetWrapper.style.width = tocInlineWidth;
                        }
                        if (tocMaxWidth) {
                            elementorWidgetWrapper.style.maxWidth = tocMaxWidth;
                        }
                        if (tocMinWidth) {
                            elementorWidgetWrapper.style.minWidth = tocMinWidth;
                        }
                        
                        // Apply alignment from settings instead of hardcoding flex-start
                        const alignment = settings.alignment || 'center';
                        if (alignment === 'left') {
                            elementorWidgetWrapper.style.alignSelf = 'flex-start';
                            elementorWidgetWrapper.style.marginRight = 'auto';
                        } else if (alignment === 'right') {
                            elementorWidgetWrapper.style.alignSelf = 'flex-end';
                            elementorWidgetWrapper.style.marginLeft = 'auto';
                        } else {
                            // Center alignment
                            elementorWidgetWrapper.style.alignSelf = 'center';
                            elementorWidgetWrapper.style.marginLeft = 'auto';
                            elementorWidgetWrapper.style.marginRight = 'auto';
                        }
                    } else if (position === 'left' || position === 'right') {
                        // For sidebar positions, apply fixed positioning to the wrapper
                        elementorWidgetWrapper.style.position = 'fixed';
                        elementorWidgetWrapper.style.top = offset + 'px';
                        elementorWidgetWrapper.style.zIndex = '999';
                        elementorWidgetWrapper.style.maxWidth = '360px';
                        elementorWidgetWrapper.style.maxHeight = '50vh';

                        // Calculate scrollbar width to account for it on right positioning
                        const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;

                        if (position === 'left') {
                            elementorWidgetWrapper.style.left = offset + 'px';
                            elementorWidgetWrapper.style.right = 'auto';
                        } else {
                            // Add scrollbar width to the offset for right positioning
                            const rightOffset = offset + scrollbarWidth;
                            elementorWidgetWrapper.style.right = rightOffset + 'px';
                            elementorWidgetWrapper.style.left = 'auto';
                        }

                        // Ensure TOC container fills the wrapper
                        toc.style.maxHeight = '50vh';
                        toc.style.overflowY = 'auto';
                    }

                    // CRITICAL: Add background to prevent content overlap/see-through
                    // Background color removed per user request to prevent covering content
                    // const tocBg = window.getComputedStyle(toc).backgroundColor;
                    // if (tocBg && tocBg !== 'rgba(0, 0, 0, 0)' && tocBg !== 'transparent') {
                    //     elementorWidgetWrapper.style.background = tocBg;
                    // } else {
                    //     elementorWidgetWrapper.style.background = '#ffffff';
                    // }



                    // Match border radius
                    const tocBorderRadius = window.getComputedStyle(toc).borderRadius;
                    if (tocBorderRadius && tocBorderRadius !== '0px') {
                        elementorWidgetWrapper.style.borderRadius = tocBorderRadius;
                    }

                    // Ensure overflow is visible on parent containers
                    let parent = elementorWidgetWrapper.parentElement;
                    while (parent && parent !== document.body) {
                        const style = window.getComputedStyle(parent);
                        if (style.overflow !== 'visible' && (parent.classList.contains('elementor-widget-container') || parent.classList.contains('e-con-inner'))) {
                            parent.style.overflow = 'visible';
                        }
                        if (parent.classList.contains('elementor-section') || parent.classList.contains('e-con')) {
                            break; // Stop at section/container level
                        }
                        parent = parent.parentElement;
                    }

                    // Don't apply sticky to the TOC itself (it's applied to the Elementor wrapper)
                    // Continue with rest of initialization (View More, entrance animations, etc.)
                }

                // SPECIAL HANDLING FOR GUTENBERG BLOCKS:
                // Similar to Elementor, Gutenberg blocks have a wrapper that needs sticky applied to it
                // instead of the inner TOC element
                const gutenbergBlockWrapper = toc.closest('.anchorkit-gutenberg-block-sticky');

                if (gutenbergBlockWrapper && !elementorWidgetWrapper && (position === 'content' || position === 'left' || position === 'right')) {
                    if (position === 'content') {
                        // Apply sticky to the block wrapper instead of the inner TOC
                        gutenbergBlockWrapper.style.position = 'sticky';
                        gutenbergBlockWrapper.style.top = offset + 'px';
                        gutenbergBlockWrapper.style.zIndex = '99';

                        // Copy width settings from TOC to wrapper so it doesn't shrink
                        const tocInlineWidth = toc.style.width;
                        const tocMaxWidth = toc.style.maxWidth;
                        const tocMinWidth = toc.style.minWidth;

                        if (tocInlineWidth) {
                            gutenbergBlockWrapper.style.width = tocInlineWidth;
                        }
                        if (tocMaxWidth) {
                            gutenbergBlockWrapper.style.maxWidth = tocMaxWidth;
                        }
                        if (tocMinWidth) {
                            gutenbergBlockWrapper.style.minWidth = tocMinWidth;
                        }

                        // Apply alignment from settings
                        const alignment = settings.alignment || 'center';
                        if (alignment === 'left') {
                            gutenbergBlockWrapper.style.alignSelf = 'flex-start';
                            gutenbergBlockWrapper.style.marginRight = 'auto';
                        } else if (alignment === 'right') {
                            gutenbergBlockWrapper.style.alignSelf = 'flex-end';
                            gutenbergBlockWrapper.style.marginLeft = 'auto';
                        } else {
                            // Center alignment
                            gutenbergBlockWrapper.style.alignSelf = 'center';
                            gutenbergBlockWrapper.style.marginLeft = 'auto';
                            gutenbergBlockWrapper.style.marginRight = 'auto';
                        }
                    } else if (position === 'left' || position === 'right') {
                        // For sidebar positions, apply fixed positioning to the wrapper
                        gutenbergBlockWrapper.style.position = 'fixed';
                        gutenbergBlockWrapper.style.top = offset + 'px';
                        gutenbergBlockWrapper.style.zIndex = '999';
                        gutenbergBlockWrapper.style.maxWidth = '360px';
                        gutenbergBlockWrapper.style.maxHeight = '50vh';

                        // Calculate scrollbar width to account for it on right positioning
                        const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;

                        if (position === 'left') {
                            gutenbergBlockWrapper.style.left = offset + 'px';
                            gutenbergBlockWrapper.style.right = 'auto';
                        } else {
                            // Add scrollbar width to the offset for right positioning
                            const rightOffset = offset + scrollbarWidth;
                            gutenbergBlockWrapper.style.right = rightOffset + 'px';
                            gutenbergBlockWrapper.style.left = 'auto';
                        }

                        // Ensure TOC container fills the wrapper
                        toc.style.maxHeight = '50vh';
                        toc.style.overflowY = 'auto';
                    }

                    // Match border radius
                    const tocBorderRadius = window.getComputedStyle(toc).borderRadius;
                    if (tocBorderRadius && tocBorderRadius !== '0px') {
                        gutenbergBlockWrapper.style.borderRadius = tocBorderRadius;
                    }

                    // Ensure overflow is visible on parent containers
                    let parent = gutenbergBlockWrapper.parentElement;
                    while (parent && parent !== document.body) {
                        const style = window.getComputedStyle(parent);
                        if (style.overflow !== 'visible') {
                            parent.style.overflow = 'visible';
                        }
                        if (style.overflowX !== 'visible') {
                            parent.style.overflowX = 'visible';
                        }
                        if (style.overflowY !== 'visible') {
                            parent.style.overflowY = 'visible';
                        }
                        parent = parent.parentElement;
                    }

                    // Don't apply sticky to the TOC itself (it's applied to the Gutenberg wrapper)
                    // Continue with rest of initialization (View More, entrance animations, etc.)
                }

                // Standard Sticky Logic (for non-Elementor and non-Gutenberg TOCs: auto-insert only)
                // Skip if we already handled sticky via Elementor or Gutenberg wrapper
                if (!elementorWidgetWrapper && !gutenbergBlockWrapper) {
                // CRITICAL FIX: Ensure parent containers have overflow: visible
                // position: sticky breaks when any parent has overflow: hidden/auto/scroll
                let stickyParent = toc.parentElement;
                while (stickyParent && stickyParent !== document.body) {
                    const parentStyle = window.getComputedStyle(stickyParent);
                    // Only fix overflow if it's set to something that breaks sticky
                    if (parentStyle.overflow === 'hidden' || parentStyle.overflow === 'auto' || parentStyle.overflow === 'scroll') {
                        stickyParent.style.overflow = 'visible';
                    }
                    if (parentStyle.overflowX === 'hidden' || parentStyle.overflowX === 'auto' || parentStyle.overflowX === 'scroll') {
                        stickyParent.style.overflowX = 'visible';
                    }
                    if (parentStyle.overflowY === 'hidden' || parentStyle.overflowY === 'auto' || parentStyle.overflowY === 'scroll') {
                        stickyParent.style.overflowY = 'visible';
                    }
                    stickyParent = stickyParent.parentElement;
                }

                if (position === 'left' || position === 'right') {
                    // For left/right sidebar positions, use fixed positioning (CSS will handle the rest)
                    toc.style.position = 'fixed';
                    toc.style.top = offset + 'px';
                    toc.style.zIndex = '999';
                    toc.style.maxWidth = '360px';
                    toc.style.maxHeight = '50vh';
                    toc.style.overflowY = 'auto';

                    // Calculate scrollbar width to account for it on right positioning
                    const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;

                    if (position === 'left') {
                        toc.style.left = offset + 'px';
                        toc.style.right = 'auto';
                    } else {
                        // Add scrollbar width to the offset for right positioning
                        const rightOffset = offset + scrollbarWidth;
                        toc.style.right = rightOffset + 'px';
                        toc.style.left = 'auto';
                    }
                } else {
                    // For content position, prefer CSS sticky when available (most modern browsers)
                    var supportsSticky = (window.CSS && CSS.supports && (CSS.supports('position', 'sticky') || CSS.supports('position', '-webkit-sticky')));
                    if (supportsSticky) {
                        toc.style.position = 'sticky';
                        toc.style.top = offset + 'px';
                        toc.style.zIndex = '99';
                        // Don't apply JS fallback, but continue with rest of initialization
                    } else {
                    // JS fallback: simulate sticky relative to the TOC's original position in the document
                    // Only used when native position: sticky is not supported
                    const placeholder = document.createElement('div');
                    placeholder.style.width = toc.offsetWidth + 'px';
                    placeholder.style.height = toc.offsetHeight + 'px';
                    placeholder.style.display = 'none';
                    toc.parentNode.insertBefore(placeholder, toc);

                    // Record original top before any style changes
                    let originalTop = toc.getBoundingClientRect().top + window.pageYOffset;
                    // Track fixed state BEFORE first trigger computation
                    let isFixed = false;

                    function getTriggerY() {
                        // If currently fixed, use placeholder position; else use original document position
                        if (isFixed) {
                            const rect = placeholder.getBoundingClientRect();
                            return rect.top + window.pageYOffset - offset;
                        }
                        return originalTop - offset;
                    }

                    let triggerY = getTriggerY();

                    function stick() {
                        if (isFixed) return;
                        isFixed = true;
                        placeholder.style.display = '';
                        const rect = placeholder.getBoundingClientRect();
                        toc.style.position = 'fixed';
                        toc.style.top = offset + 'px';
                        toc.style.left = rect.left + 'px';
                        toc.style.width = rect.width + 'px';
                        toc.style.zIndex = '999';
                    }

                    function unstick() {
                        if (!isFixed) return;
                        isFixed = false;
                        placeholder.style.display = 'none';
                        toc.style.position = '';
                        toc.style.top = '';
                        toc.style.left = '';
                        toc.style.width = '';
                        toc.style.zIndex = '';
                    }

                    function onScroll() {
                        if (window.pageYOffset >= triggerY) {
                            stick();
                        } else {
                            unstick();
                        }
                    }

                    function onResize() {
                        // Recalculate original top (if not fixed) and trigger; adjust fixed metrics if needed
                        if (!isFixed) {
                            originalTop = toc.getBoundingClientRect().top + window.pageYOffset;
                        }
                        triggerY = getTriggerY();
                        if (isFixed) {
                            const rect = placeholder.getBoundingClientRect();
                            toc.style.left = rect.left + 'px';
                            toc.style.width = rect.width + 'px';
                        }
                    }

                    window.addEventListener('scroll', onScroll);
                    window.addEventListener('resize', onResize);
                    // Initial state
                    onResize();
                    onScroll();
                }
                    }
                }
                } // end of if (!elementorWidgetWrapper && !gutenbergBlockWrapper)
        } else {
            // CLEANUP: Remove sticky positioning when disabled
            // This ensures live preview updates work correctly when toggling sticky off

            // Remove sticky classes
            toc.classList.remove('anchorkit-toc-sticky');
            toc.classList.remove('anchorkit-toc-sticky-content');
            toc.classList.remove('anchorkit-toc-sticky-left');
            toc.classList.remove('anchorkit-toc-sticky-right');

            // Reset TOC element styles (positioning only)
            // Don't clear width, maxWidth, minWidth - they're set by PHP for layout purposes
            toc.style.position = '';
            toc.style.top = '';
            toc.style.left = '';
            toc.style.right = '';
            toc.style.bottom = '';
            toc.style.maxHeight = '';
            toc.style.overflowY = '';
            toc.style.zIndex = '';
            toc.style.display = '';

            // Reset Elementor widget wrapper styles if present
            const elementorWidgetWrapper = toc.closest('.elementor-widget-anchorkit-toc');
            if (elementorWidgetWrapper) {
                elementorWidgetWrapper.style.position = '';
                elementorWidgetWrapper.style.top = '';
                elementorWidgetWrapper.style.zIndex = '';
                elementorWidgetWrapper.style.alignSelf = '';
                elementorWidgetWrapper.style.borderRadius = '';
            }

            // Reset Gutenberg block wrapper styles if present (positioning only)
            // Don't clear width, maxWidth, minWidth - they're set by PHP for layout purposes
            const gutenbergBlockWrapper = toc.closest('.anchorkit-gutenberg-block-sticky');
            if (gutenbergBlockWrapper) {
                gutenbergBlockWrapper.style.position = '';
                gutenbergBlockWrapper.style.top = '';
                gutenbergBlockWrapper.style.left = '';
                gutenbergBlockWrapper.style.right = '';
                gutenbergBlockWrapper.style.zIndex = '';
                gutenbergBlockWrapper.style.maxHeight = '';
                gutenbergBlockWrapper.style.alignSelf = '';
                gutenbergBlockWrapper.style.marginLeft = '';
                gutenbergBlockWrapper.style.marginRight = '';
                gutenbergBlockWrapper.style.borderRadius = '';
            }
        }

        // Add target highlight styles if not already present
        if (!document.getElementById('anchorkit-toc-target-styles')) {
            const style = document.createElement('style');
            style.id = 'anchorkit-toc-target-styles';
            let cssText = '@keyframes anchorkit-toc-target-fade {';
            cssText += '0%, 20% { background-color: rgba(0, 160, 210, 0.1); }';
            cssText += '100% { background-color: transparent; }';
            cssText += '}';
            cssText += '.anchorkit-toc-target-highlight {';
            cssText += 'animation: anchorkit-toc-target-fade 1.5s ease-out;';
            cssText += 'border-radius: 3px;';
            cssText += 'outline: none;';
            cssText += '}';
            style.textContent = cssText;
            document.head.appendChild(style);
        }

        // PRO FEATURE: Entrance Animation
        // Check for boolean true or string "1" (WordPress checkbox values)
        if ((settings.entrance_animation === true || settings.entrance_animation === 1 || settings.entrance_animation === "1") && settings.animation_type) {
            // Add entrance animation classes
            toc.classList.add('anchorkit-toc-entrance-animation');

            // Function to trigger animation
            const triggerAnimation = function () {
                toc.classList.add('anchorkit-toc-animate-' + settings.animation_type);
            };

            // Use Intersection Observer to trigger animation when TOC is in view
            if ('IntersectionObserver' in window) {
                const observer = new IntersectionObserver(function (entries) {
                    entries.forEach(function (entry) {
                        if (entry.isIntersecting) {
                            // Trigger animation
                            triggerAnimation();
                            // Disconnect after first trigger
                            observer.disconnect();
                        }
                    });
                }, {
                    threshold: 0.01, // Trigger when even 1% of TOC is visible
                    rootMargin: '100px 0px 100px 0px' // More generous margins
                });

                observer.observe(toc);

                // Check if TOC is already in view (for TOCs at top of page)
                const rect = toc.getBoundingClientRect();
                const inView = (
                    rect.top >= -100 &&
                    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + 100
                );

                if (inView) {
                    // If already in view, trigger immediately with a small delay
                    setTimeout(triggerAnimation, 100);
                    observer.disconnect();
                }
            } else {
                // Fallback for browsers without IntersectionObserver
                // Just add the animation class immediately
                setTimeout(triggerAnimation, 100);
            }
        } else {
            // Ensure animation classes are removed if setting is disabled
            toc.classList.remove('anchorkit-toc-entrance-animation');
            toc.classList.remove('anchorkit-toc-animate-fade');
            toc.classList.remove('anchorkit-toc-animate-slide');
            toc.classList.remove('anchorkit-toc-animate-zoom');
        }

        // PRO FEATURE: View More functionality
        const viewMoreBtn = toc.querySelector('.anchorkit-toc-view-more-btn');
        if (viewMoreBtn) {
            const initialCount = parseInt(viewMoreBtn.getAttribute('data-initial-count')) || 5;
            const viewMoreText = viewMoreBtn.getAttribute('data-view-more-text') || 'View More';
            const viewLessText = viewMoreBtn.getAttribute('data-view-less-text') || 'View Less';
            const tocItems = Array.from(toc.querySelectorAll('.anchorkit-toc-item:not(.anchorkit-toc-view-more-item)'));
            const textSpan = viewMoreBtn.querySelector('.anchorkit-toc-view-more-text');
            const list = toc.querySelector('.anchorkit-toc-list');
            // Keep view more button inside the list so it collapses with the TOC
            // The CSS will handle making it sticky and preventing overlap

            // Initially hide items beyond the initial count
            tocItems.forEach(function (item, index) {
                if (index >= initialCount) {
                    item.classList.add('anchorkit-toc-hidden-item');
                }
            });

            // If all items would be visible, remove the button
            if (tocItems.length <= initialCount) {
                const wrapper = viewMoreBtn.closest('.anchorkit-toc-view-more-item');
                if (wrapper) wrapper.remove();
            } else {
                // Adjust TOC list height to visible items if TOC is expanded
                if (settings.collapsible && list && toc.classList.contains('anchorkit-toc-expanded')) {
                    scheduleListHeightUpdate(list);
                }

                // Toggle function
                viewMoreBtn.addEventListener('click', function (e) {
                    e.preventDefault();
                    const isExpanded = this.getAttribute('aria-expanded') === 'true';

                    if (isExpanded) {
                        // Collapse - hide items beyond initial count
                        tocItems.forEach(function (item, index) {
                            if (index >= initialCount) {
                                item.classList.add('anchorkit-toc-hidden-item');
                            }
                        });
                        this.setAttribute('aria-expanded', 'false');
                        toc.classList.remove('anchorkit-toc-view-more-expanded');
                        if (textSpan) textSpan.textContent = viewMoreText;
                    } else {
                        // Expand - show all items
                        tocItems.forEach(function (item) {
                            item.classList.remove('anchorkit-toc-hidden-item');
                        });
                        this.setAttribute('aria-expanded', 'true');
                        toc.classList.add('anchorkit-toc-view-more-expanded');
                        if (textSpan) textSpan.textContent = viewLessText;
                    }

                    // Update TOC height if collapsible and currently expanded
                    if (settings.collapsible && list && toc.classList.contains('anchorkit-toc-expanded')) {
                        scheduleListHeightUpdate(list);
                    }
                    
                    // Safari fix: Force CSS variable recalculation after DOM changes
                    // Safari has a bug where CSS variables aren't inherited properly after reflow
                    if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
                        requestAnimationFrame(function() {
                            var computedBg = window.getComputedStyle(toc).backgroundColor;
                            toc.style.backgroundColor = computedBg;
                            requestAnimationFrame(function() {
                                toc.style.backgroundColor = '';
                            });
                        });
                    }
                });

                // Keyboard accessibility
                viewMoreBtn.addEventListener('keydown', function (e) {
                    if (e.key === 'Enter' || e.key === ' ') {
                        e.preventDefault();
                        this.click();
                    }
                });
            }
        }

    }

    /**
     * Initialize all TOC containers on the page
     */
    function initializeAllTocContainers() {
        const containers = document.querySelectorAll('.anchorkit-toc-container');
        if (!containers.length) return;

        containers.forEach(function (toc) {
            initializeTocContainer(toc);
        });
    }

    // Setup MutationObserver for dynamically added TOC containers (e.g., Gutenberg ServerSideRender updates)
    function setupMutationObserver() {
        if (typeof MutationObserver !== 'undefined' && document.body) {
            const observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    mutation.addedNodes.forEach(function (node) {
                        if (node.nodeType === 1) { // Element node
                            // Check if the added node is a TOC container
                            if (node.classList && node.classList.contains('anchorkit-toc-container')) {
                                initializeTocContainer(node);
                            }
                            // Check if the added node contains TOC containers
                            const tocContainers = node.querySelectorAll && node.querySelectorAll('.anchorkit-toc-container');
                            if (tocContainers) {
                                tocContainers.forEach(function (toc) {
                                    initializeTocContainer(toc);
                                });
                            }
                        }
                    });
                });
            });

            // Start observing the document body for added nodes
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }
    }

    // Initialize ASAP: if DOM already ready, run now; otherwise wait for DOMContentLoaded
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            initializeAllTocContainers();
            setupMutationObserver();
        });
    } else {
        initializeAllTocContainers();
        setupMutationObserver();
    }

    // Expose initialization functions globally for Elementor and Gutenberg
    window.initializeAnchorKitToc = initializeAllTocContainers;
    window.initializeTocContainer = initializeTocContainer;

    // Elementor frontend editor support
    if (typeof jQuery !== 'undefined') {
        jQuery(window).on('elementor/frontend/init', function () {
            // Reinitialize when Elementor editor updates widgets
            elementorFrontend.hooks.addAction('frontend/element_ready/anchorkit-toc.default', function ($scope) {
                const toc = $scope.find('.anchorkit-toc-container')[0];
                if (toc) {
                    initializeTocContainer(toc);
                }
            });
        });
    }
})(); 
