diff --git a/astro.html b/astro.html
index ff8e2d6..4788f57 100644
--- a/astro.html
+++ b/astro.html
@@ -117,6 +117,44 @@
font-size: 0.77rem;
}
.event-dual span:first-child { color: var(--text); }
+ .loc-wrap {
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ padding: 0.7rem;
+ margin: 0.2rem 0 1rem;
+ background: #171717;
+ }
+ .loc-head {
+ font-size: 0.72rem;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: var(--accent);
+ margin-bottom: 0.45rem;
+ font-weight: 700;
+ }
+ .loc-row {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 0.4rem;
+ margin-bottom: 0.4rem;
+ }
+ .loc-input {
+ background: #111;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ color: var(--text);
+ padding: 0.35rem 0.5rem;
+ font-size: 0.82rem;
+ min-width: 0;
+ }
+ .loc-status {
+ font-size: 0.76rem;
+ color: var(--muted);
+ min-height: 1.1em;
+ }
+ @media (max-width: 640px) {
+ .loc-row { grid-template-columns: 1fr 1fr; }
+ }
@@ -132,6 +170,22 @@
🌑
—
diff --git a/astro.js b/astro.js
index 470394c..bcfa344 100644
--- a/astro.js
+++ b/astro.js
@@ -261,15 +261,25 @@ function formatDate(date) {
return date.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", timeZone: "UTC" });
}
-// Default lat/lon: roughly central US (no geolocation, just reasonable default)
-// Will use user's timezone offset to pick a reasonable longitude
-function guessLon() {
- const offsetHours = -new Date().getTimezoneOffset() / 60;
- return offsetHours * 15; // rough: UTC offset * 15° per hour
+function loadLocation() {
+ try {
+ const raw = localStorage.getItem("astro_location");
+ if (!raw) return { label: "Home", lat: 40.0, lon: -75.0 };
+ const d = JSON.parse(raw);
+ const lat = Number(d.lat);
+ const lon = Number(d.lon);
+ if (!Number.isFinite(lat) || !Number.isFinite(lon)) throw new Error("bad loc");
+ return { label: d.label || "Home", lat, lon };
+ } catch {
+ return { label: "Home", lat: 40.0, lon: -75.0 };
+ }
}
-const LAT = 40.0; // roughly mid-US latitude
-const LON = guessLon();
+function saveLocation(loc) {
+ localStorage.setItem("astro_location", JSON.stringify(loc));
+}
+
+let locationState = loadLocation();
let currentDate = parseDate();
@@ -281,6 +291,7 @@ function render() {
history.replaceState(null, "", newUrl);
document.getElementById("dateTitle").textContent = formatDate(date);
+ document.getElementById("locStatus").textContent = `${locationState.label}: ${locationState.lat.toFixed(4)}, ${locationState.lon.toFixed(4)}`;
// Moon
const { age, illumination } = moonPhase(date);
@@ -304,7 +315,7 @@ function render() {
document.getElementById("nextFullGreg").textContent = nextFullDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
// Sun
- const sun = sunriseSunset(date, LAT, LON);
+ const sun = sunriseSunset(date, locationState.lat, locationState.lon);
document.getElementById("sunriseSeth").textContent = sun.riseJD ? jdToDecimalTime(sun.riseJD) : "—";
document.getElementById("sunriseGreg").textContent = sun.rise || (sun.alwaysUp ? "Midnight sun" : "Below horizon");
document.getElementById("sunsetSeth").textContent = sun.setJD ? jdToDecimalTime(sun.setJD) : "—";
@@ -360,6 +371,71 @@ function render() {
});
}
+function initLocationUI() {
+ const labelEl = document.getElementById("locLabel");
+ const zipEl = document.getElementById("locZip");
+ const latEl = document.getElementById("locLat");
+ const lonEl = document.getElementById("locLon");
+ const statusEl = document.getElementById("locStatus");
+
+ labelEl.value = locationState.label || "Home";
+ latEl.value = String(locationState.lat);
+ lonEl.value = String(locationState.lon);
+
+ document.getElementById("locSave").addEventListener("click", () => {
+ const lat = Number(latEl.value);
+ const lon = Number(lonEl.value);
+ if (!Number.isFinite(lat) || !Number.isFinite(lon) || lat < -90 || lat > 90 || lon < -180 || lon > 180) {
+ statusEl.textContent = "Invalid latitude/longitude";
+ return;
+ }
+ locationState = { label: labelEl.value.trim() || "Home", lat, lon };
+ saveLocation(locationState);
+ statusEl.textContent = `Saved: ${locationState.label}`;
+ render();
+ });
+
+ document.getElementById("locUseZip").addEventListener("click", async () => {
+ const zip = (zipEl.value || "").trim();
+ if (!/^\d{5}(-\d{4})?$/.test(zip)) {
+ statusEl.textContent = "Enter valid US ZIP";
+ return;
+ }
+ statusEl.textContent = "Looking up ZIP...";
+ try {
+ const r = await fetch(`https://api.zippopotam.us/us/${encodeURIComponent(zip)}`);
+ if (!r.ok) throw new Error("zip");
+ const data = await r.json();
+ const p = data?.places?.[0];
+ if (!p) throw new Error("zip");
+ latEl.value = String(Number(p.latitude));
+ lonEl.value = String(Number(p.longitude));
+ if (!labelEl.value.trim()) labelEl.value = `${p["place name"]}, ${p["state abbreviation"]}`;
+ statusEl.textContent = "ZIP loaded";
+ } catch {
+ statusEl.textContent = "ZIP lookup failed";
+ }
+ });
+
+ document.getElementById("locGeo").addEventListener("click", () => {
+ if (!navigator.geolocation) {
+ statusEl.textContent = "Geolocation unsupported";
+ return;
+ }
+ statusEl.textContent = "Getting location...";
+ navigator.geolocation.getCurrentPosition(
+ (pos) => {
+ latEl.value = String(pos.coords.latitude.toFixed(6));
+ lonEl.value = String(pos.coords.longitude.toFixed(6));
+ if (!labelEl.value.trim()) labelEl.value = "Current Location";
+ statusEl.textContent = "Location loaded";
+ },
+ () => { statusEl.textContent = "Location denied/unavailable"; },
+ { enableHighAccuracy: true, timeout: 10000 }
+ );
+ });
+}
+
document.getElementById("prevDay").addEventListener("click", () => {
currentDate = stepDate(currentDate, -1);
render();
@@ -369,4 +445,5 @@ document.getElementById("nextDay").addEventListener("click", () => {
render();
});
+initLocationUI();
render();