import constellationGeoJsonEmbedded from './constellations-data.js';
import constellationLineOverrides from './constellation-line-overrides.js';

// Helper function to get Aladin viewer rectangle
window.getAladinViewerRect = function(containerId) {
    const container = document.getElementById(containerId);
    if (container) {
        const rect = container.getBoundingClientRect();
        return {
            width: rect.width,
            height: rect.height,
            left: rect.left,
            top: rect.top
        };
    }
    return null;
};

window.positionSkyAtlasPopupInViewport = function(popupId, anchorX, anchorY, options) {
    const scopeRootId = options?.scopeRootId;
    const popup = scopeRootId
        ? document.querySelector(`#${scopeRootId} #${popupId}`)
        : document.getElementById(popupId);
    if (!popup) {
        return;
    }

    const gap = Number.isFinite(options?.gap) ? options.gap : 10;
    const padding = Number.isFinite(options?.padding) ? options.padding : 12;
    const preferredPlacement = options?.preferredPlacement === 'bottom' ? 'bottom' : 'top';
    const container = options?.containerId
        ? (window.aladinFullscreenPopups?.resolveElement?.(options.containerId, scopeRootId)
            || document.getElementById(options.containerId))
        : null;
    const containerRect = container ? container.getBoundingClientRect() : null;
    const boundsWidth = containerRect?.width || window.innerWidth || document.documentElement?.clientWidth || 0;
    const boundsHeight = containerRect?.height || window.innerHeight || document.documentElement?.clientHeight || 0;
    const relativeAnchorX = containerRect ? anchorX - containerRect.left : anchorX;
    const relativeAnchorY = containerRect ? anchorY - containerRect.top : anchorY;

    popup.style.transform = 'none';
    popup.style.marginTop = '0';
    popup.style.right = 'auto';
    popup.style.bottom = 'auto';

    const rect = popup.getBoundingClientRect();

    const maxLeft = Math.max(padding, boundsWidth - rect.width - padding);
    let left = relativeAnchorX - (rect.width / 2);
    left = Math.max(padding, Math.min(left, maxLeft));

    let top;
    if (preferredPlacement === 'top') {
        top = relativeAnchorY - rect.height - gap;
        if (top < padding) {
            top = Math.min(boundsHeight - rect.height - padding, relativeAnchorY + gap);
        }
    } else {
        top = relativeAnchorY + gap;
        if (top + rect.height > boundsHeight - padding) {
            top = Math.max(padding, relativeAnchorY - rect.height - gap);
        }
    }

    const maxTop = Math.max(padding, boundsHeight - rect.height - padding);
    top = Math.max(padding, Math.min(top, maxTop));

    popup.style.left = `${left}px`;
    popup.style.top = `${top}px`;
};

// Fullscreen functionality
window.enterFullscreen = async function(containerId) {
    const container = document.getElementById(containerId);
    if (!container) {
        return;
    }

    const host = container.closest('.sky-atlas-aladin-host') || container;
    const preferPseudoFullscreen = (() => {
        try {
            return window.matchMedia('(max-width: 768px)').matches
                || window.matchMedia('(pointer: coarse)').matches
                || (navigator.maxTouchPoints || 0) > 0;
        } catch {
            return false;
        }
    })();
    const applyPseudoFullscreen = () => {
        window.__amPseudoFullscreenHost = host;
        host.classList.add('am-aladin-pseudo-fullscreen');
        document.documentElement.classList.add('am-skyatlas-fullscreen-active');
        document.body.classList.add('am-skyatlas-fullscreen-active');
        window.dispatchEvent(new Event('resize'));
    };

    if (preferPseudoFullscreen) {
        applyPseudoFullscreen();
        return;
    }

    try {
        if (host.requestFullscreen) {
            await host.requestFullscreen();
            return;
        }
        if (host.webkitRequestFullscreen) {
            await host.webkitRequestFullscreen();
            return;
        }
        if (host.msRequestFullscreen) {
            await host.msRequestFullscreen();
            return;
        }
    } catch {
        applyPseudoFullscreen();
        return;
    }

    applyPseudoFullscreen();
};

window.exitFullscreen = async function() {
    try {
        if (document.exitFullscreen && document.fullscreenElement) {
            await document.exitFullscreen();
        } else if (document.webkitExitFullscreen && document.webkitFullscreenElement) {
            await document.webkitExitFullscreen();
        } else if (document.msExitFullscreen && document.msFullscreenElement) {
            await document.msExitFullscreen();
        }
    } catch {
        // fall through to pseudo fullscreen cleanup
    }

    const pseudoHost = window.__amPseudoFullscreenHost || document.querySelector('.am-aladin-pseudo-fullscreen');
    if (pseudoHost) {
        pseudoHost.classList.remove('am-aladin-pseudo-fullscreen');
        window.__amPseudoFullscreenHost = null;
    }

    document.documentElement.classList.remove('am-skyatlas-fullscreen-active');
    document.body.classList.remove('am-skyatlas-fullscreen-active');
    window.dispatchEvent(new Event('resize'));
};

