        const toCompassBearing = (suncalcAzimuthRad) => {
            const deg = suncalcAzimuthRad * (180 / Math.PI);
            return (deg + 180) % 360;
        };

        const calculateDistanceAndBearing = (lat1, lng1, lat2, lng2) => {
            const a = 6378137, b = 6356752.314245, f = 1 / 298.257223563;
            const L = (lng2 - lng1) * Math.PI / 180;
            const U1 = Math.atan((1 - f) * Math.tan(lat1 * Math.PI / 180));
            const U2 = Math.atan((1 - f) * Math.tan(lat2 * Math.PI / 180));
            const sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
            const sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
            let lambda = L, lambdaP, iterLimit = 100;
            let sinLambda, cosLambda, sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM;
            do {
                sinLambda = Math.sin(lambda);
                cosLambda = Math.cos(lambda);
                sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
                if (sinSigma === 0) return { distance: 0, bearing: 0 }; 
                cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
                sigma = Math.atan2(sinSigma, cosSigma);
                sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
                cosSqAlpha = 1 - sinAlpha * sinAlpha;
                cos2SigmaM = cosSqAlpha === 0 ? 0 : cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
                const C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
                lambdaP = lambda;
                lambda = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
            } while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);
            
            if (iterLimit === 0) {
                const R = 6371e3;
                const dLat = (lat2-lat1) * Math.PI/180;
                const dLng = (lng2-lng1) * Math.PI/180;
                const alpha = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1*Math.PI/180) * Math.cos(lat2*Math.PI/180) * Math.sin(dLng/2) * Math.sin(dLng/2);
                const c = 2 * Math.atan2(Math.sqrt(alpha), Math.sqrt(1-alpha));
                const dist = R * c;
                const y = Math.sin(dLng) * Math.cos(lat2*Math.PI/180);
                const x = Math.cos(lat1*Math.PI/180)*Math.sin(lat2*Math.PI/180) - Math.sin(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.cos(dLng);
                const brng = (Math.atan2(y, x) * 180 / Math.PI + 360) % 360;
                return { distance: dist, bearing: brng };
            }
            
            const uSq = cosSqAlpha * (a * a - b * b) / (b * b);
            const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
            const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
            const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
            const distance = b * A * (sigma - deltaSigma);
            const initialBearing = Math.atan2(cosU2 * sinLambda, cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
            return { distance, bearing: (initialBearing * 180 / Math.PI + 360) % 360 };
        };

        const calculateDistance = (lat1, lng1, lat2, lng2) => calculateDistanceAndBearing(lat1, lng1, lat2, lng2).distance;
        const calculateBearing = (lat1, lng1, lat2, lng2) => calculateDistanceAndBearing(lat1, lng1, lat2, lng2).bearing;

        const computeDestinationPoint = (lat, lng, distanceKm, bearingDegrees) => {
            const a = 6378137, b = 6356752.314245, f = 1 / 298.257223563;
            const s = distanceKm * 1000;
            const alpha1 = bearingDegrees * Math.PI / 180;
            const sinAlpha1 = Math.sin(alpha1), cosAlpha1 = Math.cos(alpha1);
            const tanU1 = (1 - f) * Math.tan(lat * Math.PI / 180);
            const cosU1 = 1 / Math.sqrt(1 + tanU1 * tanU1), sinU1 = tanU1 * cosU1;
            const sigma1 = Math.atan2(tanU1, cosAlpha1);
            const sinAlpha = cosU1 * sinAlpha1;
            const cosSqAlpha = 1 - sinAlpha * sinAlpha;
            const uSq = cosSqAlpha * (a * a - b * b) / (b * b);
            const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
            const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
            
            let sigma = s / (b * A), sigmaP = 2 * Math.PI;
            let cos2SigmaM, sinSigma, cosSigma;
            
            while (Math.abs(sigma - sigmaP) > 1e-12) {
                cos2SigmaM = Math.cos(2 * sigma1 + sigma);
                sinSigma = Math.sin(sigma);
                cosSigma = Math.cos(sigma);
                const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
                sigmaP = sigma;
                sigma = s / (b * A) + deltaSigma;
            }
            
            const tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
            const lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
            const lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
            const C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
            const L = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
            
            return [lat2 * 180 / Math.PI, (lng * Math.PI / 180 + L) * 180 / Math.PI];
        };

        const getDelta = (az1, az2) => {
            if (az1 === null || az2 === null || isNaN(az1) || isNaN(az2)) return null;
            let diff = Math.abs(az1 - az2) % 360;
            return diff > 180 ? 360 - diff : diff;
        };

        const getRelativeAz = (az, centerAz) => {
            let diff = az - centerAz;
            while (diff < -180) diff += 360;
            while (diff > 180) diff -= 360;
            return diff;
        };

        const formatTimeStr = (dateObj) => {
            if (!dateObj || isNaN(dateObj.getTime())) return "--:--:--";
            return dateObj.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
        };
        
        const formatDeg = (deg) => {
            if (deg === null || isNaN(deg)) return "--°";
            return `${Math.round(deg)}°`;
        };

        const getPhaseName = (p) => {
            if (p === 0 || p === 1) return "New Moon";
            if (p < 0.25) return "Waxing Crescent";
            if (p === 0.25) return "First Quarter";
            if (p < 0.5) return "Waxing Gibbous";
            if (p === 0.5) return "Full Moon";
            if (p < 0.75) return "Waning Gibbous";
            if (p === 0.75) return "Last Quarter";
            return "Waning Crescent";
        };

        const getSensorDims = (type) => {
            switch(type) {
                case 'APSC-N': return {w: 23.6, h: 15.6};
                case 'APSC-C': return {w: 22.2, h: 14.8};
                case 'M43': return {w: 17.3, h: 13.0};
                default: return {w: 36, h: 24}; // Full Frame
            }
        };

        const getCoC = (type) => {
            switch(type) {
                case 'APSC-N': return 0.020;
                case 'APSC-C': return 0.019;
                case 'M43': return 0.015;
                default: return 0.030; 
            }
        };

        const getLST = (date, lng) => {
            const jd = date.getTime() / 86400000 + 2440587.5;
            const d = jd - 2451545.0;
            let gmst = 280.46061837 + 360.98564736629 * d;
            let lst = (gmst + lng) % 360;
            if (lst < 0) lst += 360;
            return lst;
        };

        const getAltAz = (ra, dec, lat, lst) => {
            const rad = Math.PI / 180;
            const ha = (lst - ra) * rad;
            const d = dec * rad;
            const l = lat * rad;

            let sinAlt = Math.sin(d) * Math.sin(l) + Math.cos(d) * Math.cos(l) * Math.cos(ha);
            let alt = Math.asin(sinAlt);

            let cosAz = (Math.sin(d) - Math.sin(alt) * Math.sin(l)) / (Math.cos(alt) * Math.cos(l));
            cosAz = Math.max(-1, Math.min(1, cosAz)); 
            let az = Math.acos(cosAz);

            if (Math.sin(ha) > 0) az = 2 * Math.PI - az;

            return { alt: alt / rad, az: az / rad };
        };

        const GC_RA = 266.405;
        const GC_DEC = -28.936;

        const getGalacticEquator = () => {
            const points = [];
            const l_ncp = 122.9319 * Math.PI/180;
            const d_ngp = 27.12825 * Math.PI/180;
            const r_ngp = 192.85948 * Math.PI/180;

            for (let l = 0; l < 360; l += 5) {
                const l_rad = l * Math.PI / 180;
                const b_rad = 0; 
                const sin_d = Math.sin(d_ngp)*Math.sin(b_rad) + Math.cos(d_ngp)*Math.cos(b_rad)*Math.cos(l_ncp - l_rad);
                const d = Math.asin(sin_d);
                const y = Math.cos(b_rad)*Math.sin(l_ncp - l_rad);
                const x = Math.cos(d_ngp)*Math.sin(b_rad) - Math.sin(d_ngp)*Math.cos(b_rad)*Math.cos(l_ncp - l_rad);
                let r = Math.atan2(y, x) + r_ngp;
                points.push({ ra: (r * 180 / Math.PI + 360) % 360, dec: d * 180 / Math.PI });
            }
            return points;
        };
        const GALACTIC_EQUATOR = getGalacticEquator();

        const BRIGHT_STARS = [
            { name: "Sirius", ra: 101.287, dec: -16.716, mag: -1.46 },
            { name: "Canopus", ra: 95.987, dec: -52.695, mag: -0.74 },
            { name: "Arcturus", ra: 213.915, dec: 19.182, mag: -0.05 },
            { name: "Vega", ra: 279.234, dec: 38.783, mag: 0.03 },
            { name: "Capella", ra: 79.172, dec: 45.998, mag: 0.08 },
            { name: "Rigel", ra: 78.634, dec: -8.201, mag: 0.18 },
            { name: "Procyon", ra: 114.825, dec: 5.224, mag: 0.40 },
            { name: "Achernar", ra: 24.428, dec: -57.236, mag: 0.46 },
            { name: "Betelgeuse", ra: 88.792, dec: 7.407, mag: 0.50 },
            { name: "Altair", ra: 297.695, dec: 8.868, mag: 0.76 },
            { name: "Aldebaran", ra: 68.980, dec: 16.509, mag: 0.87 },
            { name: "Spica", ra: 201.298, dec: -11.161, mag: 0.98 },
            { name: "Antares", ra: 247.351, dec: -26.432, mag: 1.06 },
            { name: "Pollux", ra: 116.328, dec: 28.026, mag: 1.16 },
            { name: "Fomalhaut", ra: 344.412, dec: -29.622, mag: 1.17 },
            { name: "Deneb", ra: 310.357, dec: 45.280, mag: 1.25 },
            { name: "Regulus", ra: 152.092, dec: 11.967, mag: 1.36 },
            { name: "Polaris", ra: 37.954, dec: 89.264, mag: 1.97 }
        ];

        const ASTERISMS = [
            { name: "Big Dipper", lines: [[165.46, 61.75], [164.66, 56.38], [178.45, 53.69], [183.85, 57.03], [164.66, 56.38], [193.5, 55.95], [200.98, 54.92], [206.88, 49.31]] },
            { name: "Orion", lines: [[88.79, 7.41], [80.11, 6.34], [78.63, -8.20], [86.93, -9.66], [88.79, 7.41], [83.00, -0.29], [84.05, -1.20], [85.18, -1.94], [80.11, 6.34], [83.00, -0.29], [78.63, -8.20], [85.18, -1.94], [86.93, -9.66]] }
        ];

        const getAstroAzimuth = (bodyName, exactDate, observer) => {
            if (!exactDate || !window.Astronomy || !observer) return null;
            try {
                const equ = window.Astronomy.Equator(bodyName, exactDate, observer, true, true);
                const hor = window.Astronomy.Horizon(exactDate, observer, equ.ra, equ.dec, 'normal');
                return hor.azimuth; 
            } catch(e) { return null; }
        };

        const getAccurateAltAz = (type, testDate, lat, lng, elev, localAtmos) => {
            if (window.Astronomy) {
                const observer = new window.Astronomy.Observer(lat, lng, elev);
                const bodyStr = type === 'sun' ? 'Sun' : 'Moon';
                const equ = window.Astronomy.Equator(bodyStr, testDate, observer, true, true);
                const hor = window.Astronomy.Horizon(testDate, observer, equ.ra, equ.dec, 'normal');
                
                let alt = hor.altitude;
                let az = hor.azimuth;

                if (localAtmos) {
                    const tempK = localAtmos.temp + 273.15;
                    const exactPressure = localAtmos.pressure_msl * Math.pow(1 - 0.0065 * elev / (tempK + 0.0065 * elev), 5.255);
                    
                    if (alt < 15 && alt > -5) {
                         const R0 = 1.02 / Math.tan((alt + 10.3 / (alt + 5.11)) * Math.PI/180) / 60;
                         const actualRefraction = R0 * (exactPressure / 1010) * (283 / tempK);
                         alt += (actualRefraction - R0);
                    }
                }
                return { alt, az };
            } else {
                const pos = type === 'sun' ? SunCalc.getPosition(testDate, lat, lng) : SunCalc.getMoonPosition(testDate, lat, lng);
                return { alt: pos.altitude * 180 / Math.PI, az: toCompassBearing(pos.azimuth) };
            }
        };

        const getBodyRadiusDeg = (type, testDate, lat, lng, elev) => {
            if (window.Astronomy) {
                const observer = new window.Astronomy.Observer(lat, lng, elev);
                const bodyStr = type === 'sun' ? 'Sun' : 'Moon';
                const equ = window.Astronomy.Equator(bodyStr, testDate, observer, true, true);
                const distAu = equ.dist;
                if (type === 'sun') {
                    return Math.asin(0.00465047 / distAu) * (180 / Math.PI); 
                } else {
                    return Math.asin(1.1613e-5 / distAu) * (180 / Math.PI); 
                }
            } else {
                return type === 'sun' ? 0.266 : 0.264;
            }
        };