// Aladin Lite viewer initialization and management
export async function initializeAladin(pContainerId, dotNetHelper, options = {}) {
    // Default options
    let defaults = {
        target: "10.6847 41.2692", // Default to M31
        fov: 2.0, // 1 degree FOV
        survey: "https://alasky.cds.unistra.fr/DSS/DSSColor",
        showFov: false,
        fovWidth: 0,
        fovHeight: 0,
        fovRotation: 0,
        deferDotNetViewportSync: false,
        viewportSyncDebounceMs: 450,
        suspendMarkerLabelsDuringInteraction: false
    };


    let settings = { ...defaults, ...options };
    const isMobileLayout = options && options.isMobileLayout === true;

    const debugEnabled = (() => {
        try {
            return window.__amSkyAtlasDebug === true || window.localStorage?.getItem('amSkyAtlasDebug') === '1';
        } catch {
            return window.__amSkyAtlasDebug === true;
        }
    })();

    const debugLog = (...args) => {
        if (debugEnabled) {
            console.debug('[SkyAtlasDebug]', ...args);
        }
    };

    const pushTrace = (eventName, payload = {}) => {
        try {
            if (!window.__amSkyAtlasTrace) {
                window.__amSkyAtlasTrace = [];
            }

            window.__amSkyAtlasTrace.push({
                at: new Date().toISOString(),
                event: eventName,
                payload
            });

            if (window.__amSkyAtlasTrace.length > 200) {
                window.__amSkyAtlasTrace.splice(0, window.__amSkyAtlasTrace.length - 200);
            }
        } catch {
            // Keep diagnostics side-effect free
        }
    };

    const setDiagBadge = (message, isError = false) => {
        try {
            if (!debugEnabled) {
                return;
            }

            if ((window.innerWidth || document.documentElement?.clientWidth || 0) >= 1024) {
                return;
            }

            let badge = document.getElementById('skyatlas-mobile-diag-badge');
            if (!badge) {
                badge = document.createElement('div');
                badge.id = 'skyatlas-mobile-diag-badge';
                badge.style.position = 'fixed';
                badge.style.right = '10px';
                badge.style.bottom = '10px';
                badge.style.zIndex = '2147483647';
                badge.style.padding = '6px 8px';
                badge.style.borderRadius = '6px';
                badge.style.fontSize = '11px';
                badge.style.fontFamily = 'monospace';
                badge.style.maxWidth = '85vw';
                badge.style.wordBreak = 'break-word';
                document.body.appendChild(badge);
            }

            badge.style.background = isError ? 'rgba(153, 27, 27, 0.92)' : 'rgba(17, 24, 39, 0.9)';
            badge.style.color = '#fff';
            badge.style.border = isError ? '1px solid rgba(252, 165, 165, 0.9)' : '1px solid rgba(156, 163, 175, 0.7)';
            badge.textContent = message;
        } catch {
            // No-op
        }
    };

    let containerId = pContainerId;

    const normalizeInitialFov = (value) => {
        if (Array.isArray(value)) {
            const first = Number(value[0]);
            return Number.isFinite(first) && first > 0 ? first : defaults.fov;
        }

        const numeric = Number(value);
        return Number.isFinite(numeric) && numeric > 0 ? numeric : defaults.fov;
    };

    const formatInvariantNumber = (value) => {
        const numeric = Number(value);
        return Number.isFinite(numeric) ? String(numeric) : null;
    };

    const normalizeTargetForAladin = (value) => {
        if (Array.isArray(value) && value.length >= 2) {
            const ra = formatInvariantNumber(value[0]);
            const dec = formatInvariantNumber(value[1]);
            return ra && dec ? `${ra} ${dec}` : value;
        }

        if (value && typeof value === 'object') {
            const ra = formatInvariantNumber(value.ra ?? value.RA ?? value.lon ?? value.x);
            const dec = formatInvariantNumber(value.dec ?? value.DEC ?? value.lat ?? value.y);
            return ra && dec ? `${ra} ${dec}` : value;
        }

        if (typeof value !== 'string') {
            return value;
        }

        const trimmed = value.trim();
        if (!trimmed) {
            return value;
        }

        const parts = trimmed.split(/\s+/);
        if (parts.length >= 2) {
            const ra = Number(parts[0].replace(',', '.'));
            const dec = Number(parts[1].replace(',', '.'));
            if (Number.isFinite(ra) && Number.isFinite(dec)) {
                return `${ra} ${dec}`;
            }
        }

        return trimmed.replace(/(\d),(\d)/g, '$1.$2');
    };

    const normalizeSurveyForBuild = (survey, build) => {
        if (!build?.legacy || typeof survey !== 'string') {
            return survey;
        }

        const trimmed = survey.trim();
        if (!trimmed) {
            return 'P/DSS2/color';
        }

        if (trimmed === 'CDS/P/DSS2/color') {
            return 'P/DSS2/color';
        }

        if (/DSS\/DSSColor/i.test(trimmed) || /DSS2\/color/i.test(trimmed)) {
            return 'P/DSS2/color';
        }

        return trimmed;
    };

    const isApplePlatform = () => {
        const explicitPlatform = settings.platform || options?.platform;
        if (typeof explicitPlatform === 'string') {
            return explicitPlatform === 'iOS' || explicitPlatform === 'macOS';
        }

        try {
            const userAgent = navigator.userAgent || '';
            const platform = navigator.platform || '';
            const touchPoints = navigator.maxTouchPoints || 0;

            return /iPhone|iPad|iPod/i.test(userAgent)
                || /Mac/i.test(platform)
                || (/Macintosh/i.test(userAgent) && touchPoints > 1);
        } catch {
            return false;
        }
    };

    const getLegacyCanvasFallbackBuild = () => ({
        id: '2.x-canvas',
        label: '2.x-canvas',
        url: 'https://aladin.u-strasbg.fr/AladinLite/api/v2/latest/aladin.min.js',
        cssUrl: 'https://aladin.u-strasbg.fr/AladinLite/api/v2/latest/aladin.min.css',
        jqueryUrl: 'https://code.jquery.com/jquery-1.9.1.min.js',
        legacy: true
    });

    const getPreferredAladinBuilds = () => [
        {
            id: '3.6.5',
            label: '3.6.5',
            url: 'https://aladin.cds.unistra.fr/AladinLite/api/v3/3.6.5/aladin.js',
            legacy: false
        },
        getLegacyCanvasFallbackBuild()
    ];

    const getAppleFramebufferFallbackBuild = () => getLegacyCanvasFallbackBuild();

    const looksLikeAnyAladinApi = (value) => !!value
        && typeof value.aladin === 'function';

    const looksLikeBootstrappedAladinApi = (value) => looksLikeAnyAladinApi(value)
        && typeof value.init?.then === 'function';

    const looksLikeAladinApiForBuild = (value, build) => build?.legacy === true
        ? looksLikeAnyAladinApi(value)
        : looksLikeBootstrappedAladinApi(value);

    const isReadyAladinInstanceForBuild = (instance, build) => {
        if (!instance) {
            return false;
        }

        if (build?.legacy) {
            return typeof instance.getRaDec === 'function'
                && typeof instance.getFov === 'function'
                && typeof instance.gotoRaDec === 'function';
        }

        return !!instance.wasm && !!instance.view?.wasm;
    };

    const applyLegacyCatalogCompatibility = (catalog) => {
        if (!catalog || catalog.__amLegacyCompatibilityApplied === true) {
            return catalog;
        }

        catalog.__amLegacyCompatibilityApplied = true;
        catalog.__amLegacyBuild = true;

        if (typeof catalog.setSelectionColor !== 'function') {
            catalog.setSelectionColor = (color) => {
                catalog.selectionColor = color;
                return null;
            };
        }

        if (typeof catalog.setHoverColor !== 'function') {
            catalog.setHoverColor = (color) => {
                catalog.hoverColor = color;
                return null;
            };
        }

        if (typeof catalog.setColor !== 'function') {
            catalog.setColor = (color) => {
                catalog.color = color;
                return null;
            };
        }

        if (typeof catalog.show !== 'function') {
            catalog.show = () => {
                catalog.isShowing = true;
                return null;
            };
        }

        if (typeof catalog.hide !== 'function') {
            catalog.hide = () => {
                catalog.isShowing = false;
                return null;
            };
        }

        return catalog;
    };

    const applyLegacyAladinCompatibility = (instance) => {
        if (!instance || instance.__amLegacyCompatibilityApplied === true) {
            return instance;
        }

        instance.__amLegacyCompatibilityApplied = true;
        instance.__amLegacyBuild = true;

        if (typeof instance.setRotation !== 'function') {
            instance.setRotation = () => null;
        }

        if (typeof instance.setFrame !== 'function') {
            instance.setFrame = () => null;
        }

        if (typeof instance.setCooGrid !== 'function') {
            instance.setCooGrid = () => null;
        }

        if (typeof instance.showReticle !== 'function') {
            instance.showReticle = () => null;
        }

        if (typeof instance.showConstellations !== 'function') {
            instance.showConstellations = () => null;
        }

        if (typeof instance.getBaseImageLayer === 'function') {
            const originalGetBaseImageLayer = instance.getBaseImageLayer.bind(instance);
            instance.getBaseImageLayer = () => {
                const layer = originalGetBaseImageLayer();
                if (!layer || typeof layer.setColormap === 'function') {
                    return layer;
                }

                if (typeof layer.getColorMap === 'function') {
                    layer.setColormap = (colormap) => {
                        const colorMap = layer.getColorMap?.();
                        colorMap?.update?.(colormap);
                    };
                } else {
                    layer.setColormap = () => null;
                }

                return layer;
            };
        }

        if (typeof instance.on === 'function') {
            const originalOn = instance.on.bind(instance);
            instance.on = (eventName, handler) => {
                if (eventName === 'fovChanged') {
                    return originalOn('zoomChanged', handler);
                }

                if (eventName === 'layerChanged') {
                    return null;
                }

                return originalOn(eventName, handler);
            };
        }

        const legacyApi = getTrackedAladinApi();
        if (legacyApi && typeof legacyApi.catalog === 'function' && legacyApi.catalog.__amLegacyCompatibilityWrapped !== true) {
            const originalCatalogFactory = legacyApi.catalog.bind(legacyApi);
            const wrappedCatalogFactory = (...args) => applyLegacyCatalogCompatibility(originalCatalogFactory(...args));
            wrappedCatalogFactory.__amLegacyCompatibilityWrapped = true;
            legacyApi.catalog = wrappedCatalogFactory;

            if (window.A === legacyApi) {
                window.A.catalog = wrappedCatalogFactory;
            }

            if (globalThis.A === legacyApi) {
                globalThis.A.catalog = wrappedCatalogFactory;
            }
        }

        return instance;
    };

    const createDomBackedAladinButton = (hostElement, config) => {
        const btnElement = document.createElement('button');
        btnElement.type = 'button';
        btnElement.className = 'x-aladin-btn';
        btnElement.innerHTML = config.content || 'Button';
        if (Array.isArray(config.classList) && config.classList.length > 0) {
            btnElement.classList.add(...config.classList);
        }

        if (config.ariaLabel || config.tooltip) {
            btnElement.setAttribute('aria-label', config.ariaLabel || config.tooltip);
        }

        if (config.disabled) {
            btnElement.disabled = true;
        }

        btnElement.addEventListener('click', (event) => {
            event.preventDefault();
            event.stopPropagation();

            if (config.disabled) {
                return;
            }

            config.action?.();
        });

        hostElement?.appendChild?.(btnElement);
        return {
            el: btnElement
        };
    };

    const getSkyAtlasButtonTooltipState = () => {
        if (!window.__amSkyAtlasButtonTooltipState) {
            window.__amSkyAtlasButtonTooltipState = {
                element: null,
                titleElement: null,
                linkElement: null,
                activeAnchor: null,
                showTimer: null,
                hideTimer: null
            };
        }

        return window.__amSkyAtlasButtonTooltipState;
    };

    const clearSkyAtlasButtonTooltipShowTimer = () => {
        const state = getSkyAtlasButtonTooltipState();
        if (state.showTimer) {
            clearTimeout(state.showTimer);
            state.showTimer = null;
        }
    };

    const clearSkyAtlasButtonTooltipHideTimer = () => {
        const state = getSkyAtlasButtonTooltipState();
        if (state.hideTimer) {
            clearTimeout(state.hideTimer);
            state.hideTimer = null;
        }
    };

    const isSkyAtlasTooltipOrAnchorElement = (element) => {
        if (!element) {
            return false;
        }

        const state = getSkyAtlasButtonTooltipState();
        return element === state.element
            || state.element?.contains?.(element)
            || element === state.activeAnchor
            || state.activeAnchor?.contains?.(element);
    };

    const ensureSkyAtlasButtonTooltip = () => {
        const state = getSkyAtlasButtonTooltipState();
        if (state.element && document.body.contains(state.element)) {
            return state;
        }

        const tooltip = document.createElement('div');
        tooltip.id = 'am-skyatlas-button-tooltip';
        tooltip.style.position = 'fixed';
        tooltip.style.zIndex = '2147483646';
        tooltip.style.maxWidth = '280px';
        tooltip.style.padding = '10px 12px';
        tooltip.style.borderRadius = '10px';
        tooltip.style.border = '1px solid rgba(125, 211, 252, 0.28)';
        tooltip.style.background = 'linear-gradient(180deg, rgba(15, 23, 42, 0.97), rgba(2, 6, 23, 0.96))';
        tooltip.style.color = '#f8fafc';
        tooltip.style.boxShadow = '0 18px 40px rgba(0, 0, 0, 0.45)';
        tooltip.style.backdropFilter = 'blur(8px)';
        tooltip.style.webkitBackdropFilter = 'blur(8px)';
        tooltip.style.pointerEvents = 'auto';
        tooltip.style.opacity = '0';
        tooltip.style.transform = 'translateY(4px)';
        tooltip.style.transition = 'opacity 120ms ease, transform 120ms ease';
        tooltip.style.display = 'none';
        tooltip.style.fontFamily = 'system-ui, sans-serif';

        const title = document.createElement('div');
        title.style.fontSize = '12px';
        title.style.lineHeight = '1.45';
        title.style.fontWeight = '600';
        tooltip.appendChild(title);

        const link = document.createElement('a');
        link.textContent = 'Open documentation';
        link.target = '_blank';
        link.rel = 'noopener noreferrer';
        link.style.display = 'none';
        link.style.marginTop = '8px';
        link.style.fontSize = '11px';
        link.style.fontWeight = '600';
        link.style.color = '#7dd3fc';
        link.style.textDecoration = 'none';
        tooltip.appendChild(link);

        tooltip.addEventListener('mouseenter', () => {
            clearSkyAtlasButtonTooltipHideTimer();
        });

        tooltip.addEventListener('mouseleave', (event) => {
            if (isSkyAtlasTooltipOrAnchorElement(event?.relatedTarget)) {
                clearSkyAtlasButtonTooltipHideTimer();
                return;
            }

            const stateRef = getSkyAtlasButtonTooltipState();
            stateRef.hideTimer = setTimeout(() => {
                tooltip.style.opacity = '0';
                tooltip.style.transform = 'translateY(4px)';
                tooltip.style.display = 'none';
                stateRef.activeAnchor = null;
                stateRef.hideTimer = null;
            }, 260);
        });

        tooltip.addEventListener('focusin', () => {
            clearSkyAtlasButtonTooltipHideTimer();
        });

        tooltip.addEventListener('focusout', (event) => {
            if (isSkyAtlasTooltipOrAnchorElement(event?.relatedTarget)) {
                clearSkyAtlasButtonTooltipHideTimer();
                return;
            }

            scheduleSkyAtlasButtonTooltipHide();
        });

        document.body.appendChild(tooltip);

        state.element = tooltip;
        state.titleElement = title;
        state.linkElement = link;
        return state;
    };

    const hideSkyAtlasButtonTooltip = () => {
        const state = getSkyAtlasButtonTooltipState();
        if (!state.element) {
            return;
        }

        clearSkyAtlasButtonTooltipShowTimer();
        clearSkyAtlasButtonTooltipHideTimer();
        state.element.style.opacity = '0';
        state.element.style.transform = 'translateY(4px)';
        state.element.style.display = 'none';
        state.activeAnchor = null;
    };

    const positionSkyAtlasButtonTooltip = (tooltip, anchorElement) => {
        if (!tooltip || !anchorElement) {
            return;
        }

        const rect = anchorElement.getBoundingClientRect();
        const padding = 12;
        const gap = 10;

        tooltip.style.left = '0';
        tooltip.style.top = '0';
        tooltip.style.display = 'block';

        const tooltipRect = tooltip.getBoundingClientRect();
        const viewportWidth = window.innerWidth || document.documentElement?.clientWidth || 0;
        const viewportHeight = window.innerHeight || document.documentElement?.clientHeight || 0;

        let left = rect.left + (rect.width / 2) - (tooltipRect.width / 2);
        left = Math.max(padding, Math.min(left, viewportWidth - tooltipRect.width - padding));

        let top = rect.bottom + gap;
        if (top + tooltipRect.height > viewportHeight - padding) {
            top = rect.top - tooltipRect.height - gap;
        }

        top = Math.max(padding, Math.min(top, viewportHeight - tooltipRect.height - padding));

        tooltip.style.left = `${left}px`;
        tooltip.style.top = `${top}px`;
    };

    const showSkyAtlasButtonTooltip = (anchorElement, config) => {
        if (!anchorElement || !config?.tooltip) {
            hideSkyAtlasButtonTooltip();
            return;
        }

        const state = ensureSkyAtlasButtonTooltip();
        clearSkyAtlasButtonTooltipShowTimer();
        clearSkyAtlasButtonTooltipHideTimer();

        state.activeAnchor = anchorElement;
        state.titleElement.textContent = config.tooltip;

        if (config.helpUrl) {
            state.linkElement.href = config.helpUrl;
            state.linkElement.style.display = 'inline-flex';
        } else {
            state.linkElement.removeAttribute('href');
            state.linkElement.style.display = 'none';
        }

        positionSkyAtlasButtonTooltip(state.element, anchorElement);
        state.element.style.display = 'block';

        requestAnimationFrame(() => {
            state.element.style.opacity = '1';
            state.element.style.transform = 'translateY(0)';
        });
    };

    const scheduleSkyAtlasButtonTooltipShow = (anchorElement, config, delayMs) => {
        if (!anchorElement || !config?.tooltip) {
            return;
        }

        const state = getSkyAtlasButtonTooltipState();
        clearSkyAtlasButtonTooltipShowTimer();
        clearSkyAtlasButtonTooltipHideTimer();
        state.activeAnchor = anchorElement;
        state.showTimer = setTimeout(() => {
            state.showTimer = null;
            showSkyAtlasButtonTooltip(anchorElement, config);
        }, Math.max(0, Number.isFinite(delayMs) ? delayMs : 0));
    };

    const scheduleSkyAtlasButtonTooltipHide = (event = null) => {
        if (isSkyAtlasTooltipOrAnchorElement(event?.relatedTarget)) {
            clearSkyAtlasButtonTooltipShowTimer();
            clearSkyAtlasButtonTooltipHideTimer();
            return;
        }

        const state = getSkyAtlasButtonTooltipState();
        clearSkyAtlasButtonTooltipShowTimer();
        clearSkyAtlasButtonTooltipHideTimer();
        state.hideTimer = setTimeout(() => {
            hideSkyAtlasButtonTooltip();
        }, 260);
    };

    const attachSkyAtlasButtonTooltip = (buttonElement, config) => {
        if (!buttonElement || buttonElement.__amSkyAtlasTooltipAttached === true) {
            return;
        }

        buttonElement.__amSkyAtlasTooltipAttached = true;

        const ariaLabel = config?.ariaLabel || config?.tooltip;

        if (ariaLabel) {
            buttonElement.setAttribute('aria-label', ariaLabel);
        }

        if (config?.showTooltip === false || !config?.tooltip) {
            return;
        }

        buttonElement.removeAttribute('title');
        buttonElement.dataset.skyatlasTooltip = config.tooltip;

        buttonElement.addEventListener('mouseenter', () => {
            scheduleSkyAtlasButtonTooltipShow(buttonElement, config, 1200);
        });
        buttonElement.addEventListener('focus', () => {
            scheduleSkyAtlasButtonTooltipShow(buttonElement, config, 400);
        });
        buttonElement.addEventListener('mouseleave', (event) => {
            scheduleSkyAtlasButtonTooltipHide(event);
        });
        buttonElement.addEventListener('blur', (event) => {
            scheduleSkyAtlasButtonTooltipHide(event);
        });
        buttonElement.addEventListener('click', hideSkyAtlasButtonTooltip);
    };

    const CONSTELLATION_LINES_DATA_URL = new URL('../data/constellations.lines.min.geojson', import.meta.url).toString();
    const CONSTELLATION_LINES_FALLBACK_URLS = (() => {
        const urls = new Set([CONSTELLATION_LINES_DATA_URL]);
        const origin = window.location?.origin || '';
        const pathname = window.location?.pathname || '/';
        const basePath = pathname.endsWith('/')
            ? pathname
            : `${pathname.substring(0, pathname.lastIndexOf('/') + 1)}`;

        if (origin) {
            urls.add(`${origin}/_content/Blazor.UI.RCL/data/constellations.lines.min.geojson`);
            urls.add(`${origin}${basePath}_content/Blazor.UI.RCL/data/constellations.lines.min.geojson`);
        }

        urls.add('./_content/Blazor.UI.RCL/data/constellations.lines.min.geojson');
        urls.add('/_content/Blazor.UI.RCL/data/constellations.lines.min.geojson');

        return [...urls];
    })();
    let constellationGeoJsonPromise = null;
    let constellationLineSegmentsPromise = null;
    let constellationLabelEntriesPromise = null;
    let constellationGeoJsonResolvedUrl = null;

    const normalizeConstellationLineRa = (longitudeDegrees) => longitudeDegrees;

    const getConstellationProjectionCandidates = (longitudeDegrees) => {
        if (!Number.isFinite(longitudeDegrees)) {
            return [];
        }

        const wrapped360 = ((longitudeDegrees % 360) + 360) % 360;
        const signed = wrapped360 > 180 ? wrapped360 - 360 : wrapped360;
        const candidates = [
            longitudeDegrees,
            wrapped360,
            signed,
            signed + 360,
            signed - 360
        ];

        return candidates.filter((value, index) =>
            Number.isFinite(value) && candidates.findIndex((candidate) => Math.abs(candidate - value) < 1e-9) === index);
    };

    const getProjectedPointComponents = (projectedPoint) => {
        if (!projectedPoint) {
            return null;
        }

        const x = Number.isFinite(projectedPoint[0]) ? projectedPoint[0]
            : (Number.isFinite(projectedPoint.x) ? projectedPoint.x
                : (Number.isFinite(projectedPoint.cx) ? projectedPoint.cx : null));
        const y = Number.isFinite(projectedPoint[1]) ? projectedPoint[1]
            : (Number.isFinite(projectedPoint.y) ? projectedPoint.y
                : (Number.isFinite(projectedPoint.cy) ? projectedPoint.cy : null));

        if (!Number.isFinite(x) || !Number.isFinite(y)) {
            return null;
        }

        return [x, y];
    };

    const toConstellationPolyline = (coordinates) => {
        if (!Array.isArray(coordinates)) {
            return null;
        }

        const points = coordinates
            .filter((point) => Array.isArray(point) && point.length >= 2)
            .map(([longitudeDegrees, declinationDegrees]) => [
                normalizeConstellationLineRa(longitudeDegrees),
                declinationDegrees
            ]);

        return points.length >= 2 ? points : null;
    };

    const getConstellationGeoJson = async () => {
        if (!constellationGeoJsonPromise) {
            constellationGeoJsonPromise = (async () => {
                if (constellationGeoJsonEmbedded?.features?.length) {
                    constellationGeoJsonResolvedUrl = 'embedded-js-module';
                    window.__amConstellationGeoJsonUrl = constellationGeoJsonResolvedUrl;
                    window.__amConstellationGeoJsonError = null;
                    return constellationGeoJsonEmbedded;
                }

                let lastError = null;

                for (const url of CONSTELLATION_LINES_FALLBACK_URLS) {
                    try {
                        const response = await fetch(url, {
                            method: 'GET',
                            credentials: 'same-origin'
                        });

                        if (!response.ok) {
                            throw new Error(`HTTP ${response.status}`);
                        }

                        const json = await response.json();
                        constellationGeoJsonResolvedUrl = url;
                        window.__amConstellationGeoJsonUrl = url;
                        return json;
                    } catch (error) {
                        lastError = error;
                    }
                }

                const attemptedUrls = CONSTELLATION_LINES_FALLBACK_URLS.join(', ');
                const finalError = new Error(`Failed to load constellation lines. Tried: ${attemptedUrls}. Last error: ${lastError?.message || lastError || 'unknown'}`);
                window.__amConstellationGeoJsonUrl = null;
                window.__amConstellationGeoJsonError = finalError.message;
                throw finalError;
            })().catch((error) => {
                constellationGeoJsonPromise = null;
                throw error;
            });
        }

        return constellationGeoJsonPromise;
    };

    const getConstellationGeometryPoints = (geometry) => {
        if (!geometry?.coordinates) {
            return [];
        }

        if (geometry.type === 'LineString') {
            return Array.isArray(geometry.coordinates) ? geometry.coordinates : [];
        }

        if (geometry.type === 'MultiLineString') {
            return geometry.coordinates.flatMap((part) => Array.isArray(part) ? part : []);
        }

        return [];
    };

    const buildConstellationLabelEntry = (feature, index) => {
        const points = getConstellationGeometryPoints(feature?.geometry)
            .filter((point) => Array.isArray(point) && point.length >= 2);

        if (points.length === 0) {
            return null;
        }

        let sumX = 0;
        let sumY = 0;
        let sumZ = 0;

        points.forEach(([longitudeDegrees, declinationDegrees]) => {
            const raDegrees = normalizeConstellationLineRa(longitudeDegrees);
            const raRadians = raDegrees * (Math.PI / 180);
            const decRadians = declinationDegrees * (Math.PI / 180);
            const cosDec = Math.cos(decRadians);

            sumX += cosDec * Math.cos(raRadians);
            sumY += cosDec * Math.sin(raRadians);
            sumZ += Math.sin(decRadians);
        });

        const pointCount = points.length;
        const avgX = sumX / pointCount;
        const avgY = sumY / pointCount;
        const avgZ = sumZ / pointCount;
        const vectorLength = Math.sqrt((avgX * avgX) + (avgY * avgY) + (avgZ * avgZ));

        let raDegrees;
        let decDegrees;

        if (vectorLength <= 1e-6) {
            raDegrees = normalizeConstellationLineRa(points[0][0]);
            decDegrees = points[0][1];
        } else {
            raDegrees = (Math.atan2(avgY, avgX) * 180 / Math.PI + 360) % 360;
            decDegrees = Math.atan2(avgZ, Math.sqrt((avgX * avgX) + (avgY * avgY))) * 180 / Math.PI;
        }

        const shortLabel = typeof feature?.properties?.id === 'string' && feature.properties.id
            ? feature.properties.id
            : `C${index + 1}`;
        const longLabel = typeof feature?.properties?.name === 'string' && feature.properties.name
            ? feature.properties.name
            : shortLabel;
        const rank = Number.isFinite(feature?.properties?.rank)
            ? Number(feature.properties.rank)
            : 99;

        return {
            key: `${shortLabel}:${longLabel}:${index}`,
            shortLabel,
            longLabel,
            rank,
            ra: raDegrees,
            dec: decDegrees
        };
    };

    const getConstellationOverrideParts = (overrideValue) => {
        if (Array.isArray(overrideValue)) {
            return overrideValue;
        }

        return Array.isArray(overrideValue?.parts)
            ? overrideValue.parts
            : null;
    };

    const hasReplaceConstellationOverride = (constellationId) => {
        if (typeof constellationId !== 'string' || !constellationId) {
            return false;
        }

        const overrideValue = constellationLineOverrides?.[constellationId];
        return overrideValue?.replace === true && Array.isArray(getConstellationOverrideParts(overrideValue));
    };

    const getConstellationLineSegments = async () => {
        if (!constellationLineSegmentsPromise) {
            constellationLineSegmentsPromise = getConstellationGeoJson()
                .then((geoJson) => {
                    const features = Array.isArray(geoJson?.features) ? geoJson.features : [];
                    const segments = [];

                    features.forEach((feature) => {
                        const constellationId = typeof feature?.properties?.id === 'string'
                            ? feature.properties.id
                            : null;
                        const geometry = feature?.geometry;

                        if (!geometry?.coordinates || hasReplaceConstellationOverride(constellationId)) {
                            return;
                        }

                        if (geometry.type === 'LineString') {
                            const line = toConstellationPolyline(geometry.coordinates);
                            if (line) {
                                segments.push(line);
                            }

                            return;
                        }

                        if (geometry.type === 'MultiLineString') {
                            geometry.coordinates.forEach((part) => {
                                const line = toConstellationPolyline(part);
                                if (line) {
                                    segments.push(line);
                                }
                            });
                        }
                    });

                    Object.values(constellationLineOverrides || {}).forEach((overrideValue) => {
                        const parts = getConstellationOverrideParts(overrideValue);

                        if (!Array.isArray(parts)) {
                            return;
                        }

                        parts.forEach((part) => {
                            const line = toConstellationPolyline(part);
                            if (line) {
                                segments.push(line);
                            }
                        });
                    });

                    return segments;
                })
                .catch((error) => {
                    constellationLineSegmentsPromise = null;
                    throw error;
                });
        }

        return constellationLineSegmentsPromise;
    };

    const getConstellationSegmentsById = async () => {
        const geoJson = await getConstellationGeoJson();
        const features = Array.isArray(geoJson?.features) ? geoJson.features : [];
        const segmentsById = new Map();

        features.forEach((feature) => {
            const constellationId = typeof feature?.properties?.id === 'string'
                ? feature.properties.id
                : null;
            const geometry = feature?.geometry;

            if (!constellationId || !geometry?.coordinates) {
                return;
            }

            if (!segmentsById.has(constellationId)) {
                segmentsById.set(constellationId, []);
            }

            const bucket = segmentsById.get(constellationId);

            if (geometry.type === 'LineString') {
                const line = toConstellationPolyline(geometry.coordinates);
                if (line) {
                    bucket.push(line);
                }

                return;
            }

            if (geometry.type === 'MultiLineString') {
                geometry.coordinates.forEach((part) => {
                    const line = toConstellationPolyline(part);
                    if (line) {
                        bucket.push(line);
                    }
                });
            }
        });

        Object.entries(constellationLineOverrides || {}).forEach(([constellationId, overrideValue]) => {
            const parts = getConstellationOverrideParts(overrideValue);

            if (!Array.isArray(parts) || parts.length === 0) {
                return;
            }

            if (overrideValue?.replace === true || !segmentsById.has(constellationId)) {
                segmentsById.set(constellationId, []);
            }

            const bucket = segmentsById.get(constellationId);
            parts.forEach((part) => {
                const line = toConstellationPolyline(part);
                if (line) {
                    bucket.push(line);
                }
            });
        });

        return segmentsById;
    };

    const getConstellationLabelEntries = async () => {
        if (!constellationLabelEntriesPromise) {
            constellationLabelEntriesPromise = getConstellationGeoJson()
                .then((geoJson) => {
                    const features = Array.isArray(geoJson?.features) ? geoJson.features : [];
                    return features
                        .map((feature, index) => buildConstellationLabelEntry(feature, index))
                        .filter((entry) => entry !== null);
                })
                .catch((error) => {
                    constellationLabelEntriesPromise = null;
                    throw error;
                });
        }

        return constellationLabelEntriesPromise;
    };

    const getTrackedAladinApi = () => {
        if (looksLikeBootstrappedAladinApi(window.__amAladinApi)) {
            return window.__amAladinApi;
        }

        if (looksLikeBootstrappedAladinApi(window.A)) {
            return window.A;
        }

        if (looksLikeBootstrappedAladinApi(globalThis.A)) {
            return globalThis.A;
        }

        if (looksLikeAnyAladinApi(window.__amAladinApi)) {
            return window.__amAladinApi;
        }

        if (looksLikeAnyAladinApi(window.A)) {
            return window.A;
        }

        if (looksLikeAnyAladinApi(globalThis.A)) {
            return globalThis.A;
        }

        return window.__amAladinApi || window.A || globalThis.A;
    };

    const getAladinGlobalState = () => {
        const api = getTrackedAladinApi();
        return {
            hasNamespace: !!api,
            hasAladinFactory: typeof api?.aladin === 'function',
            hasInitPromise: !!api?.init && typeof api.init.then === 'function',
            apiKeys: api ? Object.keys(api).slice(0, 16) : []
        };
    };

    const installAladinGlobalTracker = () => {
        if (window.__amAladinTrackerInstalled === true) {
            return;
        }

        const originalDescriptor = Object.getOwnPropertyDescriptor(window, 'A');
        const originalValue = originalDescriptor && 'value' in originalDescriptor
            ? originalDescriptor.value
            : window.A;

        window.__amAladinTrackerInstalled = true;
        window.__amAladinTrackerDescriptor = originalDescriptor || null;
        window.__amAladinAssignmentLog = Array.isArray(window.__amAladinAssignmentLog)
            ? window.__amAladinAssignmentLog
            : [];
        window.__amAladinCurrent = originalValue;
        window.__amAladinApi = looksLikeAnyAladinApi(originalValue)
            ? originalValue
            : (looksLikeAnyAladinApi(window.__amAladinApi) ? window.__amAladinApi : null);

        Object.defineProperty(window, 'A', {
            configurable: true,
            enumerable: true,
            get() {
                return window.__amAladinCurrent || window.__amAladinApi;
            },
            set(value) {
                const isValidAladinApi = looksLikeAnyAladinApi(value);

                if (isValidAladinApi) {
                    window.__amAladinCurrent = value;
                    window.__amAladinApi = value;
                } else if (!window.__amAladinApi) {
                    window.__amAladinCurrent = value;
                }

                try {
                    window.__amAladinAssignmentLog.push({
                        at: new Date().toISOString(),
                        accepted: !!isValidAladinApi || !window.__amAladinApi,
                        hasAladinFactory: typeof value?.aladin === 'function',
                        hasInitPromise: !!value?.init && typeof value.init.then === 'function',
                        hasOwnWasmLibs: !!value && Object.prototype.hasOwnProperty.call(value, 'wasmLibs'),
                        hasWasmLibs: !!value?.wasmLibs,
                        keys: value ? Object.keys(value).slice(0, 12) : []
                    });
                } catch {
                    // Keep tracking best-effort
                }
            }
        });
    };

    const waitForRenderableContainer = async (element, timeoutMs = 2500) => {
        const startTime = Date.now();

        while (Date.now() - startTime < timeoutMs) {
            if (!element || !element.isConnected) {
                throw new Error(`Container element with ID '${containerId}' is no longer available`);
            }

            const rect = element.getBoundingClientRect();
            if (rect.width > 24 && rect.height > 24) {
                return rect;
            }

            await new Promise(resolve => requestAnimationFrame(resolve));
        }

        const finalRect = element?.getBoundingClientRect?.();
        throw new Error(`Sky Atlas container '${containerId}' did not reach a renderable size in time (${Math.round(finalRect?.width || 0)}x${Math.round(finalRect?.height || 0)})`);
    };

    const waitForContainerElement = async (elementId, timeoutMs = 2500) => {
        const startTime = Date.now();

        while (Date.now() - startTime < timeoutMs) {
            const element = document.getElementById(elementId);
            if (element) {
                return element;
            }

            await new Promise(resolve => requestAnimationFrame(resolve));
        }

        throw new Error(`Container element with ID '${elementId}' not found`);
    };
    
    // Function to load a script
    let loadScript = (src) => {
        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = src;
            script.onload = () => resolve();
            script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
            document.head.appendChild(script);
        });
    };

    const loadStylesheet = (href, key) => {
        return new Promise((resolve, reject) => {
            const existing = document.querySelector(`link[data-am-aladin-style="${key}"], link[href="${href}"]`);
            if (existing) {
                resolve();
                return;
            }

            const link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = href;
            link.dataset.amAladinStyle = key;
            link.onload = () => resolve();
            link.onerror = () => reject(new Error(`Failed to load stylesheet: ${href}`));
            document.head.appendChild(link);
        });
    };

    const waitForAladinBootstrap = async (build, timeoutMs = 8000) => {
        await new Promise((resolve, reject) => {
            const startTime = Date.now();
            const checkAladin = () => {
                const api = getTrackedAladinApi();
                if (looksLikeAladinApiForBuild(api, build)) {
                    window.__amAladinVersion = build.id;
                    resolve();
                    return;
                }

                if (Date.now() - startTime > timeoutMs) {
                    reject(new Error(`Timeout waiting for Aladin Lite ${build.label} to load`));
                    return;
                }

                setTimeout(checkAladin, 50);
            };

            checkAladin();
        });
    };

    const removeAladinScripts = (build) => {
        const selectors = [
            `script[data-am-asset="aladin-lite-${build.id}"]`,
            `script[data-am-aladin-build="${build.id}"]`,
            `script[src="${build.url}"]`
        ];

        for (const selector of selectors) {
            document.querySelectorAll(selector).forEach(script => script.remove());
        }

        if (build.cssUrl) {
            document.querySelectorAll(`link[data-am-aladin-style="aladin-lite-${build.id}"], link[href="${build.cssUrl}"]`)
                .forEach(link => link.remove());
        }
    };

    const resetAladinGlobal = (build) => {
        removeAladinScripts(build);

        try {
            delete window.A;
        } catch {
            window.A = undefined;
        }

        window.__amAladinCurrent = null;
        window.__amAladinApi = null;
        window.__amAladinVersion = null;
    };

    const ensureAladinScript = async (build, options = {}) => {
        const forceReload = options.forceReload === true;

        if (forceReload) {
            resetAladinGlobal(build);
        }

        if (build.legacy && window.__amAladinVersion && window.__amAladinVersion !== build.id) {
            try {
                delete window.A;
            } catch {
                window.A = undefined;
            }

            window.__amAladinCurrent = null;
            window.__amAladinApi = null;
            window.__amAladinVersion = null;
        }

        if (isApplePlatform() && !build.legacy && window.astroAppAssets?.ensureAladinReady) {
            await window.astroAppAssets.ensureAladinReady({ forceReload });

            const trackedApi = getTrackedAladinApi();
            if (looksLikeAladinApiForBuild(trackedApi, build)) {
                window.__amAladinVersion = build.id;
                return;
            }
        }

        const existingApi = getTrackedAladinApi();
        if (looksLikeAladinApiForBuild(existingApi, build)
            && window.__amAladinVersion === build.id) {
            return;
        }

        installAladinGlobalTracker();

        if (!forceReload && existingApi && !looksLikeAladinApiForBuild(existingApi, build)) {
            resetAladinGlobal(build);
        }

        if (build.legacy) {
            if (build.cssUrl) {
                await loadStylesheet(build.cssUrl, `aladin-lite-${build.id}`);
            }

            if (build.jqueryUrl && typeof window.jQuery === 'undefined') {
                await loadScript(build.jqueryUrl);
            }
        }

        const existingScript = document.querySelector(`script[data-am-asset="aladin-lite-${build.id}"], script[data-am-aladin-build="${build.id}"], script[src="${build.url}"]`);

        if (!existingScript) {
            const script = document.createElement('script');
            script.src = build.url;
            script.async = true;
            script.dataset.amAladinBuild = build.id;
            document.head.appendChild(script);
        }

        await waitForAladinBootstrap(build);
        const trackedApi = getTrackedAladinApi();
        if (!looksLikeAnyAladinApi(window.__amAladinApi) && looksLikeAnyAladinApi(trackedApi)) {
            window.__amAladinApi = trackedApi;
        }
    };

    const ensureAladinCoreReady = async (build, options = {}) => {
        const api = getTrackedAladinApi();

        if (!looksLikeAladinApiForBuild(api, build)) {
            throw new Error(`Aladin ${build.label} script loaded without window.A being ready`);
        }

        if (build.legacy) {
            await new Promise(resolve => requestAnimationFrame(() => resolve()));
            return;
        }

        await api.init;
        await new Promise(resolve => requestAnimationFrame(() => resolve()));
    };

    const ensureAppleAladinInitReady = async (build) => {
        if (!isApplePlatform()) {
            await ensureAladinCoreReady(build);
            return;
        }

        await ensureAladinCoreReady(build);
    };

    const waitForAnimationFrames = async (frameCount = 1) => {
        const totalFrames = Number.isFinite(frameCount) && frameCount > 0
            ? Math.floor(frameCount)
            : 1;

        for (let frame = 0; frame < totalFrames; frame++) {
            await new Promise(resolve => requestAnimationFrame(() => resolve()));
        }
    };

    const probeAppleWebGl2 = async (hostElement) => {
        if (!isApplePlatform()) {
            return { skipped: true };
        }

        const probeCanvas = document.createElement('canvas');
        probeCanvas.width = 64;
        probeCanvas.height = 64;
        probeCanvas.setAttribute('aria-hidden', 'true');
        probeCanvas.style.position = 'absolute';
        probeCanvas.style.pointerEvents = 'none';
        probeCanvas.style.opacity = '0';
        probeCanvas.style.width = '64px';
        probeCanvas.style.height = '64px';
        probeCanvas.style.left = '-10000px';
        probeCanvas.style.top = '0';

        const diagnostics = {
            supported: false,
            contextName: null,
            version: null,
            shadingLanguageVersion: null,
            vendor: null,
            renderer: null,
            reason: null
        };

        try {
            (hostElement || document.body || document.documentElement).appendChild(probeCanvas);

            for (const contextName of ['webgl2', 'experimental-webgl2']) {
                const context = probeCanvas.getContext(contextName, {
                    alpha: true,
                    antialias: true,
                    depth: true,
                    powerPreference: 'high-performance',
                    preserveDrawingBuffer: false,
                    premultipliedAlpha: true
                });

                if (!context) {
                    continue;
                }

                diagnostics.supported = true;
                diagnostics.contextName = contextName;
                diagnostics.version = context.getParameter(context.VERSION);
                diagnostics.shadingLanguageVersion = context.getParameter(context.SHADING_LANGUAGE_VERSION);
                diagnostics.vendor = context.getParameter(context.VENDOR);
                diagnostics.renderer = context.getParameter(context.RENDERER);

                try {
                    context.clearColor(0, 0, 0, 1);
                    context.clear(context.COLOR_BUFFER_BIT);
                    context.finish?.();
                } catch (error) {
                    diagnostics.reason = error?.message || String(error);
                }

                break;
            }

            if (!diagnostics.supported) {
                diagnostics.reason = 'getContext("webgl2") returned null on an attached canvas';
            }
        } catch (error) {
            diagnostics.reason = error?.message || String(error);
        } finally {
            probeCanvas.remove();
        }

        pushTrace('initializeAladin:apple-webgl-probe', diagnostics);
        debugLog('initializeAladin apple webgl probe', diagnostics);
        return diagnostics;
    };

    const captureAladinInternalErrors = (factory) => {
        const originalConsoleError = console.error;
        const capturedMessages = [];

        const formatConsoleArg = (value) => {
            if (value instanceof Error) {
                return value.stack || value.message;
            }

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

            try {
                return JSON.stringify(value);
            } catch {
                return String(value);
            }
        };

        console.error = (...args) => {
            try {
                const message = args.map(formatConsoleArg).join(' ');
                if (message.includes('Problem initializing Aladin Lite')) {
                    capturedMessages.push(message);
                }
            } catch {
                // Keep diagnostic capture best-effort
            }

            return originalConsoleError.apply(console, args);
        };

        try {
            return {
                value: factory(),
                capturedMessages,
                error: null
            };
        } catch (error) {
            return {
                value: null,
                capturedMessages,
                error
            };
        } finally {
            console.error = originalConsoleError;
        }
    };

    const ensureAppleAladinConstructorReadiness = async (hostElement) => {
        if (!isApplePlatform()) {
            return null;
        }

        const visibilityState = typeof document.visibilityState === 'string'
            ? document.visibilityState
            : 'unknown';

        pushTrace('initializeAladin:apple-readiness', { visibilityState });
        await waitForAnimationFrames(2);
        const diagnostics = await probeAppleWebGl2(hostElement);
        await new Promise(resolve => setTimeout(resolve, 120));
        await waitForAnimationFrames(1);
        return diagnostics;
    };

    const applyAppleStartupSurfaceMitigation = async (containerElement, initRect) => {
        if (!isApplePlatform() || !containerElement?.style) {
            return {
                applied: false,
                restore: async () => null
            };
        }

        const rect = initRect || containerElement.getBoundingClientRect?.();
        const width = Number(rect?.width) || 0;
        const height = Number(rect?.height) || 0;
        if (width <= 0 || height <= 0) {
            return {
                applied: false,
                restore: async () => null
            };
        }

        const maxDimension = 720;
        const maxArea = 480000;
        const areaScale = Math.min(1, Math.sqrt(maxArea / (width * height)));
        const dimensionScale = Math.min(1, maxDimension / Math.max(width, height));
        const scale = Math.min(areaScale, dimensionScale);

        if (!Number.isFinite(scale) || scale >= 0.98) {
            return {
                applied: false,
                restore: async () => null
            };
        }

        const targetWidth = Math.max(320, Math.round(width * scale));
        const targetHeight = Math.max(320, Math.round(height * scale));
        const previousStyle = {
            width: containerElement.style.width,
            height: containerElement.style.height,
            minWidth: containerElement.style.minWidth,
            minHeight: containerElement.style.minHeight,
            maxWidth: containerElement.style.maxWidth,
            maxHeight: containerElement.style.maxHeight,
            overflow: containerElement.style.overflow
        };

        containerElement.style.width = `${targetWidth}px`;
        containerElement.style.height = `${targetHeight}px`;
        containerElement.style.minWidth = `${targetWidth}px`;
        containerElement.style.minHeight = `${targetHeight}px`;
        containerElement.style.maxWidth = `${targetWidth}px`;
        containerElement.style.maxHeight = `${targetHeight}px`;
        containerElement.style.overflow = 'hidden';

        void containerElement.offsetWidth;
        pushTrace('initializeAladin:apple-surface-mitigation-applied', {
            originalWidth: Math.round(width),
            originalHeight: Math.round(height),
            targetWidth,
            targetHeight,
            scale: Number(scale.toFixed(3))
        });
        debugLog('initializeAladin apple surface mitigation applied', {
            width,
            height,
            targetWidth,
            targetHeight,
            scale
        });
        await waitForAnimationFrames(2);

        return {
            applied: true,
            targetWidth,
            targetHeight,
            scale,
            restore: async () => {
                containerElement.style.width = previousStyle.width;
                containerElement.style.height = previousStyle.height;
                containerElement.style.minWidth = previousStyle.minWidth;
                containerElement.style.minHeight = previousStyle.minHeight;
                containerElement.style.maxWidth = previousStyle.maxWidth;
                containerElement.style.maxHeight = previousStyle.maxHeight;
                containerElement.style.overflow = previousStyle.overflow;
                void containerElement.offsetWidth;
                pushTrace('initializeAladin:apple-surface-mitigation-restored', {
                    restoredWidth: Math.round(width),
                    restoredHeight: Math.round(height)
                });
                await waitForAnimationFrames(2);
            }
        };
    };

    const installAppleWebGlContextMitigation = () => {
        if (!isApplePlatform() || typeof HTMLCanvasElement === 'undefined') {
            return null;
        }

        const originalGetContext = HTMLCanvasElement.prototype.getContext;
        const requests = [];

        HTMLCanvasElement.prototype.getContext = function(type, attributes) {
            const contextType = typeof type === 'string' ? type.toLowerCase() : '';
            if (!['webgl2', 'experimental-webgl2', 'webgl', 'experimental-webgl'].includes(contextType)) {
                return originalGetContext.call(this, type, attributes);
            }

            const requestedAttributes = attributes && typeof attributes === 'object'
                ? { ...attributes }
                : {};
            const mitigatedAttributes = {
                ...requestedAttributes,
                alpha: false,
                antialias: false,
                depth: false,
                stencil: false,
                premultipliedAlpha: false,
                preserveDrawingBuffer: false,
                powerPreference: 'default'
            };

            requests.push({
                type: contextType,
                requestedAttributes,
                mitigatedAttributes
            });

            return originalGetContext.call(this, type, mitigatedAttributes)
                || originalGetContext.call(this, type, attributes);
        };

        return () => {
            HTMLCanvasElement.prototype.getContext = originalGetContext;
            return {
                requests
            };
        };
    };

    const installAppleWebClientDiagnostics = () => {
        return null;
    };

    const buildAladinOptionsDiagnostic = (aladinOptions) => {
        if (!aladinOptions || typeof aladinOptions !== 'object') {
            return null;
        }

        const keysOfInterest = [
            'survey',
            'target',
            'cooFrame',
            'fov',
            'projection',
            'northPoleOrientation',
            'showZoomControl',
            'showLayersControl',
            'showFullscreenControl',
            'showSimbadPointerControl',
            'showCooGridControl',
            'showSettingsControl',
            'showColorPickerControl',
            'showShareControl',
            'showStatusBar',
            'showFrame',
            'showFov',
            'showCooLocation',
            'showProjectionControl',
            'showContextMenu',
            'showReticle',
            'showCatalog',
            'showCooGrid',
            'inertia',
            'lockNorthUp',
            'fullScreen'
        ];

        const snapshot = {};
        for (const key of keysOfInterest) {
            if (key in aladinOptions) {
                snapshot[key] = aladinOptions[key];
            }
        }

        return snapshot;
    };

    const buildAppleDiagnosticSnapshot = (containerElement, initRect, aladinBuild, appleProbe, attempt, aladinOptions = null) => {
        if (!isApplePlatform()) {
            return null;
        }

        const rect = containerElement?.getBoundingClientRect?.() || initRect || null;
        const traceTail = Array.isArray(window.__amSkyAtlasTrace)
            ? window.__amSkyAtlasTrace.slice(-6).map(entry => ({
                event: entry?.event || null,
                payload: entry?.payload || null
            }))
            : [];

        return {
            attempt,
            aladinVersion: aladinBuild?.label || null,
            platform: settings.platform || options?.platform || 'apple-detected',
            userAgent: navigator.userAgent || null,
            visibilityState: document.visibilityState || null,
            devicePixelRatio: window.devicePixelRatio || null,
            viewport: {
                width: window.innerWidth || document.documentElement?.clientWidth || 0,
                height: window.innerHeight || document.documentElement?.clientHeight || 0
            },
            container: rect ? {
                width: Math.round(rect.width || 0),
                height: Math.round(rect.height || 0),
                top: Math.round(rect.top || 0),
                left: Math.round(rect.left || 0),
                connected: !!containerElement?.isConnected
            } : null,
            webgl2: appleProbe || null,
            trackedAladinState: getAladinGlobalState(),
            windowAState: {
                hasWindowA: !!window.A,
                hasAladinFactory: typeof window.A?.aladin === 'function',
                hasInitPromise: !!window.A?.init && typeof window.A.init.then === 'function',
                keys: window.A ? Object.keys(window.A).slice(0, 16) : []
            },
            constructorOptions: buildAladinOptionsDiagnostic(aladinOptions),
            aladinAssignmentsTail: Array.isArray(window.__amAladinAssignmentLog)
                ? window.__amAladinAssignmentLog.slice(-4)
                : [],
            lastWebClientDiagnostic: window.__amLastAladinWebClientError || null,
            traceTail
        };
    };

    const formatAppleDiagnosticSnapshot = (snapshot) => {
        if (!snapshot) {
            return '';
        }

        try {
            return ` Apple diagnostic: ${JSON.stringify(snapshot)}`;
        } catch (error) {
            return ` Apple diagnostic unavailable: ${error?.message || String(error)}`;
        }
    };

    try {
        // Check if container element exists before initializing
        let containerElement = await waitForContainerElement(containerId);
        const initRect = await waitForRenderableContainer(containerElement);
        const initialFov = normalizeInitialFov(settings.fov);
        debugLog('initializeAladin', {
            containerId,
            viewport: {
                width: window.innerWidth || document.documentElement?.clientWidth || 0,
                height: window.innerHeight || document.documentElement?.clientHeight || 0
            },
            containerRect: {
                width: Math.round(initRect.width),
                height: Math.round(initRect.height),
                top: Math.round(initRect.top),
                left: Math.round(initRect.left)
            },
            compactMode: !!options?.compact,
            initialFov
        });
        pushTrace('initializeAladin', {
            containerId,
            viewportWidth: window.innerWidth || document.documentElement?.clientWidth || 0,
            viewportHeight: window.innerHeight || document.documentElement?.clientHeight || 0,
            containerWidth: Math.round(initRect.width),
            containerHeight: Math.round(initRect.height),
            initialFov
        });

        // Use direct HiPS URL to avoid IRSA CORS issues
        const aladinBuildsToTry = getPreferredAladinBuilds();
                
        // Start with the requested survey. For high quality, we may replace the layer
        // after init with a preferred HiPS encoding from the survey properties.
        const surveyConfig = settings.survey || 'CDS/P/DSS2/color';
        const normalizedTarget = normalizeTargetForAladin(settings.target);

        const isApple = isApplePlatform();
        const createAladinOptions = (build) => build?.legacy
            ? {
                survey: normalizeSurveyForBuild(surveyConfig, build),
                fov: initialFov,
                target: normalizedTarget,
                cooFrame: 'ICRS',
                showReticle: false,
                showZoomControl: false,
                showFullscreenControl: false,
                showLayersControl: false,
                showGotoControl: false,
                showShareControl: false,
                showSimbadPointerControl: false,
                showFrame: false,
                fullScreen: false
            }
            : {
            survey: surveyConfig,
            fov: initialFov,
            target: normalizedTarget,
            cooFrame: 'ICRS',
            projection: 'SIN',
            northPoleOrientation: 0,
            showCooGrid: false, // set the grid
            showCooGridControl: false, 
            showReticle: !isApple,
            showFrame: false,
            showFov: false,
            showStatusBar: !isApple,
            showContextMenu: !isApple,
            showProjectionControl: false,
            showGotoControl: false,
            showSimbadPointerControl: false,
            showZoomControl: false,
            showLayersControl: false,
            showFullscreenControl: false,
            showCooLocation: !isApple,
            inertia: !isApple
        };

        let aladin;
        let lastInitError = null;
        let activeAladinVersion = null;
        let lastAppleDiagnosticSnapshot = null;
        let appleLegacyFallbackQueued = false;
        for (let buildIndex = 0; buildIndex < aladinBuildsToTry.length; buildIndex++) {
            const aladinBuild = aladinBuildsToTry[buildIndex];
            const aladinOptions = createAladinOptions(aladinBuild);
            try {
                await ensureAladinScript(aladinBuild);
                await ensureAppleAladinInitReady(aladinBuild);

                const maxAttempts = isApplePlatform() ? 3 : 2;
                for (let attempt = 1; attempt <= maxAttempts; attempt++) {
                    let restoreAppleWebClientDiagnostics = null;
                    let restoreAppleSurfaceMitigation = null;
                    let restoreAppleWebGlMitigation = null;
                    try {
                        const appleProbe = await ensureAppleAladinConstructorReadiness(containerElement);
                        const appleSurfaceMitigation = await applyAppleStartupSurfaceMitigation(containerElement, initRect);
                        restoreAppleSurfaceMitigation = appleSurfaceMitigation.restore;
                        lastAppleDiagnosticSnapshot = buildAppleDiagnosticSnapshot(
                            containerElement,
                            initRect,
                            aladinBuild,
                            appleProbe,
                            attempt,
                            aladinOptions);
                        restoreAppleWebClientDiagnostics = installAppleWebClientDiagnostics();
                        restoreAppleWebGlMitigation = installAppleWebGlContextMitigation();
                        pushTrace('initializeAladin:attempt', {
                            attempt,
                            containerId,
                            initialFov,
                            aladinVersion: aladinBuild.label,
                            appleProbeSupported: appleProbe?.supported ?? null,
                            appleSurfaceScale: appleSurfaceMitigation?.applied
                                ? Number(appleSurfaceMitigation.scale?.toFixed?.(3) || appleSurfaceMitigation.scale)
                                : null
                        });

                        const aladinApi = getTrackedAladinApi();
                        const {
                            value: nextAladin,
                            capturedMessages,
                            error: constructorError
                        } = captureAladinInternalErrors(() => aladinApi.aladin(containerElement, aladinOptions));
                        if (constructorError) {
                            const internalMessage = capturedMessages[0]
                                ? ` Hidden Aladin error: ${capturedMessages[0]}`
                                : '';
                            const baseMessage = constructorError?.message || String(constructorError);
                            throw new Error(`${baseMessage}${internalMessage}`);
                        }
                        if (!isReadyAladinInstanceForBuild(nextAladin, aladinBuild)) {
                            const webClientDiagnostic = restoreAppleWebClientDiagnostics?.();
                            restoreAppleWebClientDiagnostics = null;
                            const webClientMessage = webClientDiagnostic?.message
                                ? ` WebClient error: ${webClientDiagnostic.message}`
                                : '';
                            const internalMessage = capturedMessages[0]
                                ? ` Hidden Aladin error: ${capturedMessages[0]}`
                                : '';
                            const readinessLabel = aladinBuild.legacy
                                ? 'legacy renderer'
                                : 'wasm client';
                            throw new Error(`Aladin returned without a ready ${readinessLabel}.${webClientMessage}${internalMessage}`);
                        }

                        if (capturedMessages.length > 0) {
                            pushTrace('initializeAladin:attempt-hidden-error', {
                                aladinVersion: aladinBuild.label,
                                message: capturedMessages[0]
                            });
                            debugLog('initializeAladin hidden internal error (constructor recovered)', {
                                aladinVersion: aladinBuild.label,
                                message: capturedMessages[0]
                            });
                        }

                        const webGlMitigationResult = restoreAppleWebGlMitigation?.();
                        restoreAppleWebGlMitigation = null;
                        restoreAppleWebClientDiagnostics?.();
                        restoreAppleWebClientDiagnostics = null;
                        await restoreAppleSurfaceMitigation?.();
                        restoreAppleSurfaceMitigation = null;
                        aladin = aladinBuild.legacy
                            ? applyLegacyAladinCompatibility(nextAladin)
                            : nextAladin;
                        activeAladinVersion = aladinBuild.label;
                        if (isApplePlatform()) {
                            try {
                                aladin.setFov?.(initialFov);
                                aladin.view?.fixLayoutDimensions?.();
                                aladin.view?.requestRedraw?.();
                                if (!capturedMessages.length && webGlMitigationResult?.requests?.length) {
                                    pushTrace('initializeAladin:apple-webgl-mitigation', {
                                        requests: webGlMitigationResult.requests.length
                                    });
                                }
                            } catch (appleFinalizeError) {
                                debugLog('initializeAladin apple finalize skipped', {
                                    error: appleFinalizeError,
                                    aladinVersion: aladinBuild.label
                                });
                            }
                        }
                        break;
                    } catch (error) {
                        restoreAppleWebGlMitigation?.();
                        restoreAppleWebGlMitigation = null;
                        const webClientDiagnostic = restoreAppleWebClientDiagnostics?.();
                        restoreAppleWebClientDiagnostics = null;
                        await restoreAppleSurfaceMitigation?.();
                        restoreAppleSurfaceMitigation = null;
                        const errorMessage = error?.message || String(error);
                        if (webClientDiagnostic) {
                            lastAppleDiagnosticSnapshot = {
                                ...(lastAppleDiagnosticSnapshot || {}),
                                lastWebClientDiagnostic: webClientDiagnostic
                            };
                        }
                        const enrichedMessage = webClientDiagnostic?.message && !errorMessage.includes(webClientDiagnostic.message)
                            ? `${errorMessage} WebClient error: ${webClientDiagnostic.message}`
                            : errorMessage;
                        lastInitError = enrichedMessage === errorMessage
                            ? error
                            : new Error(enrichedMessage);
                        const shouldQueueAppleLegacyFallback = isApple
                            && !aladinBuild.legacy
                            && !appleLegacyFallbackQueued
                            && enrichedMessage.includes('failed to create framebuffer');
                        if (shouldQueueAppleLegacyFallback) {
                            appleLegacyFallbackQueued = true;
                            const fallbackBuild = getAppleFramebufferFallbackBuild();
                            aladinBuildsToTry.push(fallbackBuild);
                            pushTrace('initializeAladin:apple-legacy-fallback-queued', {
                                fromVersion: aladinBuild.label,
                                toVersion: fallbackBuild.label,
                                reason: 'failed to create framebuffer'
                            });
                            debugLog('initializeAladin queued apple legacy fallback', {
                                fromVersion: aladinBuild.label,
                                toVersion: fallbackBuild.label
                            });
                        }
                        pushTrace('initializeAladin:attempt-failed', {
                            attempt,
                            aladinVersion: aladinBuild.label,
                            message: enrichedMessage
                        });
                        debugLog('initializeAladin attempt failed', {
                            attempt,
                            aladinVersion: aladinBuild.label,
                            error: lastInitError,
                            containerId,
                            initialFov
                        });

                        if (attempt >= maxAttempts) {
                            break;
                        }

                        const retryDelayMs = isApplePlatform() ? 320 : 180;
                        await new Promise(resolve => setTimeout(resolve, retryDelayMs));
                        containerElement = await waitForContainerElement(containerId);
                        await waitForRenderableContainer(containerElement);
                    }
                }

                if (aladin) {
                    break;
                }
            } catch (error) {
                lastInitError = error;
                pushTrace('initializeAladin:script-failed', {
                    aladinVersion: aladinBuild.label,
                    message: error?.message || String(error)
                });
                debugLog('initializeAladin script load failed', {
                    aladinVersion: aladinBuild.label,
                    error
                });
            }
        }

        if (!aladin) {
            const appleDiagnosticSuffix = formatAppleDiagnosticSnapshot(lastAppleDiagnosticSnapshot);
            throw new Error(`Aladin constructor failed for '${containerId}' (${Math.round(initRect.width)}x${Math.round(initRect.height)}, fov ${initialFov}, versions ${aladinBuildsToTry.map(build => build.label).join(' -> ')}): ${lastInitError?.message || lastInitError || 'Unknown error'}${appleDiagnosticSuffix}`);
        }

        debugLog('initializeAladin succeeded', {
            containerId,
            initialFov,
            aladinVersion: activeAladinVersion
        });

        // Set position angle to 0 at the bottom (like Telescopius)
        aladin.setRotation(0);

        // Avoid projection/frame constructor issues on Apple WebKit by applying
        // optional frame adjustments only after the viewer is alive.
        try {
            if (typeof aladin.setFrame === 'function') {
                aladin.setFrame('ICRS');
            }
        } catch (error) {
            debugLog('initializeAladin setFrame skipped', {
                error,
                aladinVersion: activeAladinVersion
            });
        }
        
        // Track layer changes
        aladin.on('layerChanged', (layer) => {
        });

        // Update the cleanup function to remove these event listeners
        window.cleanupAladin = function() {
            if (aladin && aladin.view && aladin.view.imageCanvas) {
                aladin.view.imageCanvas.removeEventListener('click', clickHandler);
            }
        };
        
        // Function to update coordinates display
        const shouldDeferDotNetViewportSync = settings.deferDotNetViewportSync === true;
        const shouldEmitLiveCenterCoordinatesDuringInteraction =
            shouldDeferDotNetViewportSync &&
            settings.emitLiveCenterCoordinatesDuringInteraction === true;
        const viewportSyncDebounceMs = Number.isFinite(settings.viewportSyncDebounceMs)
            ? settings.viewportSyncDebounceMs
            : 450;
        const suspendMarkerLabelsDuringInteraction = shouldDeferDotNetViewportSync &&
            settings.suspendMarkerLabelsDuringInteraction === true;

        const updateCoordinates = (notifyDotNet = true) => {
            try {
                // Get current RA/Dec and FOV directly from Aladin instance
                const position = aladin.getRaDec();
                const fov = aladin.getFov(); // This returns [width, height]
                const fovWidth = fov[0];     // Get the width (first element)     

                if (position && position.length >= 2) {
                    const [ra, dec] = position;
                    
                    // Update the UI elements directly
                    updateCoordinateDisplay(ra, dec, fov);
                    
                    // Also update via DotNet if needed
                    if (notifyDotNet && dotNetHelper) {
                        dotNetHelper.invokeMethodAsync('UpdateCoordinates', {
                            ra: ra,
                            dec: dec,
                            fov: fovWidth
                        }).catch(e => console.error('Error updating coordinates:', e));
                    }
                }
            } catch (e) {
                console.error('Error in coordinate update:', e);
            }
        };
        
        // Add debounce function to prevent excessive updates
        function debounce(func, wait) {
            let timeout;
            return function() {
                const context = this;
                const args = arguments;
                clearTimeout(timeout);
                timeout = setTimeout(() => {                    
                    func.apply(context, args);

                }, wait);
            };
        }
        
        // Debounced version used to keep the inline coordinate display responsive.
        const debouncedDisplayUpdate = debounce(() => updateCoordinates(false), 50);
        const debouncedLiveUpdate = debounce(() => updateCoordinates(true), 50);
        const debouncedLiveCenterCoordinateSync = debounce(() => {
            try {
                const position = aladin.getRaDec();
                const fov = aladin.getFov();
                const fovWidth = fov[0];

                if (position && position.length >= 2 && dotNetHelper) {
                    const [ra, dec] = position;
                    dotNetHelper.invokeMethodAsync('NotifyLiveCenterCoordinates', {
                        ra: ra,
                        dec: dec,
                        fov: fovWidth
                    }).catch(e => console.error('Error updating live center coordinates:', e));
                }
            } catch (e) {
                console.error('Error in live center coordinate sync:', e);
            }
        }, 50);
        const debouncedViewportSync = debounce(() => {
            if (aladinInstance) {
                aladinInstance.setInteractionActive(false);
            }

            updateCoordinates(true);
        }, viewportSyncDebounceMs);
        

        const positionChangedHandler = () => {
            // Ensure rotation is always 0 (north up, east left)
            aladin.setRotation(0);
            // Keep local JS/UI state responsive, but defer .NET sync in WASM browser mode.
            debouncedDisplayUpdate();
            if (shouldDeferDotNetViewportSync) {
                if (shouldEmitLiveCenterCoordinatesDuringInteraction) {
                    debouncedLiveCenterCoordinateSync();
                }
                aladinInstance.setInteractionActive(true);
                debouncedViewportSync();
            } else {
                debouncedLiveUpdate();
            }
            aladinInstance.scheduleVisualRefresh({
                fov: true,
                labels: true,
                mosaic: true,
                astronomical: true
            });
        };
        
        // Add debounced FOV change handler for persistence
        let fovChangeTimeout;
        const debouncedFovSave = (fov) => {
            clearTimeout(fovChangeTimeout);
            fovChangeTimeout = setTimeout(() => {
                if (!shouldDeferDotNetViewportSync && aladinInstance.dotNetHelper) {
                    aladinInstance.dotNetHelper.invokeMethodAsync('OnFovChanged', fov);
                }
            }, 500); // Wait 500ms after last FOV change before saving
        };
        
        // Set up centralized event listeners for view changes
        aladin.on('positionChanged', positionChangedHandler);
        aladin.on('zoomChanged', () => {
            positionChangedHandler();
            // Also trigger FOV save for zoom changes
            const currentFov = aladin.getFov()[0];
            debouncedFovSave(currentFov);
        });

        // Add specific FOV change handler to ensure overlays update
        aladin.on('fovChanged', () => {
            aladinInstance.scheduleVisualRefresh({
                fov: true,
                mosaic: true,
                astronomical: true
            });
            
            // Notify Blazor component of FOV changes for persistence (debounced)
            const currentFov = aladin.getFov()[0];
            debouncedFovSave(currentFov);
        });

        // define function triggered when an object is clicked
        var objClicked;
        aladin.on('objectClicked', (object) => {
            if (object) {
                aladinInstance?.activateAsGlobalInstance?.();

                 if (object?.data?.isConstellationLabel === true) {
                    aladinInstance?.highlightConstellation?.(object.data?.constellationId);
                    return;
                }

                if (objClicked) {
                    objClicked.deselect();
                }

                objClicked = object;
                object.select();

                if (aladinInstance?.shouldSelectOnMarkerTextClick?.(object.data)) {
                    aladinInstance.invokeMarkerSelection(object.data?.objId);
                } else {
                    aladinInstance?.showMarkerPopupForObject?.(object);
                }
            } else if (objClicked) {
                objClicked.deselect();
                objClicked = null;
            }
        });
        
        // Store the Aladin instance
        let aladinInstance = {
            aladin: aladin,
            dotNetHelper: dotNetHelper, // Store the dotNetHelper reference
            markers: [],
            markerCatalog: null,
            fovSettings: null,
            fovOverlays: [], // Array to store multiple FOV overlays
            fovOverlayElements: new Map(), // Map overlay ID to DOM elements
            positionChangedListener: positionChangedHandler,
            showMarkerLabels: true, // Default to showing labels
            _lastHostWidth: 0,
            _lastHostHeight: 0,
            _zeroSizeRecoveryScheduled: false,
            _zeroSizeCount: 0,
            interactionActive: false,
            highQualityMode: true,
            _currentSurveyId: settings.survey || 'CDS/P/DSS2/color',
            _currentSurveyLayerQualityKey: null,
            _surveyChangeInProgress: false,
            _surveyInfoCache: new Map(),
            _isTouchZooming: false,
            _pendingInteractionWarmup: false,
            suspendMarkerLabelsDuringInteraction: suspendMarkerLabelsDuringInteraction,
            _markerLabelsTemporarilyHidden: false,
            _visualRefreshFrameId: null,
            _pendingVisualRefresh: {
                fov: false,
                labels: false,
                mosaic: false,
                astronomical: false
            },
            hoveredLabelBounds: null,
            lastHoveredLabelBounds: null,
            showConstellationsActive: false,
            constellationOverlay: null,
            constellationOverlayLoaded: false,
            constellationLabelCatalog: null,
            constellationLabelCatalogLoaded: false,
            highlightedConstellationId: null,
            constellationCanvasLayer: null,
            constellationLabelLayer: null,
            constellationDebugPanel: null,
            constellationDebugState: {},
            constellationLabelElements: new Map(),
            constellationSvgNamespace: 'http://www.w3.org/2000/svg',

            scheduleVisualRefresh: function(options = {}) {
                this._pendingVisualRefresh.fov = this._pendingVisualRefresh.fov || options.fov === true;
                this._pendingVisualRefresh.labels = this._pendingVisualRefresh.labels || options.labels === true;
                this._pendingVisualRefresh.mosaic = this._pendingVisualRefresh.mosaic || options.mosaic === true;
                this._pendingVisualRefresh.astronomical = this._pendingVisualRefresh.astronomical || options.astronomical === true;

                if (this._visualRefreshFrameId !== null) {
                    return;
                }

                this._visualRefreshFrameId = requestAnimationFrame(() => {
                    this._visualRefreshFrameId = null;
                    const pending = this._pendingVisualRefresh;
                    this._pendingVisualRefresh = {
                        fov: false,
                        labels: false,
                        mosaic: false,
                        astronomical: false
                    };

                    if (pending.fov && this.fovOverlays && this.fovOverlays.length > 0) {
                        this.updateAllFovOverlayPositions();
                    }

                    if (pending.labels && (!shouldDeferDotNetViewportSync || !this.interactionActive)) {
                        this.updateAllMarkerLabels();
                    }

                    if (this.showConstellationsActive) {
                        if (this.updateConstellationCanvasLayer) {
                            this.updateConstellationCanvasLayer().catch((error) => {
                                console.error('Error updating constellation lines:', error);
                            });
                        }

                        if (this.updateConstellationLabelPositions) {
                            this.updateConstellationLabelPositions().catch((error) => {
                                console.error('Error updating constellation label positions:', error);
                            });
                        }
                    }

                    if (pending.mosaic && this.updateMosaicPanelPositions && this.mosaicOverlays && this.mosaicOverlays.length > 0) {
                        this.updateMosaicPanelPositions();
                    }

                    if (pending.astronomical && this.updateAstronomicalOverlays) {
                        this.updateAstronomicalOverlays();
                    }
                });
            },

            setInteractionActive: function(active) {
                const nextActive = !!active;
                if (this.interactionActive === nextActive) {
                    return;
                }

                this.interactionActive = nextActive;

                if (!this.suspendMarkerLabelsDuringInteraction || !this.showMarkerLabels) {
                    return;
                }

                if (nextActive) {
                    this._markerLabelsTemporarilyHidden = true;
                    this.markers.forEach(marker => {
                        if (marker?.element) {
                            marker.element.style.display = 'none';
                        }
                    });
                    return;
                }

                this._markerLabelsTemporarilyHidden = false;
                this.scheduleVisualRefresh({ labels: true });

                if (!this._isTouchZooming && this._pendingInteractionWarmup) {
                    this._pendingInteractionWarmup = false;
                    this.scheduleResolutionWarmup('interaction');
                }
            },

            setHighQualityMode: function() {
                // SkyAtlas now always runs in the high-quality survey mode.
                this.highQualityMode = true;
            },

            isDirectHipsUrl: function(surveyId) {
                return typeof surveyId === 'string' && surveyId.startsWith('https://');
            },

            isRegistrySurveyId: function(surveyId) {
                return typeof surveyId === 'string'
                    && (surveyId.startsWith('CDS/') || surveyId.startsWith('P/'));
            },

            normalizeRegistrySurveyId: function(surveyId) {
                if (!surveyId || typeof surveyId !== 'string') {
                    return surveyId;
                }

                if (surveyId.startsWith('CDS/')) {
                    return surveyId;
                }

                if (surveyId.startsWith('P/')) {
                    return `CDS/${surveyId}`;
                }

                return surveyId;
            },

            getKnownRegistryBaseUrl: function(surveyId) {
                const normalized = this.normalizeRegistrySurveyId(surveyId);
                const knownMappings = {
                    'CDS/P/DSS2/color': 'https://alasky.cds.unistra.fr/DSS/DSSColor',
                    'CDS/P/DSS2/red': 'https://alasky.cds.unistra.fr/DSS/DSS2Merged',
                    'CDS/P/2MASS/color': 'https://alasky.cds.unistra.fr/2MASS/Color',
                    'CDS/P/PanSTARRS/DR1/color/z/zg/g': 'https://alasky.cds.unistra.fr/Pan-STARRS/DR1/color-z-zg-g',
                    'CDS/P/SDSS9/color': 'https://alasky.cds.unistra.fr/SDSS/DR9/color',
                    'CDS/P/allWISE/color': 'https://alasky.cds.unistra.fr/AllWISE/RGB-W4-W2-W1'
                };

                return knownMappings[normalized] || null;
            },

            getSurveyDisplayName: function(surveyId) {
                if (!surveyId) {
                    return 'Custom Survey';
                }

                if (surveyId.includes('simg.de/nebulae3')) {
                    const pathParts = surveyId.split('/');
                    const lastPart = pathParts[pathParts.length - 1];
                    return 'NSNS ' + lastPart.toUpperCase();
                }

                if (surveyId.includes('DSS')) {
                    return 'DSS2 Color';
                }

                if (surveyId.includes('PanSTARRS')) {
                    return 'PanSTARRS';
                }

                if (surveyId.includes('SDSS')) {
                    return 'SDSS';
                }

                if (surveyId.includes('WISE')) {
                    return 'WISE';
                }

                return 'Custom Survey';
            },

            activateAsGlobalInstance: function() {
                window.aladinInstance = this;
            },

            clearConstellationHighlight: function() {
                this.highlightedConstellationId = null;
                this.updateConstellationCanvasLayer?.().catch((error) => {
                    console.error('Error clearing constellation highlight:', error);
                });
                this.updateConstellationLabelPositions?.().catch((error) => {
                    console.error('Error clearing constellation label highlight:', error);
                });
            },

            highlightConstellation: async function(constellationId) {
                if (!this.showConstellationsActive || !constellationId) {
                    return;
                }

                this.highlightedConstellationId = String(constellationId);
                await this.updateConstellationCanvasLayer();
                await this.updateConstellationLabelPositions();
            },

            parseHipsProperties: function(text) {
                const result = {};
                if (!text) {
                    return result;
                }

                text.split(/\r?\n/).forEach(line => {
                    const trimmed = line.trim();
                    if (!trimmed || trimmed.startsWith('#')) {
                        return;
                    }

                    const separatorIndex = trimmed.indexOf('=');
                    if (separatorIndex <= 0) {
                        return;
                    }

                    const key = trimmed.slice(0, separatorIndex).trim();
                    const value = trimmed.slice(separatorIndex + 1).trim();
                    if (key) {
                        result[key] = value;
                    }
                });

                return result;
            },

            normalizeSurveyFrame: function(frame) {
                const normalized = (frame || '').toLowerCase();
                if (normalized.includes('gal')) {
                    return 'galactic';
                }

                return 'equatorial';
            },

            getPreferredTileFormat: function(availableFormats, surveyId) {
                const formats = (availableFormats || [])
                    .map(f => (f || '').trim().toLowerCase())
                    .filter(Boolean)
                    .map(f => f === 'jpeg' ? 'jpg' : f);

                if (formats.length === 0) {
                    return null;
                }

                const normalizedSurveyId = (surveyId || '').toLowerCase();
                const looksColorSurvey =
                    normalizedSurveyId.includes('color')
                    || normalizedSurveyId.includes('rgb')
                    || normalizedSurveyId.includes('dsscolor')
                    || normalizedSurveyId.includes('panstarrs')
                    || normalizedSurveyId.includes('wise')
                    || normalizedSurveyId.includes('2mass')
                    || normalizedSurveyId.includes('sdss')
                    || normalizedSurveyId.includes('ohs')
                    || normalizedSurveyId.includes('hbr');

                // FITS can provide the truest data, but for typical color surveys we prefer PNG.
                if (this.highQualityMode) {
                    if (!looksColorSurvey && formats.includes('fits')) {
                        return 'fits';
                    }

                    if (formats.includes('png')) {
                        return 'png';
                    }
                }

                if (formats.includes('jpg')) {
                    return 'jpg';
                }

                if (formats.includes('png')) {
                    return 'png';
                }

                if (formats.includes('fits')) {
                    return 'fits';
                }

                return formats[0];
            },

            loadDirectSurveyProperties: async function(surveyId) {
                const baseUrl = (surveyId || '').replace(/\/+$/, '');
                if (!baseUrl) {
                    return null;
                }

                const response = await fetch(`${baseUrl}/properties`, {
                    method: 'GET',
                    cache: 'force-cache'
                });

                if (!response.ok) {
                    throw new Error(`Failed to load HiPS properties (${response.status})`);
                }

                const text = await response.text();
                const properties = this.parseHipsProperties(text);
                const maxOrder = Number.parseInt(properties.hips_order || properties.hips_order_max || '', 10);
                const availableFormats = (properties.hips_tile_format || '')
                    .split(/[\s,;]+/)
                    .filter(Boolean);

                return {
                    baseUrl,
                    frame: this.normalizeSurveyFrame(properties.hips_frame),
                    maxOrder: Number.isFinite(maxOrder) ? maxOrder : undefined,
                    availableFormats
                };
            },

            loadRegistrySurveyInfo: async function(surveyId) {
                const normalizedId = this.normalizeRegistrySurveyId(surveyId);
                if (!normalizedId) {
                    return null;
                }

                if (this._surveyInfoCache.has(normalizedId)) {
                    return this._surveyInfoCache.get(normalizedId);
                }

                const fallbackBaseUrl = this.getKnownRegistryBaseUrl(normalizedId);

                try {
                    const response = await fetch(
                        `https://alasky.cds.unistra.fr/MocServer/query?ID=${encodeURIComponent(normalizedId)}&fmt=json&get=record`,
                        { method: 'GET', cache: 'force-cache' }
                    );

                    if (!response.ok) {
                        throw new Error(`Failed to load HiPS registry metadata (${response.status})`);
                    }

                    const payload = await response.json();
                    const record = Array.isArray(payload)
                        ? payload[0]
                        : Array.isArray(payload?.records)
                            ? payload.records[0]
                            : payload;

                    const baseUrl = (record?.hips_service_url || fallbackBaseUrl || '').replace(/\/+$/, '');
                    const maxOrder = Number.parseInt(record?.hips_order || record?.hips_order_max || '', 10);
                    const availableFormats = Array.isArray(record?.hips_tile_format)
                        ? record.hips_tile_format
                        : String(record?.hips_tile_format || '')
                            .split(/[\s,;]+/)
                            .filter(Boolean);

                    if (!baseUrl) {
                        throw new Error(`No HiPS service URL available for ${normalizedId}`);
                    }

                    const surveyInfo = {
                        creatorDid: normalizedId,
                        baseUrl,
                        frame: this.normalizeSurveyFrame(record?.hips_frame),
                        maxOrder: Number.isFinite(maxOrder) ? maxOrder : undefined,
                        availableFormats
                    };

                    this._surveyInfoCache.set(normalizedId, surveyInfo);
                    return surveyInfo;
                } catch (registryError) {
                    if (fallbackBaseUrl) {
                        try {
                            const directInfo = await this.loadDirectSurveyProperties(fallbackBaseUrl);
                            if (directInfo?.baseUrl && directInfo?.maxOrder) {
                                const surveyInfo = {
                                    creatorDid: normalizedId,
                                    baseUrl: directInfo.baseUrl,
                                    frame: directInfo.frame,
                                    maxOrder: directInfo.maxOrder,
                                    availableFormats: directInfo.availableFormats
                                };

                                this._surveyInfoCache.set(normalizedId, surveyInfo);
                                return surveyInfo;
                            }
                        } catch (fallbackError) {
                            console.warn('Failed to load fallback HiPS properties for registry survey:', fallbackError);
                        }
                    }

                    console.warn('Falling back to default registry survey loading:', registryError);
                    return null;
                }
            },

            createSurveyLayer: async function(surveyId) {
                if (!surveyId || !this.aladin) {
                    return null;
                }

                if (this.isRegistrySurveyId(surveyId) && this.aladin.createImageSurvey) {
                    const normalizedId = this.normalizeRegistrySurveyId(surveyId);

                    try {
                        const surveyInfo = await this.loadRegistrySurveyInfo(normalizedId);
                        if (surveyInfo?.baseUrl && surveyInfo?.maxOrder) {
                            const imgFormat = this.getPreferredTileFormat(surveyInfo.availableFormats, normalizedId);
                            return this.aladin.createImageSurvey(
                                normalizedId,
                                this.getSurveyDisplayName(normalizedId),
                                surveyInfo.baseUrl,
                                surveyInfo.frame,
                                surveyInfo.maxOrder,
                                imgFormat ? { imgFormat } : undefined
                            );
                        }
                    } catch (surveyInfoError) {
                        console.warn('Falling back to generic registry survey loading:', surveyInfoError);
                    }
                }

                if (this.isDirectHipsUrl(surveyId) && this.aladin.createImageSurvey) {
                    try {
                        const surveyInfo = await this.loadDirectSurveyProperties(surveyId);
                        if (surveyInfo?.maxOrder) {
                            const imgFormat = this.getPreferredTileFormat(surveyInfo.availableFormats, surveyId);
                            return this.aladin.createImageSurvey(
                                surveyId,
                                this.getSurveyDisplayName(surveyId),
                                surveyInfo.baseUrl,
                                surveyInfo.frame,
                                surveyInfo.maxOrder,
                                imgFormat ? { imgFormat } : undefined
                            );
                        }
                    } catch (surveyInfoError) {
                        console.warn('Falling back to default SkyAtlas survey loading:', surveyInfoError);
                    }
                }

                if (this.aladin.newImageSurvey) {
                    return await this.aladin.newImageSurvey(
                        this.isRegistrySurveyId(surveyId) ? this.normalizeRegistrySurveyId(surveyId) : surveyId
                    );
                }

                if (this.isDirectHipsUrl(surveyId)) {
                    return A.imageHiPS(surveyId, { name: this.getSurveyDisplayName(surveyId) });
                }

                return surveyId;
            },

            applySurveyLayer: async function(surveyId, options = {}) {
                const nextSurveyId = surveyId || this._currentSurveyId;
                if (!nextSurveyId || !this.aladin) {
                    return;
                }

                if (this.aladin.__amLegacyBuild) {
                    this._currentSurveyId = nextSurveyId;
                    this._currentSurveyLayerQualityKey = `${normalizeSurveyForBuild(nextSurveyId, { legacy: true })}|legacy`;
                    await this.setSurvey(nextSurveyId);
                    return;
                }

                const forceReload = options.forceReload === true;
                const qualityKey = `${nextSurveyId}|hq`;

                if (!forceReload && this._currentSurveyLayerQualityKey === qualityKey) {
                    return;
                }

                if (this._surveyChangeInProgress) {
                    this._currentSurveyId = nextSurveyId;
                    return;
                }

                this._surveyChangeInProgress = true;
                this._currentSurveyId = nextSurveyId;

                try {
                    const surveyLayer = await this.createSurveyLayer(nextSurveyId);
                    if (!surveyLayer) {
                        return;
                    }

                    this.aladin.setBaseImageLayer(surveyLayer);
                    this._currentSurveyLayerQualityKey = qualityKey;

                    setTimeout(() => {
                        this.resize(true).catch(() => {});
                        if (this.highQualityMode) {
                            this.queueHighQualityRefresh('force');
                        }
                    }, 80);
                } finally {
                    this._surveyChangeInProgress = false;
                }
            },

            queueHighQualityRefresh: function(reason = 'interaction') {
                if (!this.aladin) {
                    return;
                }

                if (!this.highQualityMode && reason !== 'force') {
                    return;
                }

                if (this._suppressResolutionWarmup && reason !== 'force') {
                    return;
                }

                if (reason === 'interaction' && (this.interactionActive || this._isTouchZooming)) {
                    this._pendingInteractionWarmup = true;
                    return;
                }

                this._pendingInteractionWarmup = false;
                this.scheduleResolutionWarmup(reason);
            },
            
            // Set whether to show marker labels
            setShowMarkerLabels: function(show) {
                this.showMarkerLabels = !!show;
                this.markers.forEach(marker => {
                    if (marker?.source?.data) {
                        marker.source.data.showLabel = (this.showMarkerLabels || marker.alwaysShowLabel) && marker.renderLabel !== false;
                    }
                });

                this.labelPositions = [];
                this._occupiedLabelBounds = [];
                this._renderedLabelCount = 0;
                this.aladin?.view?.requestRedraw?.();
            },

            isPointInLabel : function(x, y) {
                // Check if point is within any label bounds
                for (const label of this.labelPositions || []) {
                    if (x >= label.x && 
                        x <= label.x + label.width && 
                        y >= label.y && 
                        y <= label.y + label.height) {
                        return label.source;
                    }
                }
                return null;
            },

            addAladinButton: function (options) {
                if (!window.A || !this.aladin) {
                    console.error('Aladin not available or instance not provided');
                    return null;
                }

                const config = {
                    content: options.content || 'Button',
                    classList: options.classList || [],
                    tooltip: options.tooltip || options.title || null,
                    ariaLabel: options.ariaLabel || options.tooltip || options.title || null,
                    showTooltip: options.showTooltip !== false,
                    helpUrl: options.helpUrl || null,
                    position: options.position || 'top-right',
                    onClick: options.onClick || null,
                    buttonId: options.xButtonId || null,
                    disabled: !!options.disabled
                };

                try {
                    const action = () => {
                        if (config.disabled) {
                            return;
                        }

                        if (config.onClick && config.onClick.invokeMethodAsync) {
                            let methodName = 'CycleFovAsync';

                            switch (config.buttonId) {
                                case 'xFOVinput':
                                    methodName = 'ShowFovInputAsync';
                                    break;
                                case 'xDateTimeBtn':
                                    methodName = 'ShowDateTimePopupAsync';
                                    break;
                                case 'xStretchToggle':
                                    methodName = 'ToggleStretchAsync';
                                    break;
                                case 'xFullscreenToggle':
                                    methodName = 'ToggleFullscreenAsync';
                                    break;
                                case 'xSettingsBtn':
                                    methodName = 'ShowSettingsAsync';
                                    break;
                                case 'xAstroBinLayer':
                                    methodName = 'ShowAstroBinLayerAsync';
                                    break;
                                case 'xEquipmentFov':
                                    methodName = 'ShowEquipmentFovLayerAsync';
                                    break;
                                case 'xCurrentPosition':
                                    methodName = 'ShowCurrentPositionAsync';
                                    break;
                                case 'xAtlasBrowse':
                                    methodName = 'ShowAtlasBrowseAsync';
                                    break;
                                case 'xSearch':
                                    methodName = 'ShowSearchAsync';
                                    break;
                                case 'xFOVswitch':
                                    methodName = 'CycleFovAsync';
                                    break;
                                case 'xShowMarkers':
                                    methodName = 'ToggleShowMarkersAsync';
                                    break;
                                case 'xAutoNearby':
                                    methodName = 'ToggleAutoNearbyAsync';
                                    break;
                                case 'xTargetSearch':
                                    methodName = 'ShowTargetSearchAsync';
                                    break;
                                case 'xNearbyFilters':
                                    methodName = 'ShowNearbyFiltersAsync';
                                    break;
                                default:
                                    methodName = 'CycleFovAsync';
                                    break;
                            }

                            config.onClick.invokeMethodAsync(methodName).catch(error => {
                                console.error('Error invoking method:', methodName, error);
                            });
                        }
                    };

                    let btn;
                    if (typeof A.button === 'function' && typeof this.aladin.addUI === 'function' && !this.aladin.__amLegacyBuild) {
                        btn = A.button({
                            content: config.content,
                            classList: config.classList,
                            action
                        });
                        this.aladin.addUI(btn);
                    } else {
                        btn = createDomBackedAladinButton(this.aladin.aladinDiv, {
                            ...config,
                            action
                        });
                    }

                    if (btn?.el) {
                        attachSkyAtlasButtonTooltip(btn.el, config);
                    }
                    
                    // Store button reference for later updates if it has an ID
                    if (config.buttonId) {
                        if (!this.buttons) {
                            this.buttons = new Map();
                        }
                        this.buttons.set(config.buttonId, btn);
                                            }
                    
                    // Apply custom positioning
                    setTimeout(() => {
                        const btnElement = btn.el;
                        if (btnElement) {
                            const viewportWidth = window.innerWidth || document.documentElement?.clientWidth || 0;
                            const isMobileViewport = viewportWidth <= 768;
                            const isCompactHost = !!btnElement.closest('.sky-atlas-aladin-host-compact');
                            btnElement.style.position = 'absolute';

                            // Add some styling for better visibility with !important to override defaults
                            btnElement.style.setProperty('z-index', '5', 'important');
                            btnElement.style.setProperty('background-color', 'rgba(0, 0, 0, 0.9)', 'important'); // More solid black background
                            btnElement.style.setProperty('color', 'white', 'important');
                            btnElement.style.setProperty('border', '1px solid rgba(255, 255, 255, 0.3)', 'important'); // Subtle white border
                            btnElement.style.setProperty('border-radius', '4px', 'important');
                            btnElement.style.setProperty(
                                'padding',
                                isCompactHost ? '5px 10px' : (isMobileViewport ? '4px 9px' : '6px 12px'),
                                'important');
                            btnElement.style.setProperty(
                                'font-size',
                                isCompactHost ? '11px' : (isMobileViewport ? '10.5px' : '12px'),
                                'important');
                            btnElement.style.setProperty('cursor', 'pointer', 'important');
                            btnElement.style.setProperty('font-weight', '500', 'important'); // Slightly bolder text
                            btnElement.style.setProperty('text-shadow', '0 1px 2px rgba(0, 0, 0, 0.8)', 'important'); // Text shadow for better readability
                            btnElement.style.setProperty('box-shadow', '0 2px 4px rgba(0, 0, 0, 0.3)', 'important'); // Add subtle shadow

                            if (config.disabled) {
                                btnElement.setAttribute('aria-disabled', 'true');
                                btnElement.style.setProperty('opacity', '0.45', 'important');
                                btnElement.style.setProperty('cursor', 'not-allowed', 'important');
                                btnElement.style.setProperty('filter', 'grayscale(0.2)', 'important');
                            }

                            // Normalize icon-only buttons for consistent layout
                            const iconOnlyButtons = new Set([
                                'xAtlasBrowse',
                                'xSearch',
                                'xEquipmentFov',
                                'xTargetSearch',
                                'xAstroBinLayer',
                                'xSettingsBtn',
                                'xFullscreenToggle',
                                'xShowMarkers',
                                'xAutoNearby',
                                'xNearbyFilters'
                            ]);
                            if (iconOnlyButtons.has(config.buttonId)) {
                                const iconButtonSize = isCompactHost ? 34 : (isMobileViewport ? 32 : 40);
                                btnElement.style.setProperty('width', `${iconButtonSize}px`, 'important');
                                btnElement.style.setProperty('height', `${iconButtonSize}px`, 'important');
                                btnElement.style.setProperty('padding', '0', 'important');
                                btnElement.style.setProperty('display', 'flex', 'important');
                                btnElement.style.setProperty('align-items', 'center', 'important');
                                btnElement.style.setProperty('justify-content', 'center', 'important');
                            }

                            if (config.buttonId === 'xAtlasBrowse') {
                                btnElement.style.setProperty('color', '#fff7ed', 'important');
                                btnElement.style.setProperty(
                                    'background',
                                    'radial-gradient(circle at 28% 22%, rgba(255, 255, 255, 0.28), transparent 32%), linear-gradient(135deg, rgba(14, 165, 233, 0.92) 0%, rgba(30, 41, 59, 0.94) 48%, rgba(245, 158, 11, 0.96) 100%)',
                                    'important');
                                btnElement.style.setProperty('border', '1px solid rgba(251, 191, 36, 0.9)', 'important');
                                btnElement.style.setProperty(
                                    'box-shadow',
                                    '0 0 0 1px rgba(125, 211, 252, 0.22), 0 0 14px rgba(14, 165, 233, 0.28), 0 0 18px rgba(245, 158, 11, 0.34)',
                                    'important');
                                btnElement.style.setProperty('animation', 'sky-atlas-magic-pulse 3.2s ease-in-out infinite', 'important');
                            }

                            // Keep FOV button the same visual height as neighboring 40px controls
                            if (config.buttonId === 'xFOVinput') {
                                btnElement.style.setProperty('height', isCompactHost ? '26px' : (isMobileViewport ? '28px' : '30px'), 'important');
                                btnElement.style.setProperty('min-height', isCompactHost ? '26px' : (isMobileViewport ? '28px' : '30px'), 'important');
                                btnElement.style.setProperty('max-height', isCompactHost ? '34px' : (isMobileViewport ? '32px' : '40px'), 'important');
                                btnElement.style.setProperty('min-width', isCompactHost ? '42px' : (isMobileViewport ? '42px' : '48px'), 'important');
                                btnElement.style.setProperty('padding', '0', 'important');
                                btnElement.style.setProperty('display', 'inline-flex', 'important');
                                btnElement.style.setProperty('align-items', 'center', 'important');
                                btnElement.style.setProperty('justify-content', 'center', 'important');
                                btnElement.style.setProperty('line-height', '1', 'important');
                                btnElement.style.setProperty('box-sizing', 'border-box', 'important');
                            }

                            if (isMobileViewport && config.buttonId === 'xStretchToggle') {
                                btnElement.style.setProperty('height', '32px', 'important');
                                btnElement.style.setProperty('min-height', '32px', 'important');
                                btnElement.style.setProperty('padding', '0 10px', 'important');
                                btnElement.style.setProperty('font-size', '10.5px', 'important');
                                btnElement.style.setProperty('line-height', '1', 'important');
                            }

                            if (isCompactHost && (config.buttonId === 'xCurrentPosition' || config.buttonId === 'xDateTimeBtn')) {
                                if (config.buttonId === 'xCurrentPosition') {
                                    btnElement.style.setProperty('width', '32px', 'important');
                                    btnElement.style.setProperty('height', '29px', 'important');
                                    btnElement.style.setProperty('min-width', '32px', 'important');
                                    btnElement.style.setProperty('min-height', '29px', 'important');
                                    btnElement.style.setProperty('padding', '0', 'important');
                                } else {
                                    btnElement.style.setProperty('height', '29px', 'important');
                                    btnElement.style.setProperty('min-height', '29px', 'important');
                                    btnElement.style.setProperty('padding', '0 9px', 'important');
                                }

                                btnElement.style.setProperty('font-size', '10.5px', 'important');
                                btnElement.style.setProperty('line-height', '1', 'important');
                            }

                            // Special styling for checkbox-style buttons
                            if (config.buttonId === 'xShowMarkers' || config.buttonId === 'xAutoNearby') {
                                btnElement.style.transition = 'all 0.2s ease';
                                // Add checkbox indicator - will be set to correct value by updateCheckboxButton
                                btnElement.setAttribute('data-checkbox', 'false');
                            }

                            this.layoutCustomButtons();
                            attachSkyAtlasButtonTooltip(btnElement, config);
                        }
                    }, 200); // Increased timeout to ensure styles override defaults

                    return btn;
                } catch (error) {
                    console.error('Error creating Aladin button:', error);
                    return null;
                }
            },

            layoutCustomButtons: function() {
                if (!this.buttons) {
                    return;
                }

                const viewportWidth = window.innerWidth || document.documentElement?.clientWidth || 0;
                const useMobileLayout = isMobileLayout || viewportWidth <= 768;
                const firstButtonElement = Array.from(this.buttons.values())
                    .map(btn => btn ? btn.el : null)
                    .find(Boolean);
                const isCompactHost = !!firstButtonElement?.closest('.sky-atlas-aladin-host-compact');
                const spacing = useMobileLayout ? 44 : 48;
                const topBase = useMobileLayout ? 6 : 3;
                const topRowLeftBase = useMobileLayout ? 4 : 4;
                const lowerRowLeftBase = useMobileLayout ? 4 : 4;
                const rightBase = useMobileLayout ? 4 : 10;
                const bottomBase = useMobileLayout
                    ? (isCompactHost ? 20 : 21)
                    : (isCompactHost ? 42 : 38);

                const place = (buttonId, { top, left, right, bottom } = {}) => {
                    if (!this.buttons.has(buttonId)) {
                        return;
                    }

                    const btn = this.buttons.get(buttonId);
                    const btnElement = btn ? btn.el : null;
                    if (!btnElement) {
                        return;
                    }

                    btnElement.style.top = top ?? 'auto';
                    btnElement.style.left = left ?? 'auto';
                    btnElement.style.right = right ?? 'auto';
                    btnElement.style.bottom = bottom ?? 'auto';
                };

                const placeRow = (buttonIds, { top, left, bottom, gap = 6 } = {}) => {
                    let currentLeft = left;
                    buttonIds.forEach(buttonId => {
                        if (!this.buttons.has(buttonId) || currentLeft == null) {
                            return;
                        }

                        place(buttonId, {
                            top,
                            left: `${currentLeft}px`,
                            bottom
                        });

                        const btn = this.buttons.get(buttonId);
                        const btnElement = btn ? btn.el : null;
                        const buttonWidth = btnElement
                            ? Math.ceil(btnElement.getBoundingClientRect().width || btnElement.offsetWidth || 0)
                            : 0;
                        currentLeft += buttonWidth + gap;
                    });
                };

                if (isCompactHost) {
                    const compactGap = 6;
                    const compactTop = 4;
                    const compactSecondRowTop = 44;
                    const compactRight = 4;

                    placeRow(['xStretchToggle', 'xAtlasBrowse', 'xSearch', 'xEquipmentFov', 'xTargetSearch'], {
                        top: `${compactTop}px`,
                        left: 4,
                        gap: compactGap
                    });

                    placeRow(['xAstroBinLayer', 'xShowMarkers', 'xAutoNearby', 'xNearbyFilters'], {
                        top: `${compactSecondRowTop}px`,
                        left: 4,
                        gap: compactGap
                    });

                    place('xFullscreenToggle', { top: `${compactTop}px`, right: `${compactRight}px` });
                    place('xSettingsBtn', { top: `${compactSecondRowTop}px`, right: `${compactRight}px` });

                    placeRow(['xCurrentPosition', 'xFOVinput', 'xDateTimeBtn'], {
                        bottom: `${bottomBase}px`,
                        left: 4,
                        gap: compactGap
                    });

                    return;
                }

                if (useMobileLayout) {
                    place('xStretchToggle', { top: `${topBase}px`, left: `${topRowLeftBase}px` });
                    place('xAtlasBrowse', { top: `${topBase}px`, left: `${topRowLeftBase + spacing + 16}px` });
                    place('xSearch', { top: `${topBase}px`, left: `${topRowLeftBase + spacing * 2 + 16}px` });
                    place('xEquipmentFov', { top: `${topBase}px`, left: `${topRowLeftBase + spacing * 3 + 16}px` });
                    place('xTargetSearch', { top: `${topBase}px`, left: `${topRowLeftBase + spacing * 4 + 16}px` });
                    place('xAstroBinLayer', { top: `${topBase + spacing}px`, left: `${lowerRowLeftBase}px` });
                    place('xShowMarkers', { top: `${topBase + spacing}px`, left: `${lowerRowLeftBase + spacing}px` });
                    place('xAutoNearby', { top: `${topBase + spacing}px`, left: `${lowerRowLeftBase + spacing * 2}px` });
                    place('xNearbyFilters', { top: `${topBase + spacing}px`, left: `${lowerRowLeftBase + spacing * 3}px` });
                    place('xSettingsBtn', { top: `${topBase}px`, right: `${rightBase + spacing}px` });
                    place('xFullscreenToggle', { top: `${topBase}px`, right: `${rightBase}px` });
                    place('xCurrentPosition', { bottom: `${bottomBase}px`, left: `${lowerRowLeftBase}px` });
                    place('xFOVinput', { bottom: `${bottomBase}px`, left: `${lowerRowLeftBase + spacing -2}px` });
                    place('xDateTimeBtn', { bottom: `${bottomBase}px`, left: `${lowerRowLeftBase + spacing * 2}px` });
                } else {
                    place('xStretchToggle', { top: '3px', left: '4px' });
                    place('xAtlasBrowse', { top: '3px', left: '72px' });
                    place('xSearch', { top: '3px', left: '120px' });
                    place('xEquipmentFov', { top: '3px', left: '168px' });
                    place('xTargetSearch', { top: '3px', left: '216px' });
                    place('xAstroBinLayer', { top: '3px', left: '264px' });
                    place('xShowMarkers', { top: '52px', left: '72px' });
                    place('xAutoNearby', { top: '52px', left: '120px' });
                    place('xNearbyFilters', { top: '52px', left: '168px' });
                    place('xSettingsBtn', { top: '3px', right: '52px' });
                    place('xFullscreenToggle', { top: '3px', right: '10px' });
                    place('xCurrentPosition', { bottom: `${bottomBase}px`, left: '4px' });
                    place('xFOVinput', { bottom: `${bottomBase}px`, left: '52px' });
                    place('xDateTimeBtn', { bottom: `${bottomBase}px`, left: '104px' });
                }
            },
            
            updateAladinButtonText: function(selector, newText) {
                try {
                    const btnElement = document.querySelector(selector);
                    if (btnElement) {
                        btnElement.innerHTML = newText;
                    }
                } catch (error) {
                    console.error('Error updating button text:', error);
                }
            },
            
            updateButtonBadge: function(buttonId, count) {
                try {
                    if (!this.buttons || !this.buttons.has(buttonId)) {
                        return;
                    }
                    
                    const btn = this.buttons.get(buttonId);
                    const btnElement = btn.el;
                    
                    if (btnElement) {
                        const existingBadge = btnElement.querySelector('.filter-badge');
                        if (existingBadge) {
                            existingBadge.remove();
                        }
                        
                        if (count > 0) {
                            const badge = document.createElement('span');
                            badge.className = 'filter-badge';
                            badge.textContent = count.toString();
                            badge.style.cssText = 'position: absolute; top: -6px; right: -6px; background: #3b82f6; color: white; font-size: 10px; font-weight: bold; padding: 2px 5px; border-radius: 10px; min-width: 14px; text-align: center; pointer-events: none;';
                            btnElement.appendChild(badge);
                        }
                    }
                } catch (error) {
                    console.error('Error updating button badge:', error);
                }
            },
            
            updateCheckboxButton: function(buttonId, isChecked) {
                try {
                    if (!this.buttons || !this.buttons.has(buttonId)) {
                        console.warn(`Button ${buttonId} not found`);
                        return;
                    }
                    
                    const btn = this.buttons.get(buttonId);
                    const btnElement = btn.el;
                    
                    if (btnElement) {
                        btnElement.setAttribute('data-checkbox', isChecked.toString());
                        
                        if (isChecked) {
                            btnElement.style.setProperty('background-color', 'rgba(34, 197, 94, 0.8)', 'important');
                            btnElement.style.setProperty('border-color', '#22c55e', 'important');
                            btnElement.style.setProperty('color', 'white', 'important');
                        } else {
                            btnElement.style.setProperty('background-color', 'rgba(0, 0, 0, 0.9)', 'important');
                            btnElement.style.setProperty('border-color', 'rgba(255, 255, 255, 0.3)', 'important');
                            btnElement.style.setProperty('color', 'white', 'important');
                        }
                    }
                } catch (error) {
                    console.error('Error updating checkbox button:', error);
                }
            },
            
            // Toggle marker labels visibility
            toggleMarkerLabels: function() {
                this.setShowMarkerLabels(!this.showMarkerLabels);
            },
            
            // Center on RA/Dec
            gotoRaDec: async function(ra, dec) {
                this.aladin.gotoRaDec(ra, dec);
                return Promise.resolve();
            },

            // Set FOV in degrees
            setFov: async function(fov) {
                this.aladin.setFov(fov);
                // Manually trigger FOV overlay updates after FOV change
                setTimeout(() => {
                    this.scheduleVisualRefresh({
                        fov: true,
                        mosaic: true,
                        astronomical: true
                    });
                }, 100); // Small delay to ensure FOV change is processed
                this.resize(); // Call resize method after setting FOV
                return Promise.resolve();
            },

            scheduleResolutionWarmup: function(reason = 'resize') {
                try {
                    const isMobileOrTouch = isMobileLayout
                        || (() => {
                            try {
                                return window.matchMedia('(max-width: 768px)').matches
                                    || window.matchMedia('(pointer: coarse)').matches
                                    || (navigator.maxTouchPoints || 0) > 0;
                            } catch {
                                return false;
                            }
                        })();

                    if (!isMobileOrTouch || !this.aladin) {
                        return;
                    }

                    if (!this.highQualityMode && reason !== 'force') {
                        return;
                    }

                    // Passive resize/zoom changes were causing visible zoom flashing on mobile.
                    // Only run the aggressive FOV warmup for explicit/forced cases.
                    if (reason !== 'force' && reason !== 'init') {
                        return;
                    }

                    if (reason === 'interaction' && (this.interactionActive || this._isTouchZooming)) {
                        this._pendingInteractionWarmup = true;
                        return;
                    }

                    const host = this.aladin.aladinDiv;
                    const rect = host?.getBoundingClientRect?.();
                    const width = Math.round(rect?.width || 0);
                    const height = Math.round(rect?.height || 0);
                    const currentFov = this.aladin.getFov?.()?.[0];

                    if (width <= 0 || height <= 0 || !Number.isFinite(currentFov) || currentFov <= 0) {
                        return;
                    }

                    const refreshKey = `${width}x${height}@${currentFov.toFixed(5)}`;
                    if (this._lastResolutionWarmupKey === refreshKey && reason !== 'force') {
                        return;
                    }

                    this._lastResolutionWarmupKey = refreshKey;

                    if (this._resolutionWarmupTimer) {
                        clearTimeout(this._resolutionWarmupTimer);
                    }

                    this._resolutionWarmupTimer = setTimeout(() => {
                        if (this._isRunningResolutionWarmup || !this.aladin) {
                            return;
                        }

                        if (this._isTouchZooming || this.interactionActive) {
                            this._pendingInteractionWarmup = true;
                            return;
                        }

                        const center = this.aladin.getRaDec?.();
                        const fov = this.aladin.getFov?.();
                        if (!center || center.length < 2 || !fov || !Number.isFinite(fov[0])) {
                            return;
                        }

                        this._isRunningResolutionWarmup = true;
                        this._suppressResolutionWarmup = true;

                        try {
                            this.aladin.gotoRaDec(center[0], center[1]);
                            const targetFov = fov[0];
                            const warmupSequence = [];
                            const aggressiveFactor =
                                targetFov > 20 ? 0.16 :
                                targetFov > 12 ? 0.22 :
                                targetFov > 6 ? 0.30 :
                                targetFov > 3 ? 0.40 :
                                targetFov > 1.5 ? 0.54 :
                                0.72;

                            const aggressiveWarmupFov = Math.max(targetFov * aggressiveFactor, 0.0001);
                            const followupWarmupFov = Math.max(targetFov * 0.78, 0.0001);

                            if (Math.abs(aggressiveWarmupFov - targetFov) > 0.00001) {
                                warmupSequence.push(aggressiveWarmupFov);
                            }

                            if (Math.abs(followupWarmupFov - targetFov) > 0.00001 &&
                                Math.abs(followupWarmupFov - aggressiveWarmupFov) > 0.00001) {
                                warmupSequence.push(followupWarmupFov);
                            }

                            const restoreTargetView = () => {
                                try {
                                    this.aladin?.gotoRaDec?.(center[0], center[1]);
                                    this.aladin?.setFov?.(targetFov);

                                    requestAnimationFrame(() => {
                                        if (this.aladin?.view?.requestRedraw) {
                                            this.aladin.view.requestRedraw();
                                        } else if (this.aladin?.view?.redraw) {
                                            this.aladin.view.redraw();
                                        }
                                    });
                                } catch (restoreError) {
                                    console.error('Error restoring SkyAtlas warmup FOV:', restoreError);
                                }
                            };

                            const runWarmupStep = (index) => {
                                if (!this.aladin) {
                                    return;
                                }

                                if (index >= warmupSequence.length) {
                                    restoreTargetView();
                                    return;
                                }

                                try {
                                    this.aladin.gotoRaDec(center[0], center[1]);
                                    this.aladin.setFov(warmupSequence[index]);

                                    requestAnimationFrame(() => {
                                        requestAnimationFrame(() => {
                                            runWarmupStep(index + 1);
                                        });
                                    });
                                } catch (stepError) {
                                    console.error('Error applying SkyAtlas warmup step:', stepError);
                                    restoreTargetView();
                                }
                            };

                            if (warmupSequence.length > 0) {
                                runWarmupStep(0);
                            } else {
                                restoreTargetView();
                            }
                        } catch (warmupError) {
                            console.error('Error warming up SkyAtlas resolution:', warmupError);
                        } finally {
                            setTimeout(() => {
                                this._isRunningResolutionWarmup = false;
                                this._suppressResolutionWarmup = false;
                            }, 450);
                        }
                    }, reason === 'init' ? 320 : reason === 'interaction' ? 420 : 220);
                } catch (error) {
                    console.error('Error scheduling SkyAtlas resolution warmup:', error);
                }
            },

            // Force viewer to recompute layout after host size changes
            resize: async function(force) {
                try {
                    const host = this.aladin?.aladinDiv;
                    if (host) {

                        host.style.width = '100%';
                        host.style.height = '100%';

                        const rect = host.getBoundingClientRect();
                        const width = Math.round(rect.width);
                        const height = Math.round(rect.height);

                        if (debugEnabled) {
                            debugLog('resize:start', {
                                force: !!force,
                                width,
                                height,
                                lastWidth: this._lastHostWidth,
                                lastHeight: this._lastHostHeight,
                                hostClientWidth: host.clientWidth,
                                hostClientHeight: host.clientHeight
                            });
                        }
                        pushTrace('resize:start', {
                            force: !!force,
                            width,
                            height,
                            hostClientWidth: host.clientWidth,
                            hostClientHeight: host.clientHeight,
                            lastWidth: this._lastHostWidth,
                            lastHeight: this._lastHostHeight
                        });

                        if (width === 0 || height === 0) {
                            this._zeroSizeCount = (this._zeroSizeCount || 0) + 1;
                            debugLog('resize:zero-dimension-host', {
                                force: !!force,
                                width,
                                height
                            });

                            pushTrace('resize:zero-dimension-host', {
                                force: !!force,
                                width,
                                height,
                                zeroSizeCount: this._zeroSizeCount
                            });

                            if (this._zeroSizeCount >= 2) {
                                setDiagBadge(`SkyAtlas host is ${width}x${height} (zero-size #${this._zeroSizeCount})`, true);
                            }

                            if (!this._zeroSizeRecoveryScheduled) {
                                this._zeroSizeRecoveryScheduled = true;
                                [120, 350, 800, 1500].forEach((delayMs) => {
                                    setTimeout(() => {
                                        this.resize(true).catch(() => {});
                                    }, delayMs);
                                });
                                setTimeout(() => {
                                    this._zeroSizeRecoveryScheduled = false;
                                }, 1800);
                            }
                        }

                        if (!force && width > 0 && height > 0 && width === this._lastHostWidth && height === this._lastHostHeight) {
                            return Promise.resolve();
                        }

                        if (width > 0 && height > 0 && this._zeroSizeCount > 0) {
                            setDiagBadge(`SkyAtlas recovered: ${width}x${height}`);
                        }

                        this._lastHostWidth = width;
                        this._lastHostHeight = height;

                        const pixelRatio = Math.max(window.devicePixelRatio || 1, 1);
                        const internals = host.querySelectorAll('.aladin-container, .aladin-imageCanvas, canvas');
                        internals.forEach((el) => {
                            el.style.width = '100%';
                            el.style.height = '100%';

                            if (el.tagName === 'CANVAS' && width > 0 && height > 0) {
                                el.style.width = `${width}px`;
                                el.style.height = `${height}px`;

                                const targetWidth = Math.max(1, Math.round(width * pixelRatio));
                                const targetHeight = Math.max(1, Math.round(height * pixelRatio));
                                if (Math.abs(el.width - targetWidth) > 1 || Math.abs(el.height - targetHeight) > 1) {
                                    el.width = targetWidth;
                                    el.height = targetHeight;
                                }
                            }
                        });

                        if (debugEnabled) {
                            const firstCanvas = host.querySelector('canvas');
                            debugLog('resize:after-internal-sync', {
                                force: !!force,
                                width,
                                height,
                                pixelRatio,
                                firstCanvasCssWidth: firstCanvas ? Math.round(firstCanvas.getBoundingClientRect().width) : null,
                                firstCanvasCssHeight: firstCanvas ? Math.round(firstCanvas.getBoundingClientRect().height) : null,
                                firstCanvasPixelWidth: firstCanvas ? firstCanvas.width : null,
                                firstCanvasPixelHeight: firstCanvas ? firstCanvas.height : null
                            });
                        }
                        const firstCanvas = host.querySelector('canvas');
                        pushTrace('resize:after-internal-sync', {
                            force: !!force,
                            width,
                            height,
                            pixelRatio,
                            firstCanvasCssWidth: firstCanvas ? Math.round(firstCanvas.getBoundingClientRect().width) : null,
                            firstCanvasCssHeight: firstCanvas ? Math.round(firstCanvas.getBoundingClientRect().height) : null,
                            firstCanvasPixelWidth: firstCanvas ? firstCanvas.width : null,
                            firstCanvasPixelHeight: firstCanvas ? firstCanvas.height : null
                        });

                    }

                    if (this.aladin?.view?.fixLayoutDimensions) {
                        this.aladin.view.fixLayoutDimensions();
                    }

                    if (this.aladin?.view?.requestRedraw) {
                        this.aladin.view.requestRedraw();
                    } else if (this.aladin?.view?.redraw) {
                        this.aladin.view.redraw();
                    }
                    if (this.updateConstellationCanvasLayer) {
                        this.updateConstellationCanvasLayer().catch((error) => {
                            console.error('Error updating constellation canvas layer after resize:', error);
                        });
                    }
                    if (this.updateConstellationLabelPositions) {
                        this.updateConstellationLabelPositions().catch((error) => {
                            console.error('Error updating constellation label positions after resize:', error);
                        });
                    }
                    this.scheduleVisualRefresh({
                        fov: true,
                        labels: true,
                        mosaic: true,
                        astronomical: true
                    });
                    if (force && !this._isRunningResolutionWarmup) {
                        this.scheduleResolutionWarmup(force ? 'force' : 'resize');
                    }
                    this.layoutCustomButtons();
                } catch (error) {
                    console.error('Error resizing Aladin viewer:', error);
                }

                return Promise.resolve();
            },

            // Set colormap with stretch options
            setColormap: async function(colormap, options) {
                try {
                    this.aladin.getBaseImageLayer().setColormap(colormap, options);
                    return Promise.resolve();
                } catch (error) {
                    console.error('Error setting colormap:', error);
                    return Promise.reject(error);
                }
            },
            
            // Get current position [ra, dec]
            getPosition: function() {
                const pos = this.aladin.getRaDec();
                return pos && pos.length >= 2 ? [pos[0], pos[1]] : null;
            },
            
            // Get current FOV in degrees
            getFov: function() {
                const fov = this.aladin.getFov();
                return fov && fov.length > 0 ? fov[0] : 1.0; // Return width as FOV
            },

            getViewState: function() {
                const pos = this.aladin.getRaDec();
                const fov = this.aladin.getFov();
                return {
                    ra: pos && pos.length >= 2 ? pos[0] : 0,
                    dec: pos && pos.length >= 2 ? pos[1] : 0,
                    fov: fov && fov.length > 0 ? fov[0] : 1.0
                };
            },
            
            // Clear all custom Aladin buttons
            clearAladinButtons: function() {
                try {
                    // Remove all custom buttons with x-aladin-btn class
                    const customButtons = this.aladin.aladinDiv.querySelectorAll('.x-aladin-btn');
                    customButtons.forEach(button => {
                        if (button.parentNode) {
                            button.parentNode.removeChild(button);
                        }
                    });
                } catch (error) {
                    console.error('Error clearing custom Aladin buttons:', error);
                }
            },


            // Add this to your Aladin initialization code
            isPointInLabel: function(x, y, bounds) {
                return x >= bounds.x && 
                       x <= bounds.x + bounds.width && 
                       y >= bounds.y && 
                       y <= bounds.y + bounds.height;
            },

            getMousePos: function (canvas, evt) {
                const rect = canvas.getBoundingClientRect();
                return {
                    x: evt.clientX - rect.left,
                    y: evt.clientY - rect.top
                };
            },

            createConstellationLabelDrawFunction: function() {
                const boxesOverlap = (a, b) => {
                    if (!a || !b) {
                        return false;
                    }

                    return a.x < b.x + b.width
                        && a.x + a.width > b.x
                        && a.y < b.y + b.height
                        && a.y + a.height > b.y;
                };

                const getOverlapRatio = (a, b) => {
                    if (!boxesOverlap(a, b)) {
                        return 0;
                    }

                    const overlapWidth = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x));
                    const overlapHeight = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
                    const overlapArea = overlapWidth * overlapHeight;
                    const minArea = Math.max(1, Math.min(a.width * a.height, b.width * b.height));
                    return overlapArea / minArea;
                };

                const isInsideCanvas = (bounds, canvas) => {
                    if (!bounds || !canvas) {
                        return false;
                    }

                    return bounds.x >= 2
                        && bounds.y >= 2
                        && (bounds.x + bounds.width) <= (canvas.width - 2)
                        && (bounds.y + bounds.height) <= (canvas.height - 2);
                };

                const getDensity = (fov) => {
                    if (!Number.isFinite(fov) || fov > 360) {
                        return null;
                    }

                    if (fov > 240) {
                        return { fontSize: 8.25, maxLabels: 12, maxRank: 2, useShortLabel: true, maxOverlapRatio: 0.08, drawBackdrop: false };
                    }

                    if (fov > 170) {
                        return { fontSize: 8.75, maxLabels: 16, maxRank: 2, useShortLabel: true, maxOverlapRatio: 0.1, drawBackdrop: false };
                    }

                    if (fov > 110) {
                        return { fontSize: 9, maxLabels: 20, maxRank: 2, useShortLabel: true, maxOverlapRatio: 0.12, drawBackdrop: false };
                    }

                    if (fov > 70) {
                        return { fontSize: 9.5, maxLabels: 26, maxRank: 3, useShortLabel: true, maxOverlapRatio: 0.15, drawBackdrop: false };
                    }

                    if (fov > 30) {
                        return { fontSize: 10, maxLabels: 32, maxRank: 3, useShortLabel: true, maxOverlapRatio: 0.18, drawBackdrop: false };
                    }

                    if (fov > 12) {
                        return { fontSize: 10.5, maxLabels: 36, maxRank: 3, useShortLabel: true, maxOverlapRatio: 0.2, drawBackdrop: true };
                    }

                    if (fov > 4) {
                        return { fontSize: 11, maxLabels: 40, maxRank: 3, useShortLabel: true, maxOverlapRatio: 0.22, drawBackdrop: true };
                    }

                    return { fontSize: 11.25, maxLabels: 46, maxRank: 3, useShortLabel: false, maxOverlapRatio: 0.26, drawBackdrop: true };
                };

                return function(source, canvasCtx, viewParams) {
                    if (!this._constellationLabelRenderCycleActive) {
                        this._constellationLabelRenderCycleActive = true;
                        this._constellationLabelBounds = [];
                        this._constellationLabelCount = 0;

                        queueMicrotask(() => {
                            this._constellationLabelRenderCycleActive = false;
                        });
                    }

                    const fov = Math.max(viewParams.fov[0], viewParams.fov[1]);
                    const density = getDensity(fov);
                    if (!density) {
                        return;
                    }

                    const rank = Number.isFinite(source?.data?.rank) ? Number(source.data.rank) : 99;
                    if (rank > density.maxRank) {
                        return;
                    }

                    if ((this._constellationLabelCount || 0) >= density.maxLabels) {
                        return;
                    }

                    const labelText = density.useShortLabel
                        ? (source?.data?.shortLabel || source?.data?.longLabel || '')
                        : (source?.data?.longLabel || source?.data?.shortLabel || '');

                    if (!labelText) {
                        return;
                    }

                    const centerX = source.x;
                    const centerY = source.y;
                    const isHighlighted = source?.data?.constellationId
                        && source.data.constellationId === this.highlightedConstellationId;

                    if (!Number.isFinite(centerX) || !Number.isFinite(centerY)) {
                        return;
                    }

                    canvasCtx.save();
                    canvasCtx.font = `600 ${density.fontSize}px Arial`;
                    canvasCtx.textAlign = 'center';
                    canvasCtx.textBaseline = 'middle';

                    const metrics = canvasCtx.measureText(labelText);
                    const textWidth = metrics.width;
                    const textHeight = Math.ceil(density.fontSize + 2);
                    const bounds = {
                        x: centerX - (textWidth / 2) - 3,
                        y: centerY - (textHeight / 2) - 1,
                        width: textWidth + 6,
                        height: textHeight + 2
                    };

                    if (!isInsideCanvas(bounds, canvasCtx.canvas) ||
                        (this._constellationLabelBounds || []).some(existing =>
                            getOverlapRatio(existing, bounds) > density.maxOverlapRatio)) {
                        canvasCtx.restore();
                        return;
                    }

                    if (density.drawBackdrop !== false) {
                        canvasCtx.fillStyle = isHighlighted ? 'rgba(71, 85, 105, 0.52)' : 'rgba(2, 6, 23, 0.20)';
                        canvasCtx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
                    }
                    canvasCtx.fillStyle = isHighlighted ? 'rgba(248, 250, 252, 0.98)' : 'rgba(203, 213, 225, 0.9)';
                    canvasCtx.shadowColor = 'rgba(0, 0, 0, 0.92)';
                    canvasCtx.shadowBlur = isHighlighted ? 8 : 5;
                    canvasCtx.fillText(labelText, centerX, centerY);
                    canvasCtx.restore();

                    this._constellationLabelBounds.push(bounds);
                    this._constellationLabelCount = (this._constellationLabelCount || 0) + 1;
                }.bind(this);
            },

            createDSODrawFunction: function() {
                // Store label positions for hit testing
                this.labelPositions = [];
                this._occupiedLabelBounds = [];
                this._renderedLabelCount = 0;
                this._markerHitRegions = [];

                const boxesOverlap = (a, b) => {
                    if (!a || !b) {
                        return false;
                    }

                    return a.x < b.x + b.width
                        && a.x + a.width > b.x
                        && a.y < b.y + b.height
                        && a.y + a.height > b.y;
                };

                const isInsideCanvas = (bounds, canvas) => {
                    if (!bounds || !canvas) {
                        return false;
                    }

                    return bounds.x >= 2
                        && bounds.y >= 2
                        && (bounds.x + bounds.width) <= (canvas.width - 2)
                        && (bounds.y + bounds.height) <= (canvas.height - 2);
                };

                const getMarkerLabelPriority = (data) => {
                    if (!data) {
                        return 0;
                    }

                    let priority = Number.isFinite(data.popularity) ? Number(data.popularity) : 70;
                    const objId = typeof data.objId === 'string' ? data.objId : '';

                    if (objId.startsWith('scheduled:')) {
                        priority += 2200;
                    } else if (objId.startsWith('target:')) {
                        priority += 2000;
                    } else if (objId.startsWith('community:')) {
                        priority += 1800;
                    }

                    if (data.alwaysShowLabel === true) {
                        priority += 1200;
                    }

                    return priority;
                };

                const getLabelDensityConfig = (fov, canvasWidth, canvasHeight, data) => {
                    const sticky = data?.alwaysShowLabel === true;

                    if (!sticky && fov > 260) {
                        return null;
                    }

                    let fontSize = 11.75;
                    let maxLabels = 420;

                    if (fov > 160) {
                        fontSize = 8.25;
                        maxLabels = 24;
                    } else if (fov > 120) {
                        fontSize = 8.5;
                        maxLabels = 32;
                    } else if (fov > 75) {
                        fontSize = 8.75;
                        maxLabels = 48;
                    } else if (fov > 45) {
                        fontSize = 9;
                        maxLabels = 72;
                    } else if (fov > 25) {
                        fontSize = 9.5;
                        maxLabels = 96;
                    } else if (fov > 12) {
                        fontSize = 10;
                        maxLabels = 120;
                    } else if (fov > 6) {
                        fontSize = 10.5;
                        maxLabels = 160;
                    } else if (fov > 3) {
                        fontSize = 11;
                        maxLabels = 220;
                    } else if (fov > 1.5) {
                        fontSize = 11.25;
                        maxLabels = 280;
                    }

                    const areaFactor = Math.max(1.0, Math.min(2.25, (canvasWidth * canvasHeight) / (1280 * 720)));
                    return {
                        fontSize,
                        typeFontSize: Math.max(10, fontSize - 2),
                        maxLabels: sticky ? Math.max(maxLabels, 999) : Math.max(8, Math.round(maxLabels * areaFactor)),
                        markerGap: Math.max(4, Math.round(fontSize * 0.34)),
                        backgroundPadX: Math.max(1, Math.round(fontSize * 0.14)),
                        backgroundPadY: 1,
                        farPlacementGap: Math.max(7, Math.round(fontSize * 0.82)),
                        showType: fov < 0.6,
                        maxOverlapRatio: fov > 75 ? 0.16 : (fov > 25 ? 0.22 : 0.28),
                        drawBackdrop: fov <= 45
                    };
                };

                const drawRoundedBackdrop = (canvasCtx, x, y, width, height, fillStyle) => {
                    const radius = 5;
                    canvasCtx.beginPath();
                    canvasCtx.moveTo(x + radius, y);
                    canvasCtx.lineTo(x + width - radius, y);
                    canvasCtx.quadraticCurveTo(x + width, y, x + width, y + radius);
                    canvasCtx.lineTo(x + width, y + height - radius);
                    canvasCtx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
                    canvasCtx.lineTo(x + radius, y + height);
                    canvasCtx.quadraticCurveTo(x, y + height, x, y + height - radius);
                    canvasCtx.lineTo(x, y + radius);
                    canvasCtx.quadraticCurveTo(x, y, x + radius, y);
                    canvasCtx.closePath();
                    canvasCtx.fillStyle = fillStyle;
                    canvasCtx.fill();
                };

                const ensureMarkerCanvasHandlers = (canvas) => {
                    if (!canvas || canvas._amMarkerInteractionHandlersAttached) {
                        return;
                    }

                    canvas._amMarkerInteractionHandlersAttached = true;

                    const getEventCanvasPoint = (event) => {
                        const point = event?.changedTouches?.[0]
                            ?? event?.touches?.[0]
                            ?? event
                            ?? null;
                        const clientX = point?.clientX;
                        const clientY = point?.clientY;

                        if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) {
                            return null;
                        }

                        const rect = this.aladin?.aladinDiv?.getBoundingClientRect?.()
                            || canvas.getBoundingClientRect();
                        return {
                            x: clientX - rect.left,
                            y: clientY - rect.top
                        };
                    };

                    const findMarkerRegionAtPoint = (point) => {
                        if (!point || !this._markerHitRegions || this._markerHitRegions.length === 0) {
                            return null;
                        }

                        let bestRegion = null;
                        let bestDistanceSquared = Number.POSITIVE_INFINITY;

                        for (const region of this._markerHitRegions) {
                            if (!region?.source?.data) {
                                continue;
                            }

                            const insideBounds = point.x >= region.x
                                && point.x <= region.x + region.width
                                && point.y >= region.y
                                && point.y <= region.y + region.height;

                            if (!insideBounds) {
                                continue;
                            }

                            const deltaX = point.x - region.centerX;
                            const deltaY = point.y - region.centerY;
                            const distanceSquared = (deltaX * deltaX) + (deltaY * deltaY);
                            if (distanceSquared < bestDistanceSquared) {
                                bestDistanceSquared = distanceSquared;
                                bestRegion = region;
                            }
                        }

                        return bestRegion;
                    };

                    const findLabelBoundsAtPoint = (point) => {
                        if (!point || !Array.isArray(this.labelPositions) || this.labelPositions.length === 0) {
                            return null;
                        }

                        for (const bounds of this.labelPositions) {
                            if (!bounds?.source?.data) {
                                continue;
                            }

                            const insideBounds = point.x >= bounds.x
                                && point.x <= bounds.x + bounds.width
                                && point.y >= bounds.y
                                && point.y <= bounds.y + bounds.height;

                            if (insideBounds) {
                                return bounds;
                            }
                        }

                        return null;
                    };

                    const resolveInteractiveTarget = (point) => {
                        const labelBounds = findLabelBoundsAtPoint(point);
                        if (labelBounds) {
                            return {
                                kind: 'label',
                                source: labelBounds.source
                            };
                        }

                        const markerRegion = findMarkerRegionAtPoint(point);
                        if (markerRegion) {
                            return {
                                kind: 'marker',
                                source: markerRegion.source
                            };
                        }

                        return null;
                    };

                    const getTargetKey = (target) => {
                        if (!target?.source?.data?.objId) {
                            return null;
                        }

                        return String(target.source.data.objId);
                    };

                    const captureViewState = () => {
                        const center = this.aladin?.getRaDec?.();
                        const fov = this.aladin?.getFov?.();

                        return {
                            ra: Array.isArray(center) && center.length >= 2 && Number.isFinite(center[0]) ? center[0] : null,
                            dec: Array.isArray(center) && center.length >= 2 && Number.isFinite(center[1]) ? center[1] : null,
                            fov: Array.isArray(fov) && fov.length >= 1 && Number.isFinite(fov[0]) ? fov[0] : null
                        };
                    };

                    const getWrappedRaDelta = (currentRa, startRa) => {
                        if (!Number.isFinite(currentRa) || !Number.isFinite(startRa)) {
                            return 0;
                        }

                        let delta = currentRa - startRa;
                        while (delta > 180) {
                            delta -= 360;
                        }
                        while (delta < -180) {
                            delta += 360;
                        }

                        return delta;
                    };

                    const hasViewChangedSincePointerDown = () => {
                        const startState = canvas.__amActivePointerViewState;
                        if (!startState) {
                            return false;
                        }

                        const currentState = captureViewState();
                        const raDelta = Math.abs(getWrappedRaDelta(currentState.ra, startState.ra));
                        const decDelta = Math.abs((currentState.dec ?? 0) - (startState.dec ?? 0));
                        const fovDelta = Math.abs((currentState.fov ?? 0) - (startState.fov ?? 0));

                        return raDelta > 0.01 || decDelta > 0.01 || fovDelta > 0.0005;
                    };

                    const activateCanvasTarget = (event, target, sourceKind = 'unknown') => {
                        if (!target?.source?.data) {
                            return false;
                        }

                        event?.preventDefault?.();
                        event?.stopPropagation?.();
                        event?.stopImmediatePropagation?.();
                        this.handleMarkerTextInteraction(target.source.data, event);
                        return true;
                    };

                    const clearActivePointerTracking = () => {
                        canvas.__amActiveInteractiveTargetKey = null;
                        canvas.__amActivePointerStartX = null;
                        canvas.__amActivePointerStartY = null;
                        canvas.__amActivePointerMoved = false;
                        canvas.__amActivePointerViewState = null;
                    };

                    const updatePointerMovement = (point) => {
                        if (!point ||
                            !Number.isFinite(canvas.__amActivePointerStartX) ||
                            !Number.isFinite(canvas.__amActivePointerStartY)) {
                            return;
                        }

                        const deltaX = point.x - canvas.__amActivePointerStartX;
                        const deltaY = point.y - canvas.__amActivePointerStartY;
                        if (((deltaX * deltaX) + (deltaY * deltaY)) >= 36) {
                            canvas.__amActivePointerMoved = true;
                        }
                    };

                    canvas.addEventListener('pointerdown', (event) => {
                        const point = getEventCanvasPoint(event);
                        const target = resolveInteractiveTarget(point);
                        canvas.__amActiveInteractiveTargetKey = getTargetKey(target);
                        canvas.__amActivePointerStartX = point?.x ?? null;
                        canvas.__amActivePointerStartY = point?.y ?? null;
                        canvas.__amActivePointerMoved = false;
                        canvas.__amActivePointerViewState = captureViewState();
                    }, true);

                    canvas.addEventListener('pointermove', (event) => {
                        const point = getEventCanvasPoint(event);
                        updatePointerMovement(point);
                    }, true);

                    canvas.addEventListener('pointerup', (event) => {
                        const point = getEventCanvasPoint(event);
                        updatePointerMovement(point);
                        const target = resolveInteractiveTarget(point);
                        const targetKey = getTargetKey(target);
                        const activeKey = canvas.__amActiveInteractiveTargetKey;
                        const treatAsDrag = canvas.__amActivePointerMoved || hasViewChangedSincePointerDown() || this.interactionActive;

                        if (!treatAsDrag && target && (!activeKey || targetKey === activeKey)) {
                            canvas.__amLastPointerActivationAt = Date.now();
                            activateCanvasTarget(event, target, 'pointerup');
                        }

                        clearActivePointerTracking();
                    }, true);

                    canvas.addEventListener('pointercancel', () => {
                        clearActivePointerTracking();
                    }, true);

                    canvas.addEventListener('touchstart', (event) => {
                        const point = getEventCanvasPoint(event);
                        const target = resolveInteractiveTarget(point);
                        canvas.__amActiveInteractiveTargetKey = getTargetKey(target);
                        canvas.__amActivePointerStartX = point?.x ?? null;
                        canvas.__amActivePointerStartY = point?.y ?? null;
                        canvas.__amActivePointerMoved = false;
                        canvas.__amActivePointerViewState = captureViewState();
                    }, { passive: true, capture: true });

                    canvas.addEventListener('touchmove', (event) => {
                        const point = getEventCanvasPoint(event);
                        updatePointerMovement(point);
                    }, { passive: true, capture: true });

                    canvas.addEventListener('touchend', (event) => {
                        const point = getEventCanvasPoint(event);
                        updatePointerMovement(point);
                        const target = resolveInteractiveTarget(point);
                        const targetKey = getTargetKey(target);
                        const activeKey = canvas.__amActiveInteractiveTargetKey;
                        const treatAsDrag = canvas.__amActivePointerMoved || hasViewChangedSincePointerDown() || this.interactionActive;

                        if (!treatAsDrag && target && (!activeKey || targetKey === activeKey)) {
                            canvas.__amLastPointerActivationAt = Date.now();
                            activateCanvasTarget(event, target, 'touchend');
                        }

                        clearActivePointerTracking();
                    }, { passive: false, capture: true });
                };

                return function(source, canvasCtx, viewParams) {
                    if (!this._labelRenderCycleActive) {
                        this._labelRenderCycleActive = true;
                        this.labelPositions = [];
                        this._occupiedLabelBounds = [];
                        this._renderedLabelCount = 0;
                        this._markerHitRegions = [];

                        queueMicrotask(() => {
                            this._labelRenderCycleActive = false;
                        });
                    }

                    canvasCtx.save();
                    try {
                        // Get the current zoom level to adjust sizes
                        const zoom = viewParams.fov[0]; // Get the current FOV in degrees
                        const pixelsPerDegree = canvasCtx.canvas.width / zoom;

                        const objPopularity = source.data.popularity;

                        var opacity = 1.0;

                        if (objPopularity < 70) {
                            opacity = 0.8;

                            if (objPopularity < 40) {
                                opacity = 0.6;

                                if (objPopularity < 10) {
                                    opacity = 0.4;
                                }
                            }
                        }

                        canvasCtx.globalAlpha = opacity;
                        
                        // Get size in degrees from source data
                        const sizeDegrees = source.data.sizeDeg || 0.1; // Default to 0.1° if not specified
                        
                        // Convert size to pixels based on current zoom
                        const sizePixels = Math.max(5, sizeDegrees * pixelsPerDegree); // Minimum 5px

                        // Draw a small filled circle (4px radius)
                        canvasCtx.fillStyle = source.data.color || '#ff0000';
                        const radius = 4;
                        canvasCtx.beginPath();
                        canvasCtx.arc(source.x, source.y, radius, 0, 2 * Math.PI);
                        canvasCtx.fill();
            
                        // Draw circle or ellipse based on source data
                        canvasCtx.beginPath();

                        // For ellipses, calculate width and height in pixels
                        const widthPixels = source.data.widthDeg ? 
                            Math.max(5, source.data.widthDeg * pixelsPerDegree) : 
                            sizePixels;
                        const heightPixels = source.data.heightDeg ? 
                            Math.max(5, source.data.heightDeg * pixelsPerDegree) : 
                            sizePixels;

                        // Draw the marker with a direct path to avoid leaking transforms into
                        // the shared Aladin render pass.
                        if (source.data.isEllipse) {
                            const rotationRadians = source.data.rotation
                                ? source.data.rotation * Math.PI / 180
                                : 0;

                            if (typeof canvasCtx.ellipse === 'function') {
                                canvasCtx.ellipse(
                                    source.x,
                                    source.y,
                                    Math.max(2.5, widthPixels / 2),
                                    Math.max(2.5, heightPixels / 2),
                                    rotationRadians,
                                    0,
                                    2 * Math.PI);
                            } else {
                                canvasCtx.save();
                                canvasCtx.translate(source.x, source.y);
                                if (rotationRadians) {
                                    canvasCtx.rotate(rotationRadians);
                                }
                                canvasCtx.scale(1, heightPixels / widthPixels);
                                canvasCtx.arc(0, 0, widthPixels / 2, 0, 2 * Math.PI, false);
                                canvasCtx.restore();
                            }
                        } else {
                            canvasCtx.arc(source.x, source.y, sizePixels / 2, 0, 2 * Math.PI, false);
                        }

                        // Style the marker
                        canvasCtx.closePath();
                        canvasCtx.strokeStyle = source.data.color || '#7dd3fc';
                        canvasCtx.lineWidth = 2;
                        canvasCtx.globalAlpha = opacity;
                        canvasCtx.stroke();

                        // Fill the marker if needed
                        if (source.data.fill) {
                            canvasCtx.fillStyle = source.data.fillColor || (source.data.color + '40') || 'rgba(255, 0, 0, 0.2)';
                            canvasCtx.fill();
                        }

                        if (!this._markerHitRegions) {
                            this._markerHitRegions = [];
                        }

                        const markerHitRegion = {
                            x: source.x - Math.max(8, widthPixels / 2) - 4,
                            y: source.y - Math.max(8, heightPixels / 2) - 4,
                            width: Math.max(16, widthPixels) + 8,
                            height: Math.max(16, heightPixels) + 8,
                            centerX: source.x,
                            centerY: source.y,
                            source
                        };

                        source._markerHitRegion = markerHitRegion;

                        const existingMarkerHitIndex = this._markerHitRegions.findIndex(region =>
                            region.source?.data?.objId === source.data.objId);

                        if (existingMarkerHitIndex >= 0) {
                            this._markerHitRegions[existingMarkerHitIndex] = markerHitRegion;
                        } else {
                            this._markerHitRegions.push(markerHitRegion);
                        }

                        ensureMarkerCanvasHandlers(canvasCtx.canvas);

                        // Keep text labels out of Aladin's shared canvas render pass.
                        // Marker shapes stay canvas-based, but labels are handled by DOM overlays.
                        return;
                        
                        // Get current field of view
                        const fov = Math.max(viewParams.fov[0], viewParams.fov[1]);
            
                        // Only show label if zoomed in enough
                        if ((fov > 300 && source.data.alwaysShowLabel !== true) ||
                            (!source.data.showLabel && source.data.alwaysShowLabel !== true) ||
                            source.data.renderLabel === false) {
                            return;
                        }

                    const labelText = source.data.label;
                    const labelType = source.data.labelType;
                    const density = getLabelDensityConfig(fov, canvasCtx.canvas.width, canvasCtx.canvas.height, source.data);
                    if (!density || !labelText) {
                        return;
                    }

                    if ((this._renderedLabelCount || 0) >= density.maxLabels &&
                        source.data.alwaysShowLabel !== true) {
                        return;
                    }

                    const hoverMatch = this.hoveredLabelBounds?.source?.data?.objId
                        && this.hoveredLabelBounds.source.data.objId === source.data.objId;
                    const textColor = hoverMatch ? '#fde047' : (source.data.textColor || '#ffffff');
                    const markerHalfExtent = Math.max(widthPixels, heightPixels) / 2;

                    canvasCtx.save();
                    canvasCtx.globalAlpha = opacity;
                    canvasCtx.textAlign = 'left';
                    canvasCtx.textBaseline = 'top';
                    canvasCtx.font = `${density.fontSize}px Arial`;

                    const textMetrics = canvasCtx.measureText(labelText);
                    const mainTextHeight = Math.ceil(density.fontSize + 2);
                    const typeText = density.showType && labelType && labelType !== 'mount'
                        ? labelType
                        : null;

                    let totalTextWidth = textMetrics.width;
                    let totalTextHeight = mainTextHeight;
                    let typeMetrics = null;

                    if (typeText) {
                        canvasCtx.font = `${density.typeFontSize}px Arial`;
                        typeMetrics = canvasCtx.measureText(typeText);
                        totalTextWidth = Math.max(totalTextWidth, typeMetrics.width);
                        totalTextHeight += Math.ceil(density.typeFontSize + 2);
                    }

                    canvasCtx.font = `${density.fontSize}px Arial`;

                    const candidates = [
                        { textX: source.x + markerHalfExtent + density.markerGap, textY: source.y - totalTextHeight / 2 },
                        { textX: source.x + markerHalfExtent + density.markerGap, textY: source.y - totalTextHeight - density.markerGap },
                        { textX: source.x + markerHalfExtent + density.markerGap, textY: source.y + density.markerGap },
                        { textX: source.x - markerHalfExtent - density.markerGap - totalTextWidth, textY: source.y - totalTextHeight / 2 },
                        { textX: source.x - markerHalfExtent - density.markerGap - totalTextWidth, textY: source.y - totalTextHeight - density.markerGap },
                        { textX: source.x - markerHalfExtent - density.markerGap - totalTextWidth, textY: source.y + density.markerGap },
                        { textX: source.x - (totalTextWidth / 2), textY: source.y + markerHalfExtent + density.markerGap },
                        { textX: source.x - (totalTextWidth / 2), textY: source.y - markerHalfExtent - totalTextHeight - density.markerGap },
                        { textX: source.x + markerHalfExtent + density.farPlacementGap, textY: source.y - totalTextHeight / 2 },
                        { textX: source.x - markerHalfExtent - density.farPlacementGap - totalTextWidth, textY: source.y - totalTextHeight / 2 },
                        { textX: source.x - (totalTextWidth / 2), textY: source.y + markerHalfExtent + density.farPlacementGap },
                        { textX: source.x - (totalTextWidth / 2), textY: source.y - markerHalfExtent - totalTextHeight - density.farPlacementGap }
                    ];

                    const priority = getMarkerLabelPriority(source.data);
                    let labelPlacement = null;

                    for (const candidate of candidates) {
                        const candidateBounds = {
                            x: candidate.textX - density.backgroundPadX,
                            y: candidate.textY - density.backgroundPadY,
                            width: totalTextWidth + density.backgroundPadX * 2,
                            height: totalTextHeight + density.backgroundPadY * 2,
                            source,
                            priority
                        };

                        if (!isInsideCanvas(candidateBounds, canvasCtx.canvas)) {
                            continue;
                        }

                        const hasCollision = (this._occupiedLabelBounds || []).some(existing =>
                            getOverlapRatio(existing, candidateBounds) > density.maxOverlapRatio);

                        if (hasCollision && source.data.alwaysShowLabel !== true) {
                            continue;
                        }

                        labelPlacement = {
                            textX: candidate.textX,
                            textY: candidate.textY,
                            bounds: candidateBounds
                        };
                        break;
                    }

                    if (!labelPlacement) {
                        canvasCtx.restore();
                        return;
                    }

                    if (!this.labelPositions) {
                        this.labelPositions = [];
                    }

                    if (!this._occupiedLabelBounds) {
                        this._occupiedLabelBounds = [];
                    }

                    if (density.drawBackdrop !== false) {
                        drawRoundedBackdrop(
                            canvasCtx,
                            labelPlacement.bounds.x,
                            labelPlacement.bounds.y,
                            labelPlacement.bounds.width,
                            labelPlacement.bounds.height,
                            hoverMatch ? 'rgba(15, 23, 42, 0.92)' : 'rgba(2, 6, 23, 0.72)');
                    }

                    canvasCtx.fillStyle = textColor;
                    canvasCtx.font = `${density.fontSize}px Arial`;
                    canvasCtx.fillText(labelText, labelPlacement.textX, labelPlacement.textY);

                    if (typeText) {
                        canvasCtx.font = `${density.typeFontSize}px Arial`;
                        canvasCtx.fillStyle = hoverMatch ? '#fef3c7' : (source.data.textColor || '#cbd5e1');
                        canvasCtx.fillText(
                            typeText,
                            labelPlacement.textX,
                            labelPlacement.textY + mainTextHeight);
                    }

                    const existingIndex = this.labelPositions.findIndex(bounds =>
                        bounds.source?.data?.objId === source.data.objId);

                    if (existingIndex >= 0) {
                        this.labelPositions[existingIndex] = labelPlacement.bounds;
                    } else {
                        this.labelPositions.push(labelPlacement.bounds);
                    }

                    this._occupiedLabelBounds.push(labelPlacement.bounds);
                    this._renderedLabelCount = (this._renderedLabelCount || 0) + 1;
                    source._labelBounds = labelPlacement.bounds;

                    // Add this at the top of your file or in the initialization
                    let isHoveringLabel = false;

                    // Then modify your event listener code:
                    if (!source._hasLabelHandlers) {
                        source._hasLabelHandlers = true;

                        const getEventCanvasPoint = (event) => {
                            const point = event?.changedTouches?.[0]
                                ?? event?.touches?.[0]
                                ?? event
                                ?? null;
                            const clientX = point?.clientX;
                            const clientY = point?.clientY;

                            if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) {
                                return null;
                            }

                            const rect = canvasCtx.canvas.getBoundingClientRect();
                            return {
                                x: clientX - rect.left,
                                y: clientY - rect.top
                            };
                        };

                        const findLabelBoundsAtPoint = (point) => {
                            if (!point || !this.labelPositions) {
                                return null;
                            }

                            for (const bounds of this.labelPositions) {
                                if (point.x >= bounds.x &&
                                    point.x <= bounds.x + bounds.width &&
                                    point.y >= bounds.y &&
                                    point.y <= bounds.y + bounds.height) {
                                    return bounds;
                                }
                            }

                            return null;
                        };

                        const findMarkerRegionAtPoint = (point) => {
                            if (!point || !this._markerHitRegions || this._markerHitRegions.length === 0) {
                                return null;
                            }

                            let bestRegion = null;
                            let bestDistanceSquared = Number.POSITIVE_INFINITY;

                            for (const region of this._markerHitRegions) {
                                if (!region?.source?.data) {
                                    continue;
                                }

                                const insideBounds = point.x >= region.x
                                    && point.x <= region.x + region.width
                                    && point.y >= region.y
                                    && point.y <= region.y + region.height;

                                if (!insideBounds) {
                                    continue;
                                }

                                const deltaX = point.x - region.centerX;
                                const deltaY = point.y - region.centerY;
                                const distanceSquared = (deltaX * deltaX) + (deltaY * deltaY);

                                if (distanceSquared < bestDistanceSquared) {
                                    bestDistanceSquared = distanceSquared;
                                    bestRegion = region;
                                }
                            }

                            return bestRegion;
                        };

                        // Add a single mousemove listener to the canvas
                        if (!canvasCtx.canvas._hasGlobalMoveHandler) {
                            canvasCtx.canvas._hasGlobalMoveHandler = true;

                            canvasCtx.canvas.addEventListener('mousemove', debounce((event) => {
                                const point = getEventCanvasPoint(event);
                                if (!point) {
                                    return;
                                }

                                this.hoveredLabelBounds = findLabelBoundsAtPoint(point);
                                const foundHover = this.hoveredLabelBounds != null;

                                // Update cursor and request redraw if hover state changed
                                if (foundHover !== isHoveringLabel ||
                                    (this.hoveredLabelBounds !== this.lastHoveredLabelBounds)) {
                                    isHoveringLabel = foundHover;
                                    this.lastHoveredLabelBounds = this.hoveredLabelBounds;

                                    if (isHoveringLabel) {
                                        canvasCtx.canvas.classList.add('xcanvas-hover');
                                    } else {
                                        canvasCtx.canvas.classList.remove('xcanvas-hover');
                                    }

                                    // Repaint in place so hover does not churn catalogs/overlays.
                                    this.aladin?.view?.requestRedraw?.();
                                }
                            }, 16)); // ~60fps
                        }
                    }

                    canvasCtx.restore();
                    } finally {
                        canvasCtx.restore();
                    }
                }.bind(this);
            },
            
            // Add a marker at RA/Dec with options
            addMarker: async function(ra, dec, pa, options = {}) {
                if (typeof options === 'string') {
                    options = { label: options };
                }
                
                try {
                    var isEllipse = false;
                    const MIN_SIZE_DEGREES = 0.1; // Minimum size in degrees (~6 arcmin)
                    const LIGHT_BLUE = '#7dd3fc'; // Light blue color

                    // Calculate marker size in degrees (convert from arcminutes)
                    // Use sizeArcmin if available, otherwise use a default size
                    const sizeArcminMin = options.sizeArcminMin || 10; // Default to 10 arcmin if no size provided
                    const sizeArcminMax = options.sizeArcminMax || 10; // Default to 10 arcmin if no size provided

                    const majorAxis = Math.max(MIN_SIZE_DEGREES, (sizeArcminMax / 60) * 1.2) * 0.6;
                    const minorAxis = Math.max(MIN_SIZE_DEGREES, (sizeArcminMin / 60) * 1.2) * 0.6;

                    // check if both sizes are available and one of them is bigger than the other by a certain factor so that an ellipse is better
                    if (sizeArcminMax && sizeArcminMin && sizeArcminMax > sizeArcminMin * 1.2) {
                        isEllipse = true;
                    }

                    const hasValidRotation = Number.isFinite(pa) && Math.abs(pa) > 0.01;

                    if (isEllipse) {
                        console.debug('[AstroBinDebug] DSO marker rotation normalization', {
                            objId: options.objId,
                            label: options.label,
                            inputPositionAngle: options.positionAngle,
                            normalizedPa: pa,
                            hasValidRotation
                        });
                    }

                    // Store marker info for label handling
                    const markerInfo = {
                        ra,
                        dec,
                        label: options.label || '',
                        labelType: options.type,
                        popularity: options.popularity,
                        objId: options.objId,
                        color: options.color,
                        element: null,
                        visible: this.showMarkerLabels,
                        sizeDeg: majorAxis,
                        widthDeg: majorAxis,  
                        heightDeg: minorAxis, 
                        markerSize: isEllipse 
                            ? { width: majorAxis, height: minorAxis, isEllipse: true }
                            : { width: majorAxis, height: majorAxis, isEllipse: false },
                        isEllipse: isEllipse,
                        rotation: isEllipse && hasValidRotation ? pa : null,
                        renderLabel: options.renderLabel !== false,
                        alwaysShowLabel: options.alwaysShowLabel === true,
                        persistent: options.persistent === true,
                        selectOnTextClick: options.selectOnTextClick === true
                    };


                    // Create a catalog for the marker if it doesn't exist
                    if (!this.dsoCatalog) {
                        this.dsoCatalog = A.catalog({
                            name: 'DSO_Markers',
                            shape: this.createDSODrawFunction()
                            // onClick: 'showPopup'
                        });

                        this.dsoCatalog.setSelectionColor('#fce303');
                        this.aladin.addCatalog(this.dsoCatalog);
                    }
                    
                    
                    // Create source with custom data
                    const source = A.source(markerInfo.ra, markerInfo.dec, {
                        label: markerInfo.label,
                        labelType: markerInfo.labelType,
                        popularity: markerInfo.popularity,
                        objId: markerInfo.objId,
                        color: markerInfo.color || '#ff0000',
                        sizeDeg: markerInfo.sizeDeg || 10,
                        isEllipse: markerInfo.isEllipse || false,
                        rotation: markerInfo.rotation,
                        widthDeg: markerInfo.widthDeg || markerInfo.size || 10,
                        heightDeg: markerInfo.heightDeg || markerInfo.size || 10,
                        showLabel: (this.showMarkerLabels || markerInfo.alwaysShowLabel) && markerInfo.renderLabel,
                        alwaysShowLabel: markerInfo.alwaysShowLabel,
                        renderLabel: markerInfo.renderLabel,
                        selectOnTextClick: markerInfo.selectOnTextClick,
                        // Add any other custom properties you need
                        popupTitle: markerInfo.label,
                        popupDesc: this.getDSOPopupContent(markerInfo),
                        popupClass: 'dso-popup',
                        popupPos: 'bottom'
                    });
                    
                    // Add the source to the catalog
                    this.dsoCatalog.addSources([source]);
                    
                    // Store source info
                    markerInfo.source = source;
                    this.markers.push(markerInfo);

                    if ((this.showMarkerLabels || markerInfo.alwaysShowLabel) && markerInfo.renderLabel !== false) {
                        this.createMarkerLabel(markerInfo);
                    }


                    return source;

                } catch (error) {
                    console.error('Error adding marker:', error);
                    throw error;
                }
            },

            addDSOMarkers: function(markers) {
                if (!this.aladin || !markers || !markers.length) return;
    
                markers.forEach(marker => {        
                    // Create a catalog for the marker if it doesn't exist
                    if (!this.dsoCatalog) {
                        this.dsoCatalog = A.catalog({
                            name: 'DSO_Markers',
                            shape: this.createDSODrawFunction()
                            // onClick: 'showPopup'
                        });

                        this.dsoCatalog.setSelectionColor('#fce303');
                        this.aladin.addCatalog(this.dsoCatalog);
                    }
                    
                    // Add the source to the catalog
                    this.dsoCatalog.addSources([marker.source]);
                });
    
                //this.aladin.repaint();
            },

            // In the returned object, add this method:
            redrawMarkers: function() {
                if (!this.aladin) return;
    
                // Clear all markers
                this.clearMarkers(false);
    
                // Re-add all sources
                if (this.markers && this.markers.length > 0) {
                    this.addDSOMarkers(this.markers);
                }
            },

            shouldSelectOnMarkerTextClick: function(markerData) {
                const objId = markerData?.objId || '';
                return markerData?.selectOnTextClick === true
                    || (typeof objId === 'string' && (objId.startsWith('target:') || objId.startsWith('scheduled:') || objId.startsWith('community:')));
            },

            invokeMarkerSelection: function(objId) {
                if (!objId || !this.dotNetHelper) {
                    return;
                }

                this.dotNetHelper.invokeMethodAsync('SelectDSO', objId)
                    .catch(err => console.error('Error selecting marker:', err));
            },

            showMarkerPopup: function(markerData, event) {
                if (!this.dotNetHelper || !markerData) {
                    return;
                }

                this.activateAsGlobalInstance();

                const aladinDiv = document.getElementById(this.aladin.aladinDiv.id);
                const touchPoint = event?.changedTouches?.[0]
                    ?? event?.touches?.[0]
                    ?? null;
                let clickX = event?.clientX ?? touchPoint?.clientX ?? 0;
                let clickY = event?.clientY ?? touchPoint?.clientY ?? 0;

                if (aladinDiv) {
                    const containerRect = aladinDiv.getBoundingClientRect();
                    const clientX = event?.clientX ?? touchPoint?.clientX ?? containerRect.left;
                    const clientY = event?.clientY ?? touchPoint?.clientY ?? containerRect.top;
                    clickX = clientX - containerRect.left;
                    clickY = clientY - containerRect.top;
                }

                pushTrace('dso-marker-popup-requested', {
                    objId: markerData?.objId || null,
                    label: markerData?.label || null,
                    labelType: markerData?.labelType || null,
                    eventType: event?.type || null,
                    clickX,
                    clickY
                });

                const openPopup = () => {
                    pushTrace('dso-marker-popup-dispatched', {
                        objId: markerData?.objId || null,
                        label: markerData?.label || null
                    });
                    this.dotNetHelper.invokeMethodAsync(
                        'ShowDSOPopupAsync',
                        markerData.objId,
                        markerData.label,
                        markerData.labelType,
                        clickX,
                        clickY)
                        .catch(err => console.error('Error showing DSO popup:', err));
                };

                window.setTimeout(openPopup, 0);
            },

            showMarkerPopupForObject: function(object) {
                if (!object?.data) {
                    return;
                }

                const aladinDiv = document.getElementById(this.aladin?.aladinDiv?.id);
                const projectedPoint = this.aladin?.world2pix?.(object.ra, object.dec);

                if (aladinDiv &&
                    Array.isArray(projectedPoint) &&
                    projectedPoint.length >= 2 &&
                    Number.isFinite(projectedPoint[0]) &&
                    Number.isFinite(projectedPoint[1])) {
                    const rect = aladinDiv.getBoundingClientRect();
                    this.showMarkerPopup(object.data, {
                        clientX: rect.left + projectedPoint[0],
                        clientY: rect.top + projectedPoint[1]
                    });
                    return;
                }

                this.showMarkerPopup(object.data, null);
            },

            handleMarkerTextInteraction: function(markerData, event) {
                this.activateAsGlobalInstance();

                pushTrace('dso-marker-interaction', {
                    objId: markerData?.objId || null,
                    label: markerData?.label || null,
                    selectOnTextClick: markerData?.selectOnTextClick === true,
                    eventType: event?.type || null
                });

                if (event?.stopPropagation) {
                    event.stopPropagation();
                }

                if (this.shouldSelectOnMarkerTextClick(markerData)) {
                    this.invokeMarkerSelection(markerData?.objId);
                    return;
                }

                this.showMarkerPopup(markerData, event);
            },

            ensureConstellationLabelLayer: function() {
                const host = document.getElementById(this.aladin?.aladinDiv?.id);
                if (!host) {
                    return null;
                }

                if (this.constellationLabelLayer && host.contains(this.constellationLabelLayer)) {
                    return this.constellationLabelLayer;
                }

                const layer = document.createElement('div');
                layer.className = 'am-constellation-label-layer';
                if (!host.style.position) {
                    host.style.position = 'relative';
                }
                layer.style.position = 'absolute';
                layer.style.left = '0';
                layer.style.top = '0';
                layer.style.width = '100%';
                layer.style.height = '100%';
                layer.style.overflow = 'hidden';
                layer.style.pointerEvents = 'none';
                layer.style.zIndex = '3';
                host.appendChild(layer);
                this.constellationLabelLayer = layer;
                return layer;
            },

            ensureConstellationCanvasLayer: function() {
                const host = document.getElementById(this.aladin?.aladinDiv?.id);
                if (!host) {
                    return null;
                }

                if (this.constellationCanvasLayer && host.contains(this.constellationCanvasLayer)) {
                    return this.constellationCanvasLayer;
                }

                const svg = document.createElementNS(this.constellationSvgNamespace, 'svg');
                svg.classList.add('am-constellation-canvas-layer');
                if (!host.style.position) {
                    host.style.position = 'relative';
                }

                svg.style.position = 'absolute';
                svg.style.left = '0';
                svg.style.top = '0';
                svg.style.width = '100%';
                svg.style.height = '100%';
                svg.style.overflow = 'visible';
                svg.style.pointerEvents = 'none';
                svg.style.zIndex = '2';
                svg.setAttribute('preserveAspectRatio', 'none');
                host.appendChild(svg);
                this.constellationCanvasLayer = svg;
                return svg;
            },

            ensureConstellationDebugPanel: function() {
                if (this.constellationDebugPanel?.remove) {
                    this.constellationDebugPanel.remove();
                }

                this.constellationDebugPanel = null;
                return null;
            },

            updateConstellationDebugPanel: function(partialState = {}) {
                void partialState;
                this.constellationDebugState = {};
                if (window.__amConstellationDebugState) {
                    delete window.__amConstellationDebugState;
                }

                this.ensureConstellationDebugPanel();
            },

            clearConstellationCanvasLayer: function() {
                const layer = this.constellationCanvasLayer;
                if (!layer) {
                    return;
                }

                if (typeof layer.replaceChildren === 'function') {
                    layer.replaceChildren();
                    return;
                }

                while (layer.firstChild) {
                    layer.removeChild(layer.firstChild);
                }
            },

            updateConstellationCanvasLayer: async function() {
                if (!this.showConstellationsActive) {
                    if (this.constellationCanvasLayer) {
                        this.constellationCanvasLayer.style.display = 'none';
                    }
                    this.updateConstellationDebugPanel({
                        visible: false,
                        pathsDrawn: 0
                    });
                    return;
                }

                const layer = this.ensureConstellationCanvasLayer();
                if (!layer || !this.aladin) {
                    this.updateConstellationDebugPanel({
                        visible: true,
                        error: 'constellation svg layer unavailable'
                    });
                    return;
                }

                layer.style.display = 'block';

                const hostRect = layer.getBoundingClientRect();
                const width = Math.max(1, Math.round(hostRect.width));
                const height = Math.max(1, Math.round(hostRect.height));
                if (width <= 1 || height <= 1) {
                    this.updateConstellationDebugPanel({
                        visible: true,
                        svgWidth: width,
                        svgHeight: height,
                        error: `svg host too small (${width}x${height})`
                    });
                    return;
                }

                layer.setAttribute('viewBox', `0 0 ${width} ${height}`);
                layer.setAttribute('width', `${width}`);
                layer.setAttribute('height', `${height}`);
                this.clearConstellationCanvasLayer();

                const lineSegments = await getConstellationLineSegments();
                const highlightedId = this.highlightedConstellationId ? String(this.highlightedConstellationId) : null;
                const highlightedSegmentsById = highlightedId ? await getConstellationSegmentsById() : null;
                const highlightedSegments = highlightedId && highlightedSegmentsById
                    ? (highlightedSegmentsById.get(highlightedId) || [])
                    : [];
                let drawnPathCount = 0;
                let projectedPointCount = 0;
                let invalidProjectedCount = 0;
                let outsidePointCount = 0;
                let jumpSplitCount = 0;

                const drawChunk = (chunk, options) => {
                    if (!Array.isArray(chunk) || chunk.length < 2) {
                        return;
                    }

                    const polyline = document.createElementNS(this.constellationSvgNamespace, 'polyline');
                    polyline.setAttribute('points', chunk.map((point) => `${point[0].toFixed(2)},${point[1].toFixed(2)}`).join(' '));
                    polyline.setAttribute('fill', 'none');
                    polyline.setAttribute('stroke', options.stroke);
                    polyline.setAttribute('stroke-width', String(options.strokeWidth));
                    polyline.setAttribute('stroke-linecap', 'round');
                    polyline.setAttribute('stroke-linejoin', 'round');
                    polyline.setAttribute('vector-effect', 'non-scaling-stroke');
                    polyline.setAttribute('opacity', String(options.opacity));

                    if (options.strokeDashArray) {
                        polyline.setAttribute('stroke-dasharray', options.strokeDashArray);
                    }

                    layer.appendChild(polyline);
                    drawnPathCount += 1;
                };

                const projectConstellationPoint = (longitudeDegrees, declinationDegrees) => {
                    const candidates = getConstellationProjectionCandidates(longitudeDegrees);
                    for (const candidateRa of candidates) {
                        const projected = getProjectedPointComponents(this.aladin?.world2pix?.(candidateRa, declinationDegrees));
                        if (projected) {
                            return projected;
                        }
                    }

                    return null;
                };

                const appendProjectedSegments = (segments, options) => {
                    if (!Array.isArray(segments) || segments.length === 0) {
                        return;
                    }

                    const viewportMargin = Math.max(40, Math.round(Math.min(width, height) * 0.14));
                    const maxJumpPx = Math.max(180, Math.round(Math.max(width, height) * 0.72));

                    segments.forEach((segment) => {
                        if (!Array.isArray(segment) || segment.length < 2) {
                            return;
                        }

                        let chunk = [];
                        let previousPoint = null;

                        segment.forEach((skyPoint) => {
                            const projected = projectConstellationPoint(skyPoint[0], skyPoint[1]);
                            const validProjected = Array.isArray(projected)
                                && projected.length >= 2
                                && Number.isFinite(projected[0])
                                && Number.isFinite(projected[1]);

                            if (!validProjected) {
                                invalidProjectedCount += 1;
                                drawChunk(chunk, options);
                                chunk = [];
                                previousPoint = null;
                                return;
                            }

                            const px = projected[0];
                            const py = projected[1];
                            const insideExtendedViewport = px >= -viewportMargin
                                && px <= width + viewportMargin
                                && py >= -viewportMargin
                                && py <= height + viewportMargin;

                            if (!insideExtendedViewport) {
                                outsidePointCount += 1;
                                drawChunk(chunk, options);
                                chunk = [];
                                previousPoint = null;
                                return;
                            }

                            if (previousPoint) {
                                const jumpX = Math.abs(px - previousPoint[0]);
                                const jumpY = Math.abs(py - previousPoint[1]);
                                if (jumpX > maxJumpPx || jumpY > maxJumpPx) {
                                    jumpSplitCount += 1;
                                    drawChunk(chunk, options);
                                    chunk = [];
                                }
                            }

                            chunk.push([px, py]);
                            projectedPointCount += 1;
                            previousPoint = [px, py];
                        });

                        drawChunk(chunk, options);
                    });
                };

                appendProjectedSegments(lineSegments, {
                    stroke: 'rgba(203, 213, 225, 0.72)',
                    strokeWidth: 1.6,
                    opacity: 0.9
                });

                appendProjectedSegments(highlightedSegments, {
                    stroke: 'rgba(241, 245, 249, 0.96)',
                    strokeWidth: 2.35,
                    opacity: 1
                });

                this.updateConstellationDebugPanel({
                    visible: true,
                    geoJsonSource: constellationGeoJsonResolvedUrl || 'embedded-js-module',
                    svgWidth: width,
                    svgHeight: height,
                    segmentCount: lineSegments.length + highlightedSegments.length,
                    baseSegmentCount: lineSegments.length,
                    highlightedSegmentCount: highlightedSegments.length,
                    pathsDrawn: drawnPathCount,
                    projectedPointCount,
                    invalidProjectedCount,
                    outsidePointCount,
                    jumpSplitCount,
                    fov: Math.max(...(this.aladin?.getFov?.() || [0, 0])),
                    error: ''
                });
            },

            getConstellationLabelConfig: function() {
                const currentFov = Math.max(...(this.aladin?.getFov?.() || [180, 180]));

                if (!Number.isFinite(currentFov) || currentFov > 360) {
                    return null;
                }

                if (currentFov > 240) {
                    return {
                        fontSize: 8.25,
                        maxLabels: 44,
                        maxRank: 3,
                        useShortLabel: true
                    };
                }

                if (currentFov > 170) {
                    return {
                        fontSize: 8.75,
                        maxLabels: 54,
                        maxRank: 3,
                        useShortLabel: true
                    };
                }

                if (currentFov > 110) {
                    return {
                        fontSize: 9,
                        maxLabels: 64,
                        maxRank: 3,
                        useShortLabel: true
                    };
                }

                if (currentFov > 70) {
                    return {
                        fontSize: 9.5,
                        maxLabels: 76,
                        maxRank: 3,
                        useShortLabel: true
                    };
                }

                if (currentFov > 30) {
                    return {
                        fontSize: 10,
                        maxLabels: 84,
                        maxRank: 3,
                        useShortLabel: true
                    };
                }

                if (currentFov > 12) {
                    return {
                        fontSize: 10.5,
                        maxLabels: 88,
                        maxRank: 3,
                        useShortLabel: true
                    };
                }

                if (currentFov > 4) {
                    return {
                        fontSize: 11,
                        maxLabels: 88,
                        maxRank: 3,
                        useShortLabel: true
                    };
                }

                return {
                    fontSize: 11.25,
                    maxLabels: 88,
                    maxRank: 3,
                    useShortLabel: false
                };
            },

            hideConstellationLabels: function() {
                if (!this.constellationLabelElements) {
                    return;
                }

                this.constellationLabelElements.forEach((element) => {
                    if (element) {
                        element.style.display = 'none';
                    }
                });
            },

            updateConstellationLabelPositions: async function() {
                if (!this.showConstellationsActive) {
                    this.hideConstellationLabels();
                    return;
                }

                const labelConfig = this.getConstellationLabelConfig();
                const layer = this.ensureConstellationLabelLayer();

                if (!labelConfig || !layer) {
                    this.hideConstellationLabels();
                    return;
                }

                layer.style.display = 'block';
                const hostRect = layer.getBoundingClientRect();
                if (hostRect.width <= 0 || hostRect.height <= 0) {
                    this.hideConstellationLabels();
                    return;
                }

                let labelEntries = [];

                try {
                    labelEntries = await getConstellationLabelEntries();
                } catch (error) {
                    console.error('Error loading constellation labels:', error);
                    return;
                }

                const occupiedBounds = [];
                const visibleKeys = new Set();
                let visibleCount = 0;
                const filteredEntries = labelEntries
                    .filter((entry) => entry.rank <= labelConfig.maxRank)
                    .sort((a, b) => a.rank - b.rank || a.longLabel.localeCompare(b.longLabel));

                const boxesOverlap = (a, b) => a.x < b.x + b.width
                    && a.x + a.width > b.x
                    && a.y < b.y + b.height
                    && a.y + a.height > b.y;

                const projectConstellationLabelPoint = (longitudeDegrees, declinationDegrees) => {
                    const candidates = getConstellationProjectionCandidates(longitudeDegrees);
                    for (const candidateRa of candidates) {
                        const projected = getProjectedPointComponents(this.aladin?.world2pix?.(candidateRa, declinationDegrees));
                        if (projected) {
                            return projected;
                        }
                    }

                    return null;
                };

                filteredEntries.forEach((entry) => {
                    if (visibleCount >= labelConfig.maxLabels) {
                        return;
                    }

                    const pixelPoint = projectConstellationLabelPoint(entry.ra, entry.dec);
                    const element = this.constellationLabelElements.get(entry.key) || document.createElement('div');

                    if (!this.constellationLabelElements.has(entry.key)) {
                        element.className = 'am-constellation-label';
                        element.style.position = 'absolute';
                        element.style.pointerEvents = 'auto';
                        element.style.transform = 'translate(-50%, -50%)';
                        element.style.whiteSpace = 'nowrap';
                        element.style.fontFamily = 'Arial, sans-serif';
                        element.style.fontWeight = '600';
                        element.style.letterSpacing = '0.02em';
                        element.style.lineHeight = '1';
                        element.style.textShadow = '0 0 4px rgba(0, 0, 0, 0.95), 0 0 7px rgba(0, 0, 0, 0.72)';
                        element.style.color = 'rgba(203, 213, 225, 0.9)';
                        element.style.cursor = 'pointer';
                        element.style.userSelect = 'none';
                        element.style.touchAction = 'manipulation';
                        layer.appendChild(element);
                        this.constellationLabelElements.set(entry.key, element);

                        element.addEventListener('click', (event) => {
                            event.preventDefault?.();
                            event.stopPropagation?.();
                            this.highlightConstellation?.(element.dataset.constellationId || '');
                        });
                    } else if (element.parentNode !== layer) {
                        layer.appendChild(element);
                    }

                    if (!Array.isArray(pixelPoint) ||
                        pixelPoint.length < 2 ||
                        !Number.isFinite(pixelPoint[0]) ||
                        !Number.isFinite(pixelPoint[1])) {
                        element.style.display = 'none';
                        return;
                    }

                    const labelText = labelConfig.useShortLabel ? entry.shortLabel : entry.longLabel;
                    element.textContent = labelText;
                    element.dataset.constellationId = entry.shortLabel;
                    element.style.fontSize = `${labelConfig.fontSize}px`;
                    element.style.background = entry.shortLabel === this.highlightedConstellationId
                        ? 'rgba(51, 65, 85, 0.56)'
                        : 'transparent';
                    element.style.borderRadius = '4px';
                    element.style.padding = entry.shortLabel === this.highlightedConstellationId ? '1px 4px' : '0';
                    element.style.color = entry.shortLabel === this.highlightedConstellationId
                        ? 'rgba(248, 250, 252, 0.98)'
                        : 'rgba(203, 213, 225, 0.9)';
                    element.style.display = 'block';
                    element.style.left = `${pixelPoint[0]}px`;
                    element.style.top = `${pixelPoint[1]}px`;

                    const boundsRect = element.getBoundingClientRect();
                    const relativeBounds = {
                        x: pixelPoint[0] - (boundsRect.width / 2),
                        y: pixelPoint[1] - (boundsRect.height / 2),
                        width: boundsRect.width,
                        height: boundsRect.height
                    };

                    const isInside = relativeBounds.x >= 4
                        && relativeBounds.y >= 4
                        && (relativeBounds.x + relativeBounds.width) <= (hostRect.width - 4)
                        && (relativeBounds.y + relativeBounds.height) <= (hostRect.height - 4);
                    const overlapsExisting = occupiedBounds.some((bounds) => boxesOverlap(bounds, relativeBounds));

                    if (!isInside || overlapsExisting) {
                        element.style.display = 'none';
                        return;
                    }

                    occupiedBounds.push(relativeBounds);
                    visibleKeys.add(entry.key);
                    visibleCount += 1;
                });

                this.constellationLabelElements.forEach((element, key) => {
                    if (!visibleKeys.has(key)) {
                        element.style.display = 'none';
                    }
                });

                this.updateConstellationDebugPanel({
                    visible: true,
                    totalLabelCount: filteredEntries.length,
                    visibleLabelCount: visibleCount,
                    fov: Math.max(...(this.aladin?.getFov?.() || [0, 0])),
                    error: ''
                });
            },

            // Add method to generate popup content
            getDSOPopupContent: function(markerInfo) {
                const objIdJson = JSON.stringify(markerInfo.objId || '');
                const selectOnTextClickArg = markerInfo.selectOnTextClick === true ? 'true' : 'false';

                return `
                    <div class="dso-popup-header">
                        <h3 onclick='window.handleAladinMarkerPopupTextClick(event, ${objIdJson}, ${selectOnTextClickArg})'>${markerInfo.label}</h3>
                        ${markerInfo.type ? `<span class="dso-type">${markerInfo.type}</span>` : ''}
                    </div>
                    <div class="dso-popup-body" onclick='window.handleAladinMarkerPopupTextClick(event, ${objIdJson}, ${selectOnTextClickArg})'>
                        ${markerInfo.magnitude ? `<div><strong>Magnitude:</strong> ${markerInfo.magnitude}</div>` : ''}
                        ${markerInfo.size ? `<div><strong>Size:</strong> ${markerInfo.size}'</div>` : ''}
                        ${markerInfo.constellation ? `<div><strong>Constellation:</strong> ${markerInfo.constellation}</div>` : ''}
                    </div>
                    <div class="dso-popup-actions">
                        <button class="dso-btn dso-btn-select" 
                                onclick='window.handleAladinMarkerPopupSelect(event, ${objIdJson})'>
                            Select
                        </button>
                        <button class="dso-btn dso-btn-details" 
                                onclick='window.handleAladinMarkerPopupDetails(event, ${JSON.stringify(markerInfo.label || "")})'>
                            View Details
                        </button>
                    </div>
                `;
            },

            // Create a label element for a marker
            createMarkerLabel: function(markerInfo) {
                if (markerInfo.element) return; // Already has a label
                
                const container = document.getElementById(this.aladin?.aladinDiv?.id);
                if (!container) {
                    console.warn('Aladin container not found, cannot create marker label');
                    return;
                }

                // Create the label element
                const label = document.createElement('div');
                label.className = 'marker-label';
                label.textContent = markerInfo.label;
                
                // Style the label with a semi-transparent background
                label.style.cssText = `
                    position: absolute;
                    background-color: rgba(2, 6, 23, 0.64);
                    color: rgba(241, 245, 249, 0.95);
                    padding: 1px 4px;
                    border-radius: 999px;
                    border: 1px solid rgba(148, 163, 184, 0.22);
                    font-family: Arial, sans-serif;
                    font-size: 10px;
                    font-weight: 500;
                    white-space: nowrap;
                    pointer-events: auto;
                    z-index: 4;
                    text-align: center;
                    transform-origin: center;
                    will-change: transform;
                    cursor: pointer;
                    touch-action: none;
                    -webkit-tap-highlight-color: transparent;
                    user-select: none;
                    letter-spacing: 0;
                    box-shadow: 0 5px 14px rgba(2, 6, 23, 0.2);
                    text-shadow: 0 0 2px rgba(0, 0, 0, 0.92);
                `;

                const markerInteraction = {
                    objId: markerInfo.objId,
                    label: markerInfo.label,
                    labelType: markerInfo.labelType,
                    selectOnTextClick: markerInfo.selectOnTextClick === true
                };

                const captureViewState = () => {
                    const center = this.aladin?.getRaDec?.();
                    const fov = this.aladin?.getFov?.();

                    return {
                        ra: Array.isArray(center) && center.length >= 2 && Number.isFinite(center[0]) ? center[0] : null,
                        dec: Array.isArray(center) && center.length >= 2 && Number.isFinite(center[1]) ? center[1] : null,
                        fov: Array.isArray(fov) && fov.length >= 1 && Number.isFinite(fov[0]) ? fov[0] : null
                    };
                };

                const getWrappedRaDelta = (currentRa, startRa) => {
                    if (!Number.isFinite(currentRa) || !Number.isFinite(startRa)) {
                        return 0;
                    }

                    let delta = currentRa - startRa;
                    while (delta > 180) {
                        delta -= 360;
                    }
                    while (delta < -180) {
                        delta += 360;
                    }

                    return delta;
                };

                const hasViewChanged = (startState) => {
                    if (!startState) {
                        return false;
                    }

                    const currentState = captureViewState();
                    const raDelta = Math.abs(getWrappedRaDelta(currentState.ra, startState.ra));
                    const decDelta = Math.abs((currentState.dec ?? 0) - (startState.dec ?? 0));
                    const fovDelta = Math.abs((currentState.fov ?? 0) - (startState.fov ?? 0));

                    return raDelta > 0.01 || decDelta > 0.01 || fovDelta > 0.0005;
                };

                const clearPointerState = () => {
                    label.__amPointerId = null;
                    label.__amPointerStartX = null;
                    label.__amPointerStartY = null;
                    label.__amPointerMoved = false;
                    label.__amPointerViewState = null;
                    label.__amPointerDownEvent = null;
                    label.__amCanvasGestureStarted = false;
                };

                const updatePointerMovement = (clientX, clientY) => {
                    if (!Number.isFinite(label.__amPointerStartX) || !Number.isFinite(label.__amPointerStartY)) {
                        return;
                    }

                    const deltaX = clientX - label.__amPointerStartX;
                    const deltaY = clientY - label.__amPointerStartY;
                    if (((deltaX * deltaX) + (deltaY * deltaY)) >= 36) {
                        label.__amPointerMoved = true;
                    }
                };

                const activateLabel = (event) => {
                    event?.preventDefault?.();
                    event?.stopPropagation?.();
                    event?.stopImmediatePropagation?.();
                    this.handleMarkerTextInteraction(markerInteraction, event);
                };

                const getCanvasElement = () => container.querySelector('canvas');

                const dispatchPointerEventToCanvas = (originalEvent, type) => {
                    const canvas = getCanvasElement();
                    if (!canvas || !originalEvent) {
                        return;
                    }

                    try {
                        const syntheticEvent = new PointerEvent(type, {
                            bubbles: true,
                            cancelable: true,
                            composed: true,
                            pointerId: originalEvent.pointerId ?? 1,
                            pointerType: originalEvent.pointerType ?? 'mouse',
                            isPrimary: originalEvent.isPrimary ?? true,
                            clientX: originalEvent.clientX,
                            clientY: originalEvent.clientY,
                            screenX: originalEvent.screenX ?? 0,
                            screenY: originalEvent.screenY ?? 0,
                            button: originalEvent.button ?? 0,
                            buttons: originalEvent.buttons ?? 0,
                            ctrlKey: originalEvent.ctrlKey ?? false,
                            shiftKey: originalEvent.shiftKey ?? false,
                            altKey: originalEvent.altKey ?? false,
                            metaKey: originalEvent.metaKey ?? false,
                            pressure: originalEvent.pressure ?? 0.5
                        });
                        canvas.dispatchEvent(syntheticEvent);
                    } catch (error) {
                        console.debug('Unable to forward marker label pointer event to canvas', error);
                    }
                };

                const snapshotPointerEvent = (event) => {
                    if (!event) {
                        return null;
                    }

                    return {
                        pointerId: event.pointerId ?? 1,
                        pointerType: event.pointerType ?? 'mouse',
                        isPrimary: event.isPrimary ?? true,
                        clientX: event.clientX,
                        clientY: event.clientY,
                        screenX: event.screenX ?? 0,
                        screenY: event.screenY ?? 0,
                        button: event.button ?? 0,
                        buttons: event.buttons ?? 0,
                        ctrlKey: event.ctrlKey ?? false,
                        shiftKey: event.shiftKey ?? false,
                        altKey: event.altKey ?? false,
                        metaKey: event.metaKey ?? false,
                        pressure: event.pressure ?? 0.5
                    };
                };

                const startCanvasGestureIfNeeded = (currentEvent) => {
                    if (label.__amCanvasGestureStarted) {
                        return;
                    }

                    const startEvent = label.__amPointerDownEvent || currentEvent;
                    if (!startEvent) {
                        return;
                    }

                    dispatchPointerEventToCanvas(startEvent, 'pointerdown');
                    label.__amCanvasGestureStarted = true;
                };

                label.addEventListener('pointerdown', (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    label.__amPointerId = event.pointerId;
                    label.__amPointerStartX = event.clientX;
                    label.__amPointerStartY = event.clientY;
                    label.__amPointerMoved = false;
                    label.__amPointerViewState = captureViewState();
                    label.__amPointerDownEvent = snapshotPointerEvent(event);
                    label.__amCanvasGestureStarted = false;
                    label.setPointerCapture?.(event.pointerId);
                });

                label.addEventListener('pointermove', (event) => {
                    if (label.__amPointerId !== event.pointerId) {
                        return;
                    }

                    event.preventDefault();
                    event.stopPropagation();
                    updatePointerMovement(event.clientX, event.clientY);
                    const treatAsDrag = label.__amPointerMoved || hasViewChanged(label.__amPointerViewState) || this.interactionActive;
                    if (treatAsDrag) {
                        startCanvasGestureIfNeeded(event);
                        dispatchPointerEventToCanvas(event, 'pointermove');
                    }
                });

                label.addEventListener('pointerup', (event) => {
                    if (label.__amPointerId !== event.pointerId) {
                        return;
                    }

                    event.preventDefault();
                    event.stopPropagation();
                    updatePointerMovement(event.clientX, event.clientY);
                    const treatAsDrag = label.__amPointerMoved || hasViewChanged(label.__amPointerViewState) || this.interactionActive;

                    if (treatAsDrag) {
                        startCanvasGestureIfNeeded(event);
                        dispatchPointerEventToCanvas(event, 'pointerup');
                    } else {
                        activateLabel(event);
                    }

                    label.releasePointerCapture?.(event.pointerId);
                    clearPointerState();
                });

                label.addEventListener('pointercancel', (event) => {
                    if (label.__amPointerId !== event.pointerId) {
                        return;
                    }

                    if (label.__amCanvasGestureStarted) {
                        dispatchPointerEventToCanvas(event, 'pointercancel');
                    }
                    clearPointerState();
                });

                container.appendChild(label);
                markerInfo.element = label;
                markerInfo.interaction = markerInteraction;
                
                // Initial position update
                this.updateMarkerLabelPosition(markerInfo);
            },

            clearMarkerLabelHitBounds: function(markerInfo) {
                if (!markerInfo?.source) {
                    return;
                }

                if (Array.isArray(this.labelPositions)) {
                    this.labelPositions = this.labelPositions.filter(bounds =>
                        bounds?.source?.data?.objId !== markerInfo.source?.data?.objId);
                }

                markerInfo.source._labelBounds = null;
            },

            syncMarkerLabelHitBounds: function(markerInfo, aladinRect) {
                if (!markerInfo?.element || !markerInfo?.source || markerInfo.element.style.display === 'none') {
                    this.clearMarkerLabelHitBounds(markerInfo);
                    return;
                }

                const labelRect = markerInfo.element.getBoundingClientRect();
                if (!labelRect || labelRect.width <= 0 || labelRect.height <= 0) {
                    this.clearMarkerLabelHitBounds(markerInfo);
                    return;
                }

                const bounds = {
                    x: labelRect.left - aladinRect.left,
                    y: labelRect.top - aladinRect.top,
                    width: labelRect.width,
                    height: labelRect.height,
                    centerX: (labelRect.left - aladinRect.left) + (labelRect.width / 2),
                    centerY: (labelRect.top - aladinRect.top) + (labelRect.height / 2),
                    source: markerInfo.source
                };

                if (!Array.isArray(this.labelPositions)) {
                    this.labelPositions = [];
                }

                const existingIndex = this.labelPositions.findIndex(existing =>
                    existing?.source?.data?.objId === markerInfo.source?.data?.objId);

                if (existingIndex >= 0) {
                    this.labelPositions[existingIndex] = bounds;
                } else {
                    this.labelPositions.push(bounds);
                }

                markerInfo.source._labelBounds = bounds;
            },

            // Update label position based on current view
            updateMarkerLabelPosition: function(markerInfo) {
                if (!markerInfo.element || !markerInfo.visible) {
                    if (markerInfo.element) markerInfo.element.style.display = 'none';
                    this.clearMarkerLabelHitBounds(markerInfo);
                    return;
                }
                
                const pos = this.aladin.world2pix(markerInfo.ra, markerInfo.dec);
                if (pos) {
                    const aladinDiv = document.getElementById(this.aladin.aladinDiv.id);
                    if (!aladinDiv) return;
                    
                    const x = pos[0];
                    const y = pos[1];
                    
                    // Position the label based on the current mode
                    if (this.labelPosition === 'below') {
                        // Calculate offset based on marker size in pixels
                        const markerSizePx = markerInfo.markerSize.isEllipse 
                            ? Math.max(markerInfo.markerSize.width, markerInfo.markerSize.height) * 60 // Convert to arcmin
                            : (markerInfo.sizeDeg || 0.1) * 60; // Convert degrees to arcmin
                        
                        // Convert arcmin to pixels at current zoom level
                        const fov = this.aladin.getFov()[0]; // FOV in degrees
                        const containerHeight = aladinDiv.clientHeight;
                        const degreesPerPixel = fov / containerHeight;
                        const arcminPerPixel = degreesPerPixel * 60;
                        const offsetPx = (markerSizePx / arcminPerPixel) / 2 + 15; // Half the marker size + padding
                        
                        markerInfo.element.style.left = `${x}px`;
                        markerInfo.element.style.top = `${y + offsetPx}px`;
                        markerInfo.element.style.transform = 'translate(-50%, 0)';
                    } else {
                        // Keep the label anchored with a small fixed screen-space offset so
                        // zoom changes cannot fling it far away from the marker.
                        const computedFontSize = parseFloat(window.getComputedStyle(markerInfo.element).fontSize || '12');
                        const baseFontSize = Number.isFinite(computedFontSize) ? computedFontSize : 12;
                        const offsetX = Math.max(6, Math.min(10, baseFontSize * 0.55));
                        const offsetY = Math.max(4, Math.min(8, baseFontSize * 0.4));

                        markerInfo.element.style.left = `${x + offsetX}px`;
                        markerInfo.element.style.top = `${y - offsetY}px`;
                        markerInfo.element.style.transform = 'translate(0, -100%)';
                    }
                    
                    markerInfo.element.style.textAlign = 'center';
                    markerInfo.element.style.display = 'block';
                    
                    // Check if the label is within the visible area
                    const aladinRect = aladinDiv.getBoundingClientRect();
                    
                    // Hide if the marker is outside the viewport
                    if (x < 0 || x > aladinRect.width || y < 0 || y > aladinRect.height) {
                        markerInfo.element.style.display = 'none';
                        this.clearMarkerLabelHitBounds(markerInfo);
                        return;
                    }

                    this.syncMarkerLabelHitBounds(markerInfo, aladinRect);
                } else {
                    // Hide label if position is not valid
                    markerInfo.element.style.display = 'none';
                    this.clearMarkerLabelHitBounds(markerInfo);
                }
            },

            // Update all marker label positions
            updateAllMarkerLabels: function() {
                if (this._markerLabelsTemporarilyHidden) {
                    return;
                }

                const aladinDiv = document.getElementById(this.aladin?.aladinDiv?.id);
                if (!aladinDiv) {
                    return;
                }

                const fov = Math.max(...(this.aladin?.getFov?.() || [180, 180]));
                const labelConfig = (() => {
                    if (fov > 160) {
                        return { fontSize: 8.5, maxLabels: 42, maxOverlapRatio: 0.26 };
                    }
                    if (fov > 120) {
                        return { fontSize: 9, maxLabels: 58, maxOverlapRatio: 0.28 };
                    }
                    if (fov > 75) {
                        return { fontSize: 9.5, maxLabels: 84, maxOverlapRatio: 0.30 };
                    }
                    if (fov > 45) {
                        return { fontSize: 10, maxLabels: 120, maxOverlapRatio: 0.32 };
                    }
                    if (fov > 25) {
                        return { fontSize: 10.5, maxLabels: 168, maxOverlapRatio: 0.34 };
                    }
                    if (fov > 12) {
                        return { fontSize: 11, maxLabels: 230, maxOverlapRatio: 0.36 };
                    }
                    if (fov > 6) {
                        return { fontSize: 11.5, maxLabels: 320, maxOverlapRatio: 0.40 };
                    }

                    return { fontSize: 12, maxLabels: 420, maxOverlapRatio: 0.44 };
                })();

                const hostRect = aladinDiv.getBoundingClientRect();
                const areaFactor = Math.max(1, Math.min(2.1, (hostRect.width * hostRect.height) / (1280 * 720)));
                labelConfig.maxLabels = Math.max(12, Math.round(labelConfig.maxLabels * areaFactor));

                const boundsOverlap = (a, b) => {
                    if (!a || !b) {
                        return false;
                    }

                    return a.x < b.x + b.width
                        && a.x + a.width > b.x
                        && a.y < b.y + b.height
                        && a.y + a.height > b.y;
                };

                const getOverlapRatio = (a, b) => {
                    if (!boundsOverlap(a, b)) {
                        return 0;
                    }

                    const overlapWidth = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x));
                    const overlapHeight = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
                    const overlapArea = overlapWidth * overlapHeight;
                    const minArea = Math.max(1, Math.min(a.width * a.height, b.width * b.height));
                    return overlapArea / minArea;
                };

                const placedBounds = [];
                let renderedLabels = 0;
                this.labelPositions = [];
                const sortedMarkers = [...this.markers].sort((a, b) => {
                    const aSticky = a?.alwaysShowLabel === true ? 1 : 0;
                    const bSticky = b?.alwaysShowLabel === true ? 1 : 0;
                    if (aSticky !== bSticky) {
                        return bSticky - aSticky;
                    }

                    const aPopularity = Number.isFinite(a?.popularity) ? Number(a.popularity) : 0;
                    const bPopularity = Number.isFinite(b?.popularity) ? Number(b.popularity) : 0;
                    return bPopularity - aPopularity;
                });

                sortedMarkers.forEach((marker) => {
                    const shouldShow = ((this.showMarkerLabels || marker.alwaysShowLabel) && marker.renderLabel !== false);
                    marker.visible = shouldShow;

                    if (!shouldShow) {
                        if (marker.element) {
                            marker.element.style.display = 'none';
                        }
                        return;
                    }

                    if (!marker.element) {
                        this.createMarkerLabel(marker);
                    }

                    if (!marker.element) {
                        return;
                    }

                    if (!marker.alwaysShowLabel && renderedLabels >= labelConfig.maxLabels) {
                        marker.element.style.display = 'none';
                        return;
                    }

                    marker.element.style.fontSize = `${labelConfig.fontSize}px`;
                    marker.element.style.display = 'block';
                    this.updateMarkerLabelPosition(marker);

                    if (marker.element.style.display === 'none') {
                        return;
                    }

                    const rect = marker.element.getBoundingClientRect();
                    const bounds = {
                        x: rect.left - hostRect.left,
                        y: rect.top - hostRect.top,
                        width: rect.width,
                        height: rect.height
                    };

                    const inside = bounds.x >= 0
                        && bounds.y >= 0
                        && (bounds.x + bounds.width) <= hostRect.width
                        && (bounds.y + bounds.height) <= hostRect.height;
                    const overlaps = placedBounds.some(existing =>
                        getOverlapRatio(existing, bounds) > labelConfig.maxOverlapRatio);

                    if ((!marker.alwaysShowLabel && overlaps) || !inside) {
                        marker.element.style.display = 'none';
                        return;
                    }

                    this.labelPositions.push({
                        ...bounds,
                        source: marker.source
                    });
                    placedBounds.push(bounds);
                    renderedLabels += 1;
                });
            },

            // Toggle all marker labels
            setShowMarkerLabels: function(show) {
                this.showMarkerLabels = !!show;
                this.markers.forEach(marker => {
                    if (marker?.source?.data) {
                        marker.source.data.showLabel = (this.showMarkerLabels || marker.alwaysShowLabel) && marker.renderLabel !== false;
                    }

                    if (this.showMarkerLabels) {
                        if (!marker.element) {
                            this.createMarkerLabel(marker);
                        } else if (!this._markerLabelsTemporarilyHidden) {
                            marker.element.style.display = 'block';
                        }
                        if (!this._markerLabelsTemporarilyHidden) {
                            this.updateMarkerLabelPosition(marker);
                        }
                    } else if (marker.element) {
                        marker.element.style.display = 'none';
                    }
                });

                this.labelPositions = [];
                this._occupiedLabelBounds = [];
                this._renderedLabelCount = 0;
                this.aladin?.view?.requestRedraw?.();
            },
            
            // Set label position ('center' or 'below')
            setLabelPosition: function(position) {
                if (position === 'center' || position === 'below') {
                    this.labelPosition = position;
                    this.updateAllMarkerLabels();
                    return true;
                }
                return false;
            },
            
            // Toggle between center and below positions
            toggleLabelPosition: function() {
                this.setLabelPosition(this.labelPosition === 'center' ? 'below' : 'center');
            },
            
            // Remove a marker by its objId
            removeMarkerByObjId: function(objId) {
                if (!objId) return;
                
                // Find and remove the marker from our array
                const index = this.markers.findIndex(m => m.objId === objId);
                if (index !== -1) {
                    const markerInfo = this.markers[index];
                    
                    // Remove label element if exists
                    if (markerInfo.element && markerInfo.element.parentNode) {
                        markerInfo.element.parentNode.removeChild(markerInfo.element);
                    }
                    
                    // Remove from catalog if exists
                    if (this.dsoCatalog && markerInfo.source) {
                        try {
                            this.dsoCatalog.remove(markerInfo.source);
                        } catch (err) {
                            console.debug('Could not remove source from catalog:', err);
                        }
                    }
                    
                    // Remove from array
                    this.markers.splice(index, 1);
                }
            },
            
            // Clear all markers and their labels
            clearMarkers: function(deleteMarkers = true) {
                // Store label positions for hit testing
                this.labelPositions = [];

                // Remove the marker overlay if it exists
                if (this.markerOverlay) {
                    this.markerOverlay.remove();
                    this.markerOverlay = null;
                }

                // Remove all label elements
                this.markers.forEach(markerInfo => {
                    if (markerInfo.element && markerInfo.element.parentNode) {
                        markerInfo.element.parentNode.removeChild(markerInfo.element);
                    }
                });

                if (this.dsoCatalog) {
                    this.dsoCatalog.removeAll();

                    try {
                        this.aladin.removeCatalog(this.dsoCatalog.name);
                    } catch (err) { }

                    try {
                        this.aladin.removeOverlay(this.dsoCatalog.name);
                    } catch (err) { }
                    
                    this.dsoCatalog = null;
                }
                
                if (deleteMarkers == true)
                {
                    this.markers = this.markers.filter(marker => marker?.persistent === true);
                    if (this.markers.length > 0) {
                        this.addDSOMarkers(this.markers);
                    }
                }
            },

            ensurePoleReferenceMarkers: async function() {
                const hasNorthPole = this.markers.some(marker => marker?.objId === '__north_pole__');
                const hasSouthPole = this.markers.some(marker => marker?.objId === '__south_pole__');

                if (!hasNorthPole) {
                    await this.addMarker(0, 89.4, 0, {
                        label: 'North Pole',
                        type: 'Pole',
                        objId: '__north_pole__',
                        color: '#facc15',
                        sizeArcminMin: 18,
                        sizeArcminMax: 18,
                        alwaysShowLabel: true,
                        persistent: true
                    });
                }

                if (!hasSouthPole) {
                    await this.addMarker(0, -89.4, 0, {
                        label: 'South Pole',
                        type: 'Pole',
                        objId: '__south_pole__',
                        color: '#f97316',
                        sizeArcminMin: 18,
                        sizeArcminMax: 18,
                        alwaysShowLabel: true,
                        persistent: true
                    });
                }
            },
                        
            // Set the survey (background image) using the standard Aladin survey-switching flow.
            setSurvey: async function(surveyId) {
                try {
                    const nextSurveyId = surveyId || this._currentSurveyId;
                    if (!nextSurveyId || !this.aladin) {
                        return Promise.resolve();
                    }

                    this._currentSurveyId = nextSurveyId;

                    if (this.aladin.__amLegacyBuild) {
                        const legacySurveyTarget = normalizeSurveyForBuild(nextSurveyId, { legacy: true });
                        const legacySurveyLayer = await this.createSurveyLayer(legacySurveyTarget);
                        this.aladin.setImageSurvey(legacySurveyLayer || legacySurveyTarget);
                        this._currentSurveyLayerQualityKey = `${legacySurveyTarget}|legacy`;
                        setTimeout(() => {
                            this.resize(true).catch(() => {});
                        }, 80);
                        return Promise.resolve();
                    }

                    const surveyTarget = this.isDirectHipsUrl(nextSurveyId)
                        ? A.imageHiPS(nextSurveyId, { name: this.getSurveyDisplayName(nextSurveyId) })
                        : (this.isRegistrySurveyId(nextSurveyId) ? this.normalizeRegistrySurveyId(nextSurveyId) : nextSurveyId);

                    this.aladin.setImageSurvey(surveyTarget);

                    setTimeout(() => {
                        this.resize(true).catch(() => {});
                        if (this.highQualityMode) {
                            this.queueHighQualityRefresh('force');
                        }
                    }, 80);

                    return Promise.resolve();
                } catch (e) {
                    console.error('Error setting survey:', e);
                    return Promise.reject(e);
                }
            },
            
            // Get current view parameters (RA, Dec, FOV)
            getViewParams: function() {
                const center = aladin.getRaDec();
                const fov = aladin.getFov(); // This returns [width, height]

                return {
                    ra: center[0],
                    dec: center[1],
                    fov: fov[0]
                };
            },
            
            // Zoom in
            zoomIn: async function() {
                try {
                    const currentZoom = this.aladin.getFov()[0];
                    this.aladin.setZoomFactor(currentZoom * 0.7); // Zoom in by reducing FOV
                    return Promise.resolve();
                } catch (e) {
                    console.error('Error zooming in:', e);
                    return Promise.reject(e);
                }
            },
            
            // Zoom out
            zoomOut: async function() {
                try {
                    const currentZoom = this.aladin.getFov()[0];
                    this.aladin.setZoomFactor(currentZoom * 1.3); // Zoom out by increasing FOV
                    return Promise.resolve();
                } catch (e) {
                    console.error('Error zooming out:', e);
                    return Promise.reject(e);
                }
            },
            
            // Destroy the Aladin instance
            destroy: async function() {
                try {
                    // Clear markers first
                    await this.clearMarkers();
                    
                    // Destroy the Aladin instance
                    if (this.aladin) {
                        this.aladin.destroy();
                    }
                    
                    return Promise.resolve();
                } catch (e) {
                    console.error('Error destroying Aladin instance:', e);
                    return Promise.reject(e);
                }
            },
            
            // Show/hide grid
            showGrid: async function(isVisible) {
                try {
                    this.aladin.setCooGrid({ enabled: isVisible });

                    return Promise.resolve();
                } catch (e) {
                    console.error('Error showing/hiding grid:', e);
                    return Promise.reject(e);
                }
            },
            
            // Show/hide reticle
            showAladinReticle: async function(isVisible) {
                try {
                    this.aladin.showReticle(isVisible);

                    return Promise.resolve();
                } catch (e) {
                    console.error('Error showing/hiding reticle:', e);
                    return Promise.reject(e);
                }
            },

            showConstellations: async function(isVisible) {
                try {
                    this.showConstellationsActive = !!isVisible;

                    if (!isVisible) {
                        this.highlightedConstellationId = null;
                        this.hideConstellationLabels();
                        if (this.constellationCanvasLayer) {
                            this.clearConstellationCanvasLayer();
                            this.constellationCanvasLayer.style.display = 'none';
                        }
                        if (this.constellationLabelLayer) {
                            this.constellationLabelLayer.style.display = 'none';
                        }
                        if (this.constellationOverlay && typeof this.constellationOverlay.hide === 'function') {
                            this.constellationOverlay.hide();
                        }
                        if (this.constellationHighlightOverlay && typeof this.constellationHighlightOverlay.hide === 'function') {
                            this.constellationHighlightOverlay.hide();
                        }
                        if (this.constellationLabelCatalog && typeof this.constellationLabelCatalog.hide === 'function') {
                            this.constellationLabelCatalog.hide();
                        }
                        this.updateConstellationDebugPanel({
                            visible: false,
                            pathsDrawn: 0,
                            visibleLabelCount: 0
                        });

                        return Promise.resolve();
                    }

                    const lineLayer = this.ensureConstellationCanvasLayer();
                    const labelLayer = this.ensureConstellationLabelLayer();
                    if (lineLayer) {
                        lineLayer.style.display = 'block';
                    }
                    if (labelLayer) {
                        labelLayer.style.display = 'block';
                    }

                    if (this.constellationOverlay && typeof this.constellationOverlay.hide === 'function') {
                        this.constellationOverlay.hide();
                    }
                    if (this.constellationHighlightOverlay && typeof this.constellationHighlightOverlay.hide === 'function') {
                        this.constellationHighlightOverlay.hide();
                    }
                    if (this.constellationLabelCatalog && typeof this.constellationLabelCatalog.hide === 'function') {
                        this.constellationLabelCatalog.hide();
                    }

                    await this.updateConstellationCanvasLayer();
                    await this.updateConstellationLabelPositions();

                    return Promise.resolve();
                } catch (e) {
                    console.error('Error showing/hiding constellations:', e);
                    return Promise.reject(e);
                }
            },

            // Multiple FOV Overlay Management
            updateFovOverlays: async function(overlays) {
                try {
                                        // Clear existing overlays
                    this.clearAllFovOverlays();
                    
                    // Add new overlays
                    if (overlays && overlays.length > 0) {
                        overlays.forEach(overlay => {
                            if (overlay.isVisible) {
                                this.addFovOverlay(overlay);
                            }
                        })
                    }
                    
                    return Promise.resolve();
                } catch (e) {
                    console.error('Error updating FOV overlays:', e);
                    return Promise.reject(e);
                }
            },

            addFovOverlay: function(overlay) {
                try {
                    const container = this.aladin.aladinDiv;
                    if (!container) return;

                    // Create overlay element
                    const overlayElement = document.createElement('div');
                    overlayElement.id = `fov-overlay-${overlay.id}`;
                    overlayElement.className = 'fov-overlay';
                    overlayElement.style.cssText = `
                        position: absolute;
                        border: 2px dashed ${overlay.color};
                        background: transparent;
                        pointer-events: none;
                        z-index: 10;
                        transform-origin: center center;
                    `;

                    container.appendChild(overlayElement);
                    
                    // Store overlay data with unique identifier
                    this.fovOverlays.push({
                        id: overlay.id,
                        data: overlay,
                        element: overlayElement
                    });
                    
                    this.fovOverlayElements.set(overlay.id, overlayElement);
                    
                    // Update overlay position and size
                    this.updateFovOverlayPosition(overlay.id);
                    
                } catch (e) {
                    console.error('Error adding FOV overlay:', e);
                }
            },

            removeFovOverlay: function(overlayId) {
                try {
                    const element = this.fovOverlayElements.get(overlayId);
                    if (element && element.parentNode) {
                        element.parentNode.removeChild(element);
                    }
                    
                    this.fovOverlayElements.delete(overlayId);
                    this.fovOverlays = this.fovOverlays.filter(o => o.id !== overlayId);
                    
                } catch (e) {
                    console.error('Error removing FOV overlay:', e);
                }
            },

            clearAllFovOverlays: function() {
                try {
                    this.fovOverlays.forEach(overlay => {
                        // If overlay has a container wrapper, remove the container
                        // But only if the container is a fov-container, not the Aladin div
                        if (overlay.container && overlay.container.classList.contains('fov-container')) {
                            overlay.container.remove();
                        } else if (overlay.element) {
                            // Fallback: just remove the element itself
                            overlay.element.remove();
                        }
                    });
                    
                    this.fovOverlayElements.clear();
                    this.fovOverlays = [];
                    
                } catch (e) {
                    console.error('Error clearing FOV overlays:', e);
                }
            },


            // Remove image layer
            removeImageLayer: function(layer) {
                this.aladin.removeImageLayer(layer);
            },

            updateAllFovOverlayPositions: function() {
                this.updateFovOverlayPosition();
            },


            updateFovOverlayPosition: function (overlayId = null) {
                try {
                    // Check if projection is ready before attempting coordinate conversions
                    try {
                        const testCoords = this.aladin.pix2world(100, 100);
                        if (!testCoords || !Array.isArray(testCoords)) return;
                    } catch (e) {
                        return; // Projection not ready, skip silently
                    }
                    
                    const overlaysToUpdate = overlayId
                        ? this.fovOverlays.filter(o => o.id === overlayId)
                        : this.fovOverlays;

                    overlaysToUpdate.forEach(overlay => {
                        const element = overlay.element;
                        const data = overlay.data;

                        if (!element || !data) return;

                        // Get overlay position - use locked position if available, otherwise current view center
                        let overlayRA, overlayDec;
                        if (data.isPositionLocked && 
                            typeof data.lockedRA === 'number' && 
                            typeof data.lockedDec === 'number' && 
                            !isNaN(data.lockedRA) && 
                            !isNaN(data.lockedDec) &&
                            (data.lockedRA !== 0 || data.lockedDec !== 0)) {
                            // Use locked position
                            overlayRA = data.lockedRA;
                            overlayDec = data.lockedDec;
                        } else {
                            // Use current view center
                            const raDec = this.aladin.getRaDec();
                            overlayRA = raDec[0];
                            overlayDec = raDec[1];
                        }

                        const fov = this.aladin.getFov();
                        const containerWidth = this.aladin.aladinDiv.offsetWidth;
                        const containerHeight = this.aladin.aladinDiv.offsetHeight;
                        
                        // Calculate overlay position in pixels
                        let overlayX, overlayY;
                        if (data.isPositionLocked && 
                            typeof data.lockedRA === 'number' && 
                            typeof data.lockedDec === 'number' && 
                            !isNaN(data.lockedRA) && 
                            !isNaN(data.lockedDec) &&
                            (data.lockedRA !== 0 || data.lockedDec !== 0)) {
                            // Convert locked RA/Dec to pixel coordinates
                            const lockedPixelCoords = this.aladin.world2pix(overlayRA, overlayDec);
                            if (lockedPixelCoords && lockedPixelCoords.length >= 2) {
                                overlayX = lockedPixelCoords[0];
                                overlayY = lockedPixelCoords[1];
                            } else {
                                // Hide overlay when it's on the back side of the spherical globe
                                element.style.display = 'none';
                                return; // Skip further processing for this overlay
                            }
                        } else {
                            // Position overlay at center of view
                            overlayX = containerWidth / 2;
                            overlayY = containerHeight / 2;
                        }

                        const clampDec = (value) => Math.max(-89.9, Math.min(89.9, value));
                        const projectDistancePx = (ra1, dec1, ra2, dec2) => {
                            try {
                                const start = this.aladin.world2pix(ra1, dec1);
                                const end = this.aladin.world2pix(ra2, dec2);
                                if (!start || !end || start.length < 2 || end.length < 2) {
                                    return null;
                                }

                                return Math.hypot(end[0] - start[0], end[1] - start[1]);
                            } catch (error) {
                                return null;
                            }
                        };

                        const widthDegrees = (data.widthArcmin || 0) / 60;
                        const heightDegrees = (data.heightArcmin || 0) / 60;
                        const halfWidthDegrees = widthDegrees / 2;
                        const halfHeightDegrees = heightDegrees / 2;
                        const cosDec = Math.max(Math.cos((overlayDec * Math.PI) / 180), 0.05);
                        const halfWidthRaDegrees = halfWidthDegrees / cosDec;

                        const projectedWidthPx = projectDistancePx(
                            overlayRA - halfWidthRaDegrees,
                            overlayDec,
                            overlayRA + halfWidthRaDegrees,
                            overlayDec
                        );
                        const projectedHeightPx = projectDistancePx(
                            overlayRA,
                            clampDec(overlayDec - halfHeightDegrees),
                            overlayRA,
                            clampDec(overlayDec + halfHeightDegrees)
                        );

                        const fallbackWidthPx = widthDegrees * (containerWidth / Math.max(fov[0], 0.0001));
                        const fallbackHeightPx = heightDegrees * (containerHeight / Math.max(fov[1] || fov[0], 0.0001));
                        const overlayWidthPx = projectedWidthPx || fallbackWidthPx;
                        const overlayHeightPx = projectedHeightPx || fallbackHeightPx;

                        // Create or update the unified container for FOV + label
                        let container = overlay.container;
                        if (!container) {
                            // Create container div that holds both FOV overlay and label
                            container = document.createElement('div');
                            container.className = 'fov-container';
                            container.style.position = 'absolute';
                            container.style.pointerEvents = 'none';
                            container.style.transformOrigin = 'center center';
                            
                            // Move the existing FOV element into the container
                            const parent = element.parentNode;
                            parent.insertBefore(container, element);
                            container.appendChild(element);
                            
                            // Create label element within the container
                            const labelElement = document.createElement('div');
                            labelElement.className = 'fov-label';
                            labelElement.style.position = 'absolute';
                            labelElement.style.color = 'white';
                            labelElement.style.fontSize = '12px';
                            labelElement.style.fontWeight = 'bold';
                            labelElement.style.textShadow = '1px 1px 2px rgba(0,0,0,0.8)';
                            labelElement.style.whiteSpace = 'nowrap';
                            labelElement.style.pointerEvents = 'none';
                            labelElement.textContent = data.labelText || '';
                            
                            container.appendChild(labelElement);
                            
                            // Store references
                            overlay.container = container;
                            overlay.labelElement = labelElement;
                        }
                        
                        // Position the container at the calculated position
                        container.style.left = `${overlayX}px`;
                        container.style.top = `${overlayY}px`;
                        container.style.transform = `rotate(${data.rotationDegrees}deg)`;
                        container.style.display = data.isVisible ? 'block' : 'none';
                        
                        // Position FOV overlay within container (centered)
                        element.style.width = `${overlayWidthPx}px`;
                        element.style.height = `${overlayHeightPx}px`;
                        element.style.left = `${-overlayWidthPx / 2}px`;
                        element.style.top = `${-overlayHeightPx / 2}px`;
                        element.style.transform = 'none'; // No rotation needed, container handles it
                        element.style.borderColor = data.color;
                        
                        // Position label within container (switch sides based on rotation to keep text readable)
                        const labelElement = overlay.labelElement;
                        if (labelElement) {
                            labelElement.textContent = data.labelText || '';
                            labelElement.style.left = '50%';
                            labelElement.style.color = data.color; // Use same color as FOV overlay
                            
                            // Position label based on rotation to keep text readable
                            const rotation = (data.rotationDegrees || 0) % 360;
                            const normalizedRotation = rotation < 0 ? rotation + 360 : rotation;
                            
                            // If rotation would make text upside down (90° to 270°), flip to opposite side
                            if (normalizedRotation > 90 && normalizedRotation < 270) {
                                labelElement.style.top = `${-overlayHeightPx / 2 - 25}px`; // 25px above FOV top edge
                                // Add 180° rotation to flip text right-side up when on opposite side
                                labelElement.style.transform = `translateX(-50%) rotate(180deg)`;
                            } else {
                                labelElement.style.top = `${overlayHeightPx / 2 + 10}px`; // 10px below FOV bottom edge
                                // No additional rotation needed
                                labelElement.style.transform = `translateX(-50%)`;
                            }
                        }
                    });

                } catch (e) {
                    console.error('Error updating FOV overlay positions:', e);
                }
            },
            // Show/hide all markers
            setMarkersVisible: function(visible) {
                try {
                    if (this.dsoCatalog) {
                        if (visible) {
                            this.dsoCatalog.show();
                        } else {
                            this.dsoCatalog.hide();
                        }
                        
                        // Toggle marker labels visibility
                        // this.setShowMarkerLabels(visible);
                        
                        // Trigger a redraw
                        //this.aladin.repaint();
                    }
                    return Promise.resolve();
                } catch (e) {
                    console.error('Error toggling markers visibility:', e);
                    return Promise.reject(e);
                }
            }
        };
        
        // Initial update
        updateCoordinates();
        
        // Add resize observer to update overlays when container resizes
        if (window.ResizeObserver) {
            const resizeObserver = new ResizeObserver(() => {
                try {
                    aladinInstance.resize();

                    // Check if projection is ready before updating overlays
                    const testCoords = aladinInstance.aladin.pix2world(100, 100);
                    if (!testCoords || !Array.isArray(testCoords)) return;
                    
                    aladinInstance.scheduleVisualRefresh({
                        fov: true,
                        labels: true,
                        mosaic: true,
                        astronomical: true
                    });
                } catch (e) {
                    // Projection not ready yet, skip this update
                }
            });
            resizeObserver.observe(aladinInstance.aladin.aladinDiv);

            const hostContainer = aladinInstance.aladin.aladinDiv?.parentElement;
            if (hostContainer) {
                resizeObserver.observe(hostContainer);
            }
        }

        // Masonry and card layouts can settle after init; force a few deferred sync passes
        requestAnimationFrame(() => {
            aladinInstance.resize(true);
        });
        setTimeout(() => {
            aladinInstance.resize(true);
        }, 120);
        setTimeout(() => {
            aladinInstance.resize(true);
        }, 350);
        setTimeout(() => {
            aladinInstance.setSurvey(settings.survey).catch(error => {
                console.warn('SkyAtlas initial survey setup failed:', error);
            });
            aladinInstance.scheduleResolutionWarmup('init');
        }, 520);

        const viewportResizeHandler = () => {
            pushTrace('window:resize-or-orientationchange', {
                viewportWidth: window.innerWidth || document.documentElement?.clientWidth || 0,
                viewportHeight: window.innerHeight || document.documentElement?.clientHeight || 0
            });
            aladinInstance.layoutCustomButtons();
            aladinInstance.resize(true).catch(() => {});
        };
        window.addEventListener('resize', viewportResizeHandler, { passive: true });
        window.addEventListener('orientationchange', viewportResizeHandler, { passive: true });

        const preventGestureRotation = (event) => {
            if (event && typeof event.preventDefault === 'function') {
                event.preventDefault();
            }

            if (aladin?.setRotation) {
                requestAnimationFrame(() => aladin.setRotation(0));
            }
        };

        if (containerElement) {
            containerElement.addEventListener('gesturestart', preventGestureRotation, { passive: false });
            containerElement.addEventListener('gesturechange', preventGestureRotation, { passive: false });
            containerElement.addEventListener('gestureend', preventGestureRotation, { passive: false });
            containerElement.addEventListener('touchstart', (event) => {
                if (event.touches && event.touches.length > 1) {
                    aladinInstance._isTouchZooming = true;
                    aladinInstance._pendingInteractionWarmup = true;
                    if (aladinInstance._resolutionWarmupTimer) {
                        clearTimeout(aladinInstance._resolutionWarmupTimer);
                        aladinInstance._resolutionWarmupTimer = null;
                    }
                }
            }, { passive: true });
            containerElement.addEventListener('touchmove', (event) => {
                if (event.touches && event.touches.length > 1 && aladin?.setRotation) {
                    aladinInstance._isTouchZooming = true;
                    aladinInstance._pendingInteractionWarmup = true;
                    requestAnimationFrame(() => aladin.setRotation(0));
                }
            }, { passive: true });
            containerElement.addEventListener('touchend', () => {
                if (aladinInstance._isTouchZooming) {
                    aladinInstance._isTouchZooming = false;
                    aladinInstance.queueHighQualityRefresh('interaction');
                }
            }, { passive: true });
            containerElement.addEventListener('touchcancel', () => {
                if (aladinInstance._isTouchZooming) {
                    aladinInstance._isTouchZooming = false;
                    aladinInstance.queueHighQualityRefresh('interaction');
                }
            }, { passive: true });
        }
        
        // Extend Aladin instance with mosaic functionality
        if (typeof window.extendAladinWithMosaic === 'function') {
            window.extendAladinWithMosaic(aladinInstance);
        } else {
            console.warn('extendAladinWithMosaic function not available - mosaic functionality will not work');
        }
        
        // Extend Aladin instance with astronomical functionality
        if (typeof window.extendAladinWithAstronomical === 'function') {
            window.extendAladinWithAstronomical(aladinInstance);
        } else {
            console.warn('extendAladinWithAstronomical function not available - astronomical functionality will not work');
        }

        const notifyAtlasReadyWhenProjectionUsable = () => {
            let attempts = 0;
            const maxAttempts = 120;

            const probeReady = () => {
                attempts += 1;
                try {
                    const testCoords = aladinInstance.aladin?.pix2world?.(100, 100);
                    const hostRect = aladinInstance.aladin?.aladinDiv?.getBoundingClientRect?.();
                    const hasProjection = Array.isArray(testCoords)
                        && testCoords.length >= 2
                        && Number.isFinite(testCoords[0])
                        && Number.isFinite(testCoords[1]);
                    const hasSize = !!hostRect && hostRect.width > 0 && hostRect.height > 0;

                    if (hasProjection && hasSize) {
                        dotNetHelper?.invokeMethodAsync('NotifyAtlasReadyAsync').catch(() => {});
                        return;
                    }
                } catch {
                    // Projection is not usable yet; keep polling.
                }

                if (attempts < maxAttempts) {
                    requestAnimationFrame(probeReady);
                }
            };

            requestAnimationFrame(probeReady);
        };

        notifyAtlasReadyWhenProjectionUsable();

        await aladinInstance.ensurePoleReferenceMarkers();
        
        // Add global functions for Blazor to call - these get refreshed on each initialization
        window.getAladinPosition = function() { return window.aladinInstance && window.aladinInstance.getPosition ? window.aladinInstance.getPosition() : null; };
        window.getAladinFov = function() { return window.aladinInstance && window.aladinInstance.getFov ? window.aladinInstance.getFov() : null; };
        window.getAladinViewState = function() { return window.aladinInstance && window.aladinInstance.getViewState ? window.aladinInstance.getViewState() : null; };
        window.addAladinMarker = function(ra, dec, pa, label, color, sizeArcminMin, sizeArcminMax, type, popularity, objId) {
            if (window.aladinInstance && window.aladinInstance.addMarker) {
                const normalizedPa = Number.isFinite(pa) && Math.abs(pa) > 0.01 ? pa : null;
                return window.aladinInstance.addMarker(ra, dec, normalizedPa, { 
                    label: label, 
                    color: color, 
                    sizeArcminMin: sizeArcminMin,
                    sizeArcminMax: sizeArcminMax,
                    type: type,
                    popularity: popularity,
                    objId: objId
                });
            }
            return null;
        };
        window.addAladinMarkersBatch = function(markers) {
            if (!window.aladinInstance || !window.aladinInstance.addMarker || !Array.isArray(markers)) {
                return;
            }

            markers.forEach(marker => {
                const normalizedPa = Number.isFinite(marker.pa) && Math.abs(marker.pa) > 0.01 ? marker.pa : null;
                window.aladinInstance.addMarker(marker.ra, marker.dec, normalizedPa, {
                    label: marker.label,
                    color: marker.color,
                    sizeArcminMin: marker.sizeArcminMin,
                    sizeArcminMax: marker.sizeArcminMax,
                    type: marker.type,
                    popularity: marker.popularity,
                    objId: marker.objId,
                    renderLabel: marker.renderLabel !== false,
                    selectOnTextClick: marker.selectOnTextClick === true
                });
            });

            if (window.aladinInstance.showConstellationsActive === true &&
                typeof window.aladinInstance.updateConstellationCanvasLayer === 'function') {
                Promise.resolve(window.aladinInstance.updateConstellationCanvasLayer()).catch(() => {});
                Promise.resolve(window.aladinInstance.updateConstellationLabelPositions?.()).catch(() => {});
            }

            window.aladinInstance.scheduleVisualRefresh?.({
                labels: true,
                fov: true,
                astronomical: true
            });
            window.aladinInstance.aladin?.view?.requestRedraw?.();
        };
        window.clearAladinMarkers = function() { if (window.aladinInstance && window.aladinInstance.clearMarkers) window.aladinInstance.clearMarkers(); };
        window.removeAladinMarkerByObjId = function(objId) { if (window.aladinInstance && window.aladinInstance.removeMarkerByObjId) window.aladinInstance.removeMarkerByObjId(objId); };
        window.setMarkerLabelsVisible = function(visible) { if (window.aladinInstance && window.aladinInstance.setShowMarkerLabels) window.aladinInstance.setShowMarkerLabels(visible); };
        window.toggleMarkerLabels = function() { if (window.aladinInstance && window.aladinInstance.toggleMarkerLabels) window.aladinInstance.toggleMarkerLabels(); };
        window.updateAladinCheckboxButton = function(buttonId, isChecked) { if (window.aladinInstance && window.aladinInstance.updateCheckboxButton) window.aladinInstance.updateCheckboxButton(buttonId, isChecked); };
        window.updateButtonBadge = function(buttonId, count) { if (window.aladinInstance && window.aladinInstance.updateButtonBadge) window.aladinInstance.updateButtonBadge(buttonId, count); };
        window.handleAladinMarkerPopupSelect = function(event, objId) {
            if (event?.stopPropagation) {
                event.stopPropagation();
            }

            const popup = event?.target?.closest?.('.aladin-popup');
            if (popup) {
                popup.style.display = 'none';
            }

            window.aladinInstance?.invokeMarkerSelection?.(objId);
        };
        window.handleAladinMarkerPopupTextClick = function(event, objId, selectOnTextClick) {
            if (event?.stopPropagation) {
                event.stopPropagation();
            }

            const shouldSelect = selectOnTextClick === true
                || (typeof objId === 'string' && (objId.startsWith('target:') || objId.startsWith('scheduled:') || objId.startsWith('community:')));

            if (shouldSelect) {
                const popup = event?.target?.closest?.('.aladin-popup');
                if (popup) {
                    popup.style.display = 'none';
                }

                window.aladinInstance?.invokeMarkerSelection?.(objId);
            }
        };
        window.handleAladinMarkerPopupDetails = function(event, identifier) {
            if (event?.stopPropagation) {
                event.stopPropagation();
            }

            window.aladinInstance?.dotNetHelper?.invokeMethodAsync('ViewDSODetails', identifier)
                .catch(error => console.error('Error invoking ViewDSODetails:', error));
        };
        window.getSkyAtlasDebugTrace = function() {
            return window.__amSkyAtlasTrace || [];
        };
        window.pushSkyAtlasDebugTrace = function(eventName, payload) {
            pushTrace(eventName, payload || {});
        };

        const activateGlobalFromContainerInteraction = () => aladinInstance.activateAsGlobalInstance();
        const aladinContainer = document.getElementById(containerId);
        if (aladinContainer) {
            aladinContainer.addEventListener('pointerdown', activateGlobalFromContainerInteraction, { passive: true });
            aladinContainer.addEventListener('mouseenter', activateGlobalFromContainerInteraction);
            aladinContainer.addEventListener('touchstart', activateGlobalFromContainerInteraction, { passive: true });
        }
        
        // Store global reference for mosaic functions
        window.aladinInstance = aladinInstance;
        
        // Return the Aladin instance
        return aladinInstance;
    } catch (error) {
        console.error('Error initializing Aladin Lite:', error);
        throw error;
    }
}

// Update coordinate display in the UI
export function updateCoordinateDisplay(ra, dec, fov) {
    const raDisplay = document.getElementById('ra-display');
    const decDisplay = document.getElementById('dec-display');
    const fovDisplay = document.getElementById('fov-display');
    
    if (raDisplay) {
        // Format RA as HH:MM:SS
        const raHours = Math.floor(ra / 15);
        const raMinutes = Math.floor((ra / 15 - raHours) * 60);
        const raSeconds = ((ra / 15 - raHours - raMinutes / 60) * 3600).toFixed(1);
        raDisplay.textContent = `${raHours}h ${raMinutes}m ${raSeconds}s`;
    }
    
    if (decDisplay) {
        // Format Dec as DD°MM'SS"
        const decSign = dec >= 0 ? '+' : '-';
        const decAbs = Math.abs(dec);
        const decDeg = Math.floor(decAbs);
        const decMinutes = Math.floor((decAbs - decDeg) * 60);
        const decSeconds = ((decAbs - decDeg - decMinutes / 60) * 3600).toFixed(0);
        decDisplay.textContent = `${decSign}${decDeg}° ${decMinutes}' ${decSeconds}"`;
    }
    
    if (fovDisplay) {
        // Format FOV
        if (fov >= 1) {
            fovDisplay.textContent = `${fov.toFixed(1)}°`;
        } else if (fov >= 1/60) {
            fovDisplay.textContent = `${(fov * 60).toFixed(1)}'`;
        } else {
            fovDisplay.textContent = `${(fov * 3600).toFixed(1)}"`;
        }
    }
}

// Add mount position marker for session mode
export function addMountPositionMarker(aladinInstance, raDegrees, dec) {
    if (!aladinInstance) return;
    
    try {
        // Create a catalog for the mount position marker
        const catalog = A.catalog({ name: 'Mount Position', sourceSize: 18, color: '#00FF00' });
        aladinInstance.addCatalog(catalog);
        
        // Format coordinates for display
        const raHours = raDegrees / 15;
        const raH = Math.floor(raHours);
        const raM = Math.floor((raHours - raH) * 60);
        const raS = ((raHours - raH) * 60 - raM) * 60;
        const raStr = `${raH}h ${raM}m ${raS.toFixed(1)}s`;
        
        const decSign = dec >= 0 ? '+' : '-';
        const decAbs = Math.abs(dec);
        const decD = Math.floor(decAbs);
        const decM = Math.floor((decAbs - decD) * 60);
        const decS = ((decAbs - decD) * 60 - decM) * 60;
        const decStr = `${decSign}${decD}° ${decM}' ${decS.toFixed(1)}"`;
        
        // Add marker at mount position
        catalog.addSources([A.marker(raDegrees, dec, {
            popupTitle: 'Mount Position',
            popupDesc: `RA: ${raStr}<br>Dec: ${decStr}`
        })]);
    } catch (error) {
        console.error('Error adding mount position marker:', error);
    }
}

// ... rest of the code remains the same ...
